]> Cypherpunks repositories - gostls13.git/commitdiff
net/http: update bundled x/net/http2
authorBrad Fitzpatrick <bradfitz@golang.org>
Tue, 13 Sep 2016 18:51:16 +0000 (18:51 +0000)
committerBrad Fitzpatrick <bradfitz@golang.org>
Tue, 13 Sep 2016 19:15:41 +0000 (19:15 +0000)
Updates x/net/http2 (and x/net/lex/httplex) to git rev 749a502 for:

   http2: don't sniff first Request.Body byte in Transport until we have a conn
   https://golang.org/cl/29074
   Fixes #17071

   http2: add Transport support for unicode domain names
   https://golang.org/cl/29071
   Updates #13835

   http2: don't send bogus :path pseudo headers if Request.URL.Opaque is set
   https://golang.org/cl/27632
     +
   http2: fix bug where '*' as a valid :path value in Transport
   https://golang.org/cl/29070
   Updates #16847

   http2: fix all vet warnings
   https://golang.org/cl/28344
   Updates #16228
   Updates #11041

Also uses the new -underscore flag to x/tools/cmd/bundle from
https://golang.org/cl/29086

Change-Id: Ica0f6bf6e33266237e37527a166a783d78c059c4
Reviewed-on: https://go-review.googlesource.com/29110
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Chris Broadfoot <cbro@golang.org>
src/go/build/deps_test.go
src/net/http/h2_bundle.go
src/vendor/golang_org/x/net/lex/httplex/httplex.go
src/vendor/golang_org/x/net/lex/httplex/httplex_test.go

index cb8f95fb1deed5a3f64f6dc8df4f29e896b364ee..b8ee601f50cd82628228e947b3761f0dda888a9a 100644 (file)
@@ -379,6 +379,7 @@ var pkgDeps = map[string][]string{
                "mime/multipart", "runtime/debug",
                "net/http/internal",
                "golang_org/x/net/http2/hpack",
+               "golang_org/x/net/idna",
                "golang_org/x/net/lex/httplex",
                "internal/nettrace",
                "net/http/httptrace",
index a41e3ca07f15b4a77cecc73aa066b15e52adbc52..33b13db91f30a819fab90440338eb0dc7eae6a74 100644 (file)
@@ -1,5 +1,5 @@
 // Code generated by golang.org/x/tools/cmd/bundle.
-//go:generate bundle -o h2_bundle.go -prefix http2 golang.org/x/net/http2
+//go:generate bundle -o h2_bundle.go -prefix http2 -underscore golang.org/x/net/http2
 
 // Package http2 implements the HTTP/2 protocol.
 //
@@ -43,6 +43,7 @@ import (
        "time"
 
        "golang_org/x/net/http2/hpack"
+       "golang_org/x/net/idna"
        "golang_org/x/net/lex/httplex"
 )
 
@@ -2145,6 +2146,8 @@ func http2requestTrace(req *Request) *http2clientTrace {
        return (*http2clientTrace)(trace)
 }
 
+func http2cloneTLSConfig(c *tls.Config) *tls.Config { return c.Clone() }
+
 var http2DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1"
 
 type http2goroutineLock uint64
@@ -2686,6 +2689,19 @@ func (s *http2sorter) SortStrings(ss []string) {
        s.v = save
 }
 
+// validPseudoPath reports whether v is a valid :path pseudo-header
+// value. It must be either:
+//
+//     *) a non-empty string starting with '/', but not with with "//",
+//     *) the string '*', for OPTIONS requests.
+//
+// For now this is only used a quick check for deciding when to clean
+// up Opaque URLs before sending requests from the Transport.
+// See golang.org/issue/16847
+func http2validPseudoPath(v string) bool {
+       return (len(v) > 0 && v[0] == '/' && (len(v) == 1 || v[1] != '/')) || v == "*"
+}
+
 // pipe is a goroutine-safe io.Reader/io.Writer pair.  It's like
 // io.Pipe except there are no PipeReader/PipeWriter halves, and the
 // underlying buffer is an interface. (io.Pipe is always unbuffered)
@@ -5133,14 +5149,18 @@ func (t *http2Transport) RoundTrip(req *Request) (*Response, error) {
 // authorityAddr returns a given authority (a host/IP, or host:port / ip:port)
 // and returns a host:port. The port 443 is added if needed.
 func http2authorityAddr(scheme string, authority string) (addr string) {
-       if _, _, err := net.SplitHostPort(authority); err == nil {
-               return authority
+       host, port, err := net.SplitHostPort(authority)
+       if err != nil {
+               port = "443"
+               if scheme == "http" {
+                       port = "80"
+               }
+               host = authority
        }
-       port := "443"
-       if scheme == "http" {
-               port = "80"
+       if a, err := idna.ToASCII(host); err == nil {
+               host = a
        }
-       return net.JoinHostPort(authority, port)
+       return net.JoinHostPort(host, port)
 }
 
 // RoundTripOpt is like RoundTrip, but takes options.
@@ -5203,7 +5223,7 @@ func (t *http2Transport) dialClientConn(addr string, singleUse bool) (*http2Clie
 func (t *http2Transport) newTLSConfig(host string) *tls.Config {
        cfg := new(tls.Config)
        if t.TLSClientConfig != nil {
-               *cfg = *t.TLSClientConfig
+               *cfg = *http2cloneTLSConfig(t.TLSClientConfig)
        }
        if !http2strSliceContains(cfg.NextProtos, http2NextProtoTLS) {
                cfg.NextProtos = append([]string{http2NextProtoTLS}, cfg.NextProtos...)
@@ -5486,9 +5506,6 @@ func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) {
        }
        hasTrailers := trailers != ""
 
-       body, contentLen := http2bodyAndLength(req)
-       hasBody := body != nil
-
        cc.mu.Lock()
        cc.lastActive = time.Now()
        if cc.closed || !cc.canTakeNewRequestLocked() {
@@ -5496,6 +5513,9 @@ func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) {
                return nil, http2errClientConnUnusable
        }
 
+       body, contentLen := http2bodyAndLength(req)
+       hasBody := body != nil
+
        // TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere?
        var requestedGzip bool
        if !cc.t.disableCompression() &&
@@ -5792,6 +5812,26 @@ func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trail
        if host == "" {
                host = req.URL.Host
        }
+       host, err := httplex.PunycodeHostPort(host)
+       if err != nil {
+               return nil, err
+       }
+
+       var path string
+       if req.Method != "CONNECT" {
+               path = req.URL.RequestURI()
+               if !http2validPseudoPath(path) {
+                       orig := path
+                       path = strings.TrimPrefix(path, req.URL.Scheme+"://"+host)
+                       if !http2validPseudoPath(path) {
+                               if req.URL.Opaque != "" {
+                                       return nil, fmt.Errorf("invalid request :path %q from URL.Opaque = %q", orig, req.URL.Opaque)
+                               } else {
+                                       return nil, fmt.Errorf("invalid request :path %q", orig)
+                               }
+                       }
+               }
+       }
 
        for k, vv := range req.Header {
                if !httplex.ValidHeaderFieldName(k) {
@@ -5807,7 +5847,7 @@ func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trail
        cc.writeHeader(":authority", host)
        cc.writeHeader(":method", req.Method)
        if req.Method != "CONNECT" {
-               cc.writeHeader(":path", req.URL.RequestURI())
+               cc.writeHeader(":path", path)
                cc.writeHeader(":scheme", "https")
        }
        if trailers != "" {
index bd0ec24f444365fa73bb459dbd80f40397e8ad96..b6493f045a2202d49cf800e6af6904857777a84e 100644 (file)
 package httplex
 
 import (
+       "net"
        "strings"
        "unicode/utf8"
+
+       "golang_org/x/net/idna"
 )
 
 var isTokenTable = [127]bool{
@@ -310,3 +313,39 @@ func ValidHeaderFieldValue(v string) bool {
        }
        return true
 }
+
+func isASCII(s string) bool {
+       for i := 0; i < len(s); i++ {
+               if s[i] >= utf8.RuneSelf {
+                       return false
+               }
+       }
+       return true
+}
+
+// PunycodeHostPort returns the IDNA Punycode version
+// of the provided "host" or "host:port" string.
+func PunycodeHostPort(v string) (string, error) {
+       if isASCII(v) {
+               return v, nil
+       }
+
+       host, port, err := net.SplitHostPort(v)
+       if err != nil {
+               // The input 'v' argument was just a "host" argument,
+               // without a port. This error should not be returned
+               // to the caller.
+               host = v
+               port = ""
+       }
+       host, err = idna.ToASCII(host)
+       if err != nil {
+               // Non-UTF-8? Not representable in Punycode, in any
+               // case.
+               return "", err
+       }
+       if port == "" {
+               return host, nil
+       }
+       return net.JoinHostPort(host, port), nil
+}
index c4ace1991b35dc4baca17d6685116584fef05b6b..f47adc939fece61d8647a214fd3be5025c912a0c 100644 (file)
@@ -99,3 +99,21 @@ func TestHeaderValuesContainsToken(t *testing.T) {
                }
        }
 }
+
+func TestPunycodeHostPort(t *testing.T) {
+       tests := []struct {
+               in, want string
+       }{
+               {"www.google.com", "www.google.com"},
+               {"гофер.рф", "xn--c1ae0ajs.xn--p1ai"},
+               {"bücher.de", "xn--bcher-kva.de"},
+               {"bücher.de:8080", "xn--bcher-kva.de:8080"},
+               {"[1::6]:8080", "[1::6]:8080"},
+       }
+       for _, tt := range tests {
+               got, err := PunycodeHostPort(tt.in)
+               if tt.want != got || err != nil {
+                       t.Errorf("PunycodeHostPort(%q) = %q, %v, want %q, nil", tt.in, got, err, tt.want)
+               }
+       }
+}