Today I was investigating why HTTP redirects resulted in more persistent connections staying open than I was expecting. I found myself digging around deep inside net/http/transport.go and I noticed the new net/http/httptrace package. It is new in Go 1.7, which is currently in beta.
net/http/httptrace is lightly documented, and because it is new, there are no examples to look at. So I decided to share what I came up with here.
package main
import (
"context"
"fmt"
"log"
"net/http"
"net/http/httptrace"
"os"
)
func main() {
ctx := httptrace.WithClientTrace(context.Background(), &httptrace.ClientTrace{
PutIdleConn: func(err error) {
if err == nil {
fmt.Println("put idle: no err")
} else {
fmt.Println("put idle:", err.Error())
}
},
})
req, err := http.NewRequest("GET", "http://www.golang.org/", nil)
if err != nil {
log.Fatal("new req", err)
}
req = req.WithContext(ctx)
_, err = http.DefaultClient.Do(req)
if err != nil {
fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
os.Exit(1)
}
}
The key things I had to figure out (and hopefully you won’t have to since you found this post) were:
- How to get a base context to add the tracer to ( context.Background())
- Which callback to use in the ClientTrace structure (for my purposes, PutIdleConn)
- How to do an HTTP request with the context attached (NewRequest -> req.WithContext -> http.(*Client).Do)
If you are used to using the simple http.(*Client).Get method to do HTTP you’ll need to learn to use http.NewRequest in order to make a request object, and then pass that to a client. In this simple case, I used the DefaultClient. In a library, you should be using the http.Client passed into you by the caller, so that your library would work in contexts like Google App Engine.