]> Cypherpunks repositories - gostls13.git/commitdiff
net/http: update bundled http2, add test for Transport's User-Agent behavior
authorBrad Fitzpatrick <bradfitz@golang.org>
Wed, 6 Jan 2016 07:14:49 +0000 (07:14 +0000)
committerBrad Fitzpatrick <bradfitz@golang.org>
Wed, 6 Jan 2016 18:53:46 +0000 (18:53 +0000)
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 <bradfitz@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
src/net/http/clientserver_test.go
src/net/http/h2_bundle.go
src/net/http/request.go

index 0455794257d9509981a59dc1ae5630686fbdcf37..5143c104d045608f86fa5a5ccfe8be958c1defdb 100644 (file)
@@ -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)
+               }
+       }
+}
index 6d84018a730ba56ce234fdbb2376a5fba8fabe1a..c7bf2ab84d80051854e05bdd8c81078b68f21419 100644 (file)
@@ -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()
 }
 
index 76a8b0950183c1ee90e902ec3420f7aac289e2ce..28f05174c047157055e449288c3a2fbcf720a1f3 100644 (file)
@@ -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)