Commit 802cb592 authored by Brad Fitzpatrick's avatar Brad Fitzpatrick

net/http: update bundled x/net/http2

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: default avatarChris Broadfoot <cbro@golang.org>
parent 9980b70c
...@@ -379,6 +379,7 @@ var pkgDeps = map[string][]string{ ...@@ -379,6 +379,7 @@ var pkgDeps = map[string][]string{
"mime/multipart", "runtime/debug", "mime/multipart", "runtime/debug",
"net/http/internal", "net/http/internal",
"golang_org/x/net/http2/hpack", "golang_org/x/net/http2/hpack",
"golang_org/x/net/idna",
"golang_org/x/net/lex/httplex", "golang_org/x/net/lex/httplex",
"internal/nettrace", "internal/nettrace",
"net/http/httptrace", "net/http/httptrace",
......
// Code generated by golang.org/x/tools/cmd/bundle. // 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. // Package http2 implements the HTTP/2 protocol.
// //
...@@ -43,6 +43,7 @@ import ( ...@@ -43,6 +43,7 @@ import (
"time" "time"
"golang_org/x/net/http2/hpack" "golang_org/x/net/http2/hpack"
"golang_org/x/net/idna"
"golang_org/x/net/lex/httplex" "golang_org/x/net/lex/httplex"
) )
...@@ -2145,6 +2146,8 @@ func http2requestTrace(req *Request) *http2clientTrace { ...@@ -2145,6 +2146,8 @@ func http2requestTrace(req *Request) *http2clientTrace {
return (*http2clientTrace)(trace) return (*http2clientTrace)(trace)
} }
func http2cloneTLSConfig(c *tls.Config) *tls.Config { return c.Clone() }
var http2DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1" var http2DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1"
type http2goroutineLock uint64 type http2goroutineLock uint64
...@@ -2686,6 +2689,19 @@ func (s *http2sorter) SortStrings(ss []string) { ...@@ -2686,6 +2689,19 @@ func (s *http2sorter) SortStrings(ss []string) {
s.v = save 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 // pipe is a goroutine-safe io.Reader/io.Writer pair. It's like
// io.Pipe except there are no PipeReader/PipeWriter halves, and the // io.Pipe except there are no PipeReader/PipeWriter halves, and the
// underlying buffer is an interface. (io.Pipe is always unbuffered) // underlying buffer is an interface. (io.Pipe is always unbuffered)
...@@ -5133,14 +5149,18 @@ func (t *http2Transport) RoundTrip(req *Request) (*Response, error) { ...@@ -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) // 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. // and returns a host:port. The port 443 is added if needed.
func http2authorityAddr(scheme string, authority string) (addr string) { func http2authorityAddr(scheme string, authority string) (addr string) {
if _, _, err := net.SplitHostPort(authority); err == nil { host, port, err := net.SplitHostPort(authority)
return authority if err != nil {
port = "443"
if scheme == "http" {
port = "80"
}
host = authority
} }
port := "443" if a, err := idna.ToASCII(host); err == nil {
if scheme == "http" { host = a
port = "80"
} }
return net.JoinHostPort(authority, port) return net.JoinHostPort(host, port)
} }
// RoundTripOpt is like RoundTrip, but takes options. // RoundTripOpt is like RoundTrip, but takes options.
...@@ -5203,7 +5223,7 @@ func (t *http2Transport) dialClientConn(addr string, singleUse bool) (*http2Clie ...@@ -5203,7 +5223,7 @@ func (t *http2Transport) dialClientConn(addr string, singleUse bool) (*http2Clie
func (t *http2Transport) newTLSConfig(host string) *tls.Config { func (t *http2Transport) newTLSConfig(host string) *tls.Config {
cfg := new(tls.Config) cfg := new(tls.Config)
if t.TLSClientConfig != nil { if t.TLSClientConfig != nil {
*cfg = *t.TLSClientConfig *cfg = *http2cloneTLSConfig(t.TLSClientConfig)
} }
if !http2strSliceContains(cfg.NextProtos, http2NextProtoTLS) { if !http2strSliceContains(cfg.NextProtos, http2NextProtoTLS) {
cfg.NextProtos = append([]string{http2NextProtoTLS}, cfg.NextProtos...) cfg.NextProtos = append([]string{http2NextProtoTLS}, cfg.NextProtos...)
...@@ -5486,9 +5506,6 @@ func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) { ...@@ -5486,9 +5506,6 @@ func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) {
} }
hasTrailers := trailers != "" hasTrailers := trailers != ""
body, contentLen := http2bodyAndLength(req)
hasBody := body != nil
cc.mu.Lock() cc.mu.Lock()
cc.lastActive = time.Now() cc.lastActive = time.Now()
if cc.closed || !cc.canTakeNewRequestLocked() { if cc.closed || !cc.canTakeNewRequestLocked() {
...@@ -5496,6 +5513,9 @@ func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) { ...@@ -5496,6 +5513,9 @@ func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) {
return nil, http2errClientConnUnusable return nil, http2errClientConnUnusable
} }
body, contentLen := http2bodyAndLength(req)
hasBody := body != nil
// TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere? // TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere?
var requestedGzip bool var requestedGzip bool
if !cc.t.disableCompression() && if !cc.t.disableCompression() &&
...@@ -5792,6 +5812,26 @@ func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trail ...@@ -5792,6 +5812,26 @@ func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trail
if host == "" { if host == "" {
host = req.URL.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 { for k, vv := range req.Header {
if !httplex.ValidHeaderFieldName(k) { if !httplex.ValidHeaderFieldName(k) {
...@@ -5807,7 +5847,7 @@ func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trail ...@@ -5807,7 +5847,7 @@ func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trail
cc.writeHeader(":authority", host) cc.writeHeader(":authority", host)
cc.writeHeader(":method", req.Method) cc.writeHeader(":method", req.Method)
if req.Method != "CONNECT" { if req.Method != "CONNECT" {
cc.writeHeader(":path", req.URL.RequestURI()) cc.writeHeader(":path", path)
cc.writeHeader(":scheme", "https") cc.writeHeader(":scheme", "https")
} }
if trailers != "" { if trailers != "" {
......
...@@ -10,8 +10,11 @@ ...@@ -10,8 +10,11 @@
package httplex package httplex
import ( import (
"net"
"strings" "strings"
"unicode/utf8" "unicode/utf8"
"golang_org/x/net/idna"
) )
var isTokenTable = [127]bool{ var isTokenTable = [127]bool{
...@@ -310,3 +313,39 @@ func ValidHeaderFieldValue(v string) bool { ...@@ -310,3 +313,39 @@ func ValidHeaderFieldValue(v string) bool {
} }
return true 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
}
...@@ -99,3 +99,21 @@ func TestHeaderValuesContainsToken(t *testing.T) { ...@@ -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)
}
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment