From 194a5c3e61d4509bdc9c84005305a881783939e3 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 6 Jan 2016 07:14:49 +0000 Subject: [PATCH] net/http: update bundled http2, add test for Transport's User-Agent behavior Adds a test that both http1 and http2's Transport send a default User-Agent, with the same behavior. Updates bundled http2 to golang.org/x/net git rev 1ade16a545 (for https://go-review.googlesource.com/18285) The http1 behavior changes slightly: if req.Header["User-Agent"] is defined at all, even if it's nil or a zero-length slice, then the User-Agent header is omitted. This is a slight behavior change for http1, but is consistent with how http1 & http2 do optional headers elsewhere (such as "Date", "Content-Type"). The old behavior (set it explicitly to "", aka []string{""}) still works as before. And now there are even tests. Fixes #13685 Change-Id: I5786a6913b560de4a5f1f90e595fe320ff567adf Reviewed-on: https://go-review.googlesource.com/18284 Run-TryBot: Brad Fitzpatrick Reviewed-by: Ian Lance Taylor --- src/net/http/clientserver_test.go | 61 +++++++++++++++++++++++++++++++ src/net/http/h2_bundle.go | 20 +++++++++- src/net/http/request.go | 6 +-- 3 files changed, 82 insertions(+), 5 deletions(-) diff --git a/src/net/http/clientserver_test.go b/src/net/http/clientserver_test.go index 0455794257..5143c104d0 100644 --- a/src/net/http/clientserver_test.go +++ b/src/net/http/clientserver_test.go @@ -734,3 +734,64 @@ func testConnectRequest(t *testing.T, h2 bool) { } } } + +func TestTransportUserAgent_h1(t *testing.T) { testTransportUserAgent(t, h1Mode) } +func TestTransportUserAgent_h2(t *testing.T) { testTransportUserAgent(t, h2Mode) } +func testTransportUserAgent(t *testing.T, h2 bool) { + defer afterTest(t) + cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { + fmt.Fprintf(w, "%q", r.Header["User-Agent"]) + })) + defer cst.close() + + either := func(a, b string) string { + if h2 { + return b + } + return a + } + + tests := []struct { + setup func(*Request) + want string + }{ + { + func(r *Request) {}, + either(`["Go-http-client/1.1"]`, `["Go-http-client/2.0"]`), + }, + { + func(r *Request) { r.Header.Set("User-Agent", "foo/1.2.3") }, + `["foo/1.2.3"]`, + }, + { + func(r *Request) { r.Header["User-Agent"] = []string{"single", "or", "multiple"} }, + `["single"]`, + }, + { + func(r *Request) { r.Header.Set("User-Agent", "") }, + `[]`, + }, + { + func(r *Request) { r.Header["User-Agent"] = nil }, + `[]`, + }, + } + for i, tt := range tests { + req, _ := NewRequest("GET", cst.ts.URL, nil) + tt.setup(req) + res, err := cst.c.Do(req) + if err != nil { + t.Errorf("%d. RoundTrip = %v", i, err) + continue + } + slurp, err := ioutil.ReadAll(res.Body) + res.Body.Close() + if err != nil { + t.Errorf("%d. read body = %v", i, err) + continue + } + if string(slurp) != tt.want { + t.Errorf("%d. body mismatch.\n got: %s\nwant: %s\n", i, slurp, tt.want) + } + } +} diff --git a/src/net/http/h2_bundle.go b/src/net/http/h2_bundle.go index 6d84018a73..c7bf2ab84d 100644 --- a/src/net/http/h2_bundle.go +++ b/src/net/http/h2_bundle.go @@ -24,7 +24,6 @@ import ( "encoding/binary" "errors" "fmt" - "golang.org/x/net/http2/hpack" "io" "io/ioutil" "log" @@ -38,6 +37,8 @@ import ( "strings" "sync" "time" + + "golang.org/x/net/http2/hpack" ) // ClientConnPool manages a pool of HTTP/2 client connections. @@ -4095,6 +4096,8 @@ const ( // transportDefaultStreamMinRefresh is the minimum number of bytes we'll send // a stream-level WINDOW_UPDATE for at a time. http2transportDefaultStreamMinRefresh = 4 << 10 + + http2defaultUserAgent = "Go-http-client/2.0" ) // Transport is an HTTP/2 Transport. @@ -4794,11 +4797,23 @@ func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trail cc.writeHeader("trailer", trailers) } + var didUA bool for k, vv := range req.Header { lowKey := strings.ToLower(k) if lowKey == "host" { continue } + if lowKey == "user-agent" { + + didUA = true + if len(vv) < 1 { + continue + } + vv = vv[:1] + if vv[0] == "" { + continue + } + } for _, v := range vv { cc.writeHeader(lowKey, v) } @@ -4806,6 +4821,9 @@ func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trail if addGzipHeader { cc.writeHeader("accept-encoding", "gzip") } + if !didUA { + cc.writeHeader("user-agent", http2defaultUserAgent) + } return cc.hbuf.Bytes() } diff --git a/src/net/http/request.go b/src/net/http/request.go index 76a8b09501..28f05174c0 100644 --- a/src/net/http/request.go +++ b/src/net/http/request.go @@ -427,10 +427,8 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header, wai // Use the defaultUserAgent unless the Header contains one, which // may be blank to not send the header. userAgent := defaultUserAgent - if req.Header != nil { - if ua := req.Header["User-Agent"]; len(ua) > 0 { - userAgent = ua[0] - } + if _, ok := req.Header["User-Agent"]; ok { + userAgent = req.Header.Get("User-Agent") } if userAgent != "" { _, err = fmt.Fprintf(w, "User-Agent: %s\r\n", userAgent) -- 2.50.0