Write to response body waits for client to read in Go? - http

I'm using the httptest package in Go to test my application. Recently I noticed that one of my tests was failing to finish because my test wasn't reading the body of the response
func Test_AnyTest(t *testing.T) {
serve := newServer()
s := httptest.NewServer(serve)
defer s.Close()
testUrl := "/any/old/url"
c := &http.Client{}
r, _ := http.NewRequest("GET", s.URL+testUrl, new(bytes.Buffer))
r.Header.Add("If-None-Match", cacheVersion)
res, _ := c.Do(r)
if res.StatusCode == 304 {
t.Errorf("Should not have got 304")
}
}
The above code was blocking on the deferred call to s.Close() because there were outstanding http connections on the test server. My server has some code that was writing to the body (using the http.ResponseWriter interface). Turns out that this code was actually blocking until I read the body in my test.
A call like this did the trick.
ioutil.ReadAll(res.Body)
I don't mind making this call in my tests but I am concerned that a client that misbehaves could cause this behaviour and consume server resources. Does anyone know what's happening here? Is this expected behaviour in the wild or is it just a problem with the test framework?
Thanks!

From the http docs:
The client must close the response body when finished with it:
resp, err := http.Get("http://example.com/")
if err != nil {
// handle error
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
// ...
So your test is violating the contract of http.Client; adding a defer res.Close() should let the server know that the response channel is closed, and stop writing to it. The effect of your ioutil.ReadAll was the same, as it closes the Reader once it reaches EOF.
In the wild, if a client opens a connection and sends a request, the server just sends the response and then closes the connection. It doesn't wait around for the client to close it. If the client is too slow to ACK the IP packets that the server sends, the OS will eventually take care of timing out the connection, and the http.Server will respond by exiting the goroutine that served the request, no kittens harmed.

Related

How does this goroutine fail?

func serveApp() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(resp http.ResponseWriter, req *http.Request) {
fmt.Fprintln(resp, "Hello, QCon!")
})
http.ListenAndServe("0.0.0.0:8080", mux)
}
func serveDebug() {
http.ListenAndServe("127.0.0.1:8001", http.DefaultServeMux)
}
func main() {
go serveDebug()
serveApp()
}
However, serveDebug is run in a separate goroutine and if it returns
just that goroutine will exit while the rest of the program continues
on. Your operations staff will not be happy to find that they cannot
get the statistics out of your application when they want too because
the /debug handler stopped working a long time ago.
I am new to Golang and coding in general. I came across an article online, and found this code. I copy and paste it into my editor and type go run main.go. The program runs forever without any errors. I can curl it with no problems. Why is it bad code? I am a noob, and I am trying to get a better understanding of this, if this could be explained in simple terms that would be great.
The program creates two HTTP servers to respond to traffic received on to different ports. The debug server is run in a separate goroutine, and there is no way to detect if that server failed. The program may continue running serving with the App server.
A better implementation would be to stop both servers if one of them failed:
stop:=make(chan struct{},2)
go func() {
defer func() {
stop<-struct{}{}
}()
serveDebug()
}()
go func() {
defer func() {
stop <-struct{}{}
}{}
serveApp()
}()
<-stop
Above, the program will create two goroutines and block at <-stop until someone writes to the channel. If any one of the servers fail, the goroutine will write to the channel, which will unblock <-stop, so the program will exit.

How to determine exactly when a golang server is listening on a port [duplicate]

When I look at the net/http server interface, I don't see an obvious way to get notified and react when the http.Server comes up and starts listening:
ListenAndServe(":8080", nil)
The function doesn't return until the server actually shuts down. I also looked at the Server type, but there doesn't appear to be anything that lets me tap into that timing. Some function or a channel would have been great but I don't see any.
Is there any way that will let me detect that event, or am I left to just sleeping "enough" to fake it?
ListenAndServe is a helper function that opens a listening socket and then serves connections on that socket. Write the code directly in your application to signal when the socket is open:
l, err := net.Listen("tcp", ":8080")
if err != nil {
// handle error
}
// Signal that server is open for business.
if err := http.Serve(l, rootHandler); err != nil {
// handle error
}
If the signalling step does not block, then http.Serve will easily consume any backlog on the listening socket.
Related question: https://stackoverflow.com/a/32742904/5728991

Dial tcp I/O timeout on simultaneous requests

I am building a tool in Go that needs to make a very large number of simultaneous HTTP requests to many different servers. My initial prototype in Python had no problem doing a few hundred simultaneous requests.
However, I have found that in Go this almost always results in a Get http://www.google.com: dial tcp 216.58.205.228:80: i/o timeout for some if the number of simultaneous requests exceeds ~30-40.
I've tested on macOS, openSUSE, different hardware, in different networks and with different domain lists, and changing the DNS server as described in other Stackoverflow answers does not work either.
The interesting thing is that the failed requests do not even produce a packet, as can be seen when checking with Wireshark.
Is there anything that I am doing wrong or is that a bug in Go?
Minimum reproducible program below:
package main
import (
"fmt"
"net/http"
"sync"
)
func main() {
domains := []string{/* large domain list here, eg from https://moz.com/top500 */}
limiter := make(chan string, 50) // Limits simultaneous requests
wg := sync.WaitGroup{} // Needed to not prematurely exit before all requests have been finished
for i, domain := range domains {
wg.Add(1)
limiter <- domain
go func(i int, domain string) {
defer func() { <-limiter }()
defer wg.Done()
resp, err := http.Get("http://"+domain)
if err != nil {
fmt.Printf("%d %s failed: %s\n", i, domain, err)
return
}
fmt.Printf("%d %s: %s\n", i, domain, resp.Status)
}(i, domain)
}
wg.Wait()
}
Two particular error messages are happening, a net.DNSError that does not make any sense and a non-descript poll.TimeoutError:
&url.Error{Op:"Get", URL:"http://harvard.edu", Err:(*net.OpError)(0xc00022a460)}
&net.OpError{Op:"dial", Net:"tcp", Source:net.Addr(nil), Addr:net.Addr(nil), Err:(*net.DNSError)(0xc000aca200)}
&net.DNSError{Err:"no such host", Name:"harvard.edu", Server:"", IsTimeout:false, IsTemporary:false}
&url.Error{Op:"Get", URL:"http://latimes.com", Err:(*net.OpError)(0xc000d92730)}
&net.OpError{Op:"dial", Net:"tcp", Source:net.Addr(nil), Addr:net.Addr(nil), Err:(*poll.TimeoutError)(0x14779a0)}
&poll.TimeoutError{}
Update:
Running the requests with a seperate http.Client as well as http.Transport and net.Dialer does not make any difference as can be seen when running code from this playground.
I think many of your net.DNSErrors are actually too many open files errors in disguise. You can see this by running your sample code with the netgo tag (recommendation from here) (go run -tags netgo main.go) which will emit errors like:
…dial tcp: lookup buzzfeed.com on 192.168.1.1:53: dial udp 192.168.1.1:53: socket: too many open files
instead of
…dial tcp: lookup buzzfeed.com: no such host
Make sure you're closing the request's response body (resp.Body.Close()). You can find more about this specific problem at What's the best way to handle "too many open files"? and How to set ulimit -n from a golang program?. (On my machine (macOS), increasing file limits manually seemed to help, but I don't think it's a good solution since it doesn't really scale, and I'm not sure how many open files you'd need overall.)
As suggested by #liam-kelly, I think the i/o timeout error is coming from a DNS server or some other security mechanism. Setting a custom (bad) DNS server IP gives me the same error.

How to do an HTTP duplex handler in Go?

I heard here that
once you write anything in the response, the request body will be
closed which prevents you from reading anything from it
If that is true, how can I write a proper duplex handler that is able to read from the request body, make some kind of transformation, and then write to the response body, in a streaming fashion just like people do in node.js ?
I ended up managing to do this with http.Hijacker.
After the request is made and the request headers are parsed, I can read from *http.Request.Body, then hijack the connection and write to it, at the same time, like this:
hj, ok := w.(http.Hijacker)
if !ok {
http.Error(w, "hijacking not supported", 500)
return
}
conn, bufrw, err := hj.Hijack()
if err != nil {
http.Error(w, err.Error(), 500)
return
}
defer conn.Close()
And then conn is a net.Conn which is the underlying TCP connection to the client, bufrw is a *bufio.ReadWriter, and to write the response without closing the body all I have to do is
_, err = bufrw.WriteString("HTTP/1.1 200 OK\n\n")
_, err = bufrw.WriteString("this")
_, err = bufrw.WriteString("is")
_, err = bufrw.WriteString("the")
_, err = bufrw.WriteString("response")
_, err = bufrw.WriteString("body")
And then I'm not sure about this but maybe someone can complete the answer, it's a good idea to flush the buffers down the connection once in a while with
err := bufrw.Flush()
From the net/http docs you are referring to
var ErrBodyReadAfterClose = errors.New("http: invalid Read on closed
Body")
ErrBodyReadAfterClose is returned when reading a Request or Response
Body after the body has been closed. This typically happens when the
body is read after an HTTP Handler calls WriteHeader or Write on its
ResponseWriter
However I tried the code linked from the blog article you mentioned and it works fine under go 1.1.2 unless I write more than about 4k of data first in which case r.ParseForm() returns ErrBodyReadAfterClose.
So I think the answer is no, you can't do full duplex HTTP in go in general unless the responses are short (under 4k).
I would say that doing full duplex HTTP requests is unlikely to be a big benefit though as most clients won't attempt to read from the response until they have finished sending the request, so the most you'll win is the size of the TCP buffers in the client & server. A deadlock seems likely if these buffers are exceeded, eg
client is sending request
server is sending response
servers buffers get full sending response
server blocks
server no longer reading clients request
client buffers fill up
client blocks
deadlock

Client to do long-polling with multiple requests in a http pipeline.

I am looking for a client software that will run on a Unix system to do long-polling with multiple requests in a single http pipeline.
Basically we need to issue several long-polling GET requests to a server. All the requests need to be done within a single HTTP pipeline.
The client needs to have N requests open at any given time, where N > 1.
The server will respond either with a 200 OK or 204 No Content.
In case of a 200 OK, the response needs to be piped into a new process.
This can be easily implemented using PHP. The HttpRequestPool can be used to build a custom client doing just that. Also see How can I make use of HTTP 1.1 persistent connections and pipelining from PHP?
With Go it's also fairly easy, if you create the connection yourself, you just have to send all the requests and then you can read responses sequentially, and it will send it all through one http pipelined connection.
conn, _ := net.Dial("tcp", "127.0.0.1:80")
client := httputil.NewClientConn(conn, nil)
req, _ := http.NewRequest("GET", "/", nil)
client.Write(req)
resp, _ := client.Read(req)
You should do some more error checking though.

Resources