SlyMarbo / spdy

[deprecated] A full-featured SPDY library for the Go language.
BSD 2-Clause "Simplified" License
116 stars 13 forks source link

Data is not sent if message is near to or larger than window #82

Open rnapier opened 9 years ago

rnapier commented 9 years ago

If the size of the data sent by an HTTP Handler is within 15 bytes of the window size, then the data will be sent, but no FIN, which will cause the client to hang.

If the size of the data sent by an HTTP Handler is greater than the window size, then no data will be sent.

client.go

package main

import (
    "fmt"
    "io/ioutil"

    "github.com/SlyMarbo/spdy" // This adds SPDY support to net/http
)

func main() {
    spdy.EnableDebugOutput()
    client := spdy.NewClient(true)
    res, err := client.Get("https://localhost:10443/")
    if err != nil {
        panic(err)
    }
    defer res.Body.Close()

    bytes, err := ioutil.ReadAll(res.Body)
    if err != nil {
        panic(err)
    }

    fmt.Printf("Received: %s\n", bytes)
}

server.go

// openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes -batch

package main

import (
    "bytes"
    "log"
    "net/http"

    "github.com/SlyMarbo/spdy"
    "github.com/SlyMarbo/spdy/common"
)

func httpHandler(w http.ResponseWriter, req *http.Request) {
    w.Header().Set("Content-Type", "text/plain")
    // w.Write(bytes.Repeat([]byte("X"), common.DEFAULT_INITIAL_CLIENT_WINDOW_SIZE-16)) // Works
    w.Write(bytes.Repeat([]byte("X"), common.DEFAULT_INITIAL_CLIENT_WINDOW_SIZE-15)) // Hangs (no FIN is sent)
    // w.Write(bytes.Repeat([]byte("X"), common.DEFAULT_INITIAL_CLIENT_WINDOW_SIZE)) // Sends FIN, but no data
}

func main() {
    spdy.EnableDebugOutput()
    http.HandleFunc("/", httpHandler)
    log.Printf("About to listen on 10443. Go to https://127.0.0.1:10443/")
    err := spdy.ListenAndServeTLS(":10443", "cert.pem", "key.pem", nil)
    if err != nil {
        log.Fatal(err)
    }
}
SlyMarbo commented 9 years ago

Thanks for the bug report. I've already got some suspicions about what is causing this. I'll push a fix once I've confirmed it.

SlyMarbo commented 9 years ago

I've found another related issue, with a complicated fix.

The connection-level flow control added in SPDY/3.1 doesn't work properly. If a connection becomes constrained but then receives a WINDOW_UPDATE frame which relieves the constraint, buffered frames are not sent immediately, but will be sent when the next frame is chosen.

The ideal fix for this would introduce several nasty race conditions, so it may take some time to provide a better fix.