I'm having trouble using net/http's WriteTimeout in Go1.3 on ubuntu 12.04 and 14.04. The short is that if I set a WriteTimeout to N seconds and I run a handler that takes more than N seconds then the response is never written to the socket and the socket hangs. I see issue
https://code.google.com/p/go/issues/detail?id=6410 and I can add the samples below to it, if what I'm observing is the same problem and a valid problem...
Here's my expectation of what ReadTimeout and WriteTimeout should do. If I'm misguided, obviously the examples below may not apply...
- ReadTimeout: when net/http.Server is actively reading the next request from the socket it times out if the read doesn't complete within the timeout
- WriteTimeout: when net/http.ResponseWriter is trying to write something to the socket and the write doesn't complete or doesn't manage to write at least one byte within the WriteTimeout
- If there is no read or no write pending then the respective timeouts should not apply
This is the test program I used to narrow down the problem. Basically it sets ReadTimeout and WriteTimeout per the command line flags, and starts an http server that has a single handler that responds with "Hello World" after 10 seconds.
package main
import (
"flag"
"fmt"
"net/http"
"time"
)
var r *int = flag.Int("r", 0, "read timeout")
var w *int = flag.Int("w", 0, "write timeout")
func main() {
flag.Parse()
http.HandleFunc("/", slashHandler)
server := http.Server{
Addr: ":8123",
ReadTimeout: time.Duration(*r) * time.Second,
WriteTimeout: time.Duration(*w) * time.Second,
}
fmt.Printf("Read timeout: %ds, write timeout: %ds\n", *r, *w)
server.ListenAndServe()
}
func slashHandler(w http.ResponseWriter, r *http.Request) {
time.Sleep(10 * time.Second)
w.Write([]byte("Hello world!\n"))
}
Test case #1 no timeouts:
$ go run timeouttest.go & sleep 1; time curl -v http://localhost:8123/; kill %+
[1] 30352
Read timeout: 0s, write timeout: 0s
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8123 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:8123
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Thu, 14 Aug 2014 20:00:44 GMT
< Content-Length: 13
< Content-Type: text/plain; charset=utf-8
<
Hello world!
* Connection #0 to host localhost left intact
real 0m10.014s
user 0m0.006s
sys 0m0.004s
Works as expected: curl gets the response after 10 seconds.
Test case #2 read and write timeouts:
$ go run timeouttest.go -r 2 -w 2 & sleep 1; time curl -v http://localhost:8123/; kill %+
Read timeout: 2s, write timeout: 2s
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8123 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:8123
> Accept: */*
>
* Empty reply from server
* Connection #0 to host localhost left intact
curl: (52) Empty reply from server
real 0m12.013s
user 0m0.006s
sys 0m0.004s
My interpretation of the 12s delay is that Go thinks it responded after 10 seconds (but didn't) and then times out the next request after 2 additional seconds. Bug: Empty reply from server.
Test case #3 write timeout:
$ go run timeouttest.go -w 2 & sleep 1; time curl -v http://localhost:8123/; kill %+
[3] 30416
Read timeout: 0s, write timeout: 2s
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8123 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:8123
> Accept: */*
>
^C
real 0m38.046s
user 0m0.006s
sys 0m0.004s
Bug: the response after 10 seconds is never written to the socket, I had to kill curl (see timestamp).
Test case #4: read timeout:
$ go run timeouttest.go -r 2 & sleep 1; time curl -v http://localhost:8123/; kill %+
[4] 30482
Read timeout: 2s, write timeout: 0s
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8123 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:8123
> Accept: */*
>
^C
real 0m40.855s
user 0m0.006s
sys 0m0.004s
Similar effect to #3; I had expected to see the read timeout hit after 12 seconds like #2.