From: Brad Fitzpatrick Date: Tue, 27 Sep 2016 18:27:02 +0000 (+0000) Subject: net/http: add more IDNA2008 tests and fix some omissions X-Git-Tag: go1.8beta1~1131 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=a73020f847b3cf8575250569ebefb02573d19224;p=gostls13.git net/http: add more IDNA2008 tests and fix some omissions It wasn't lowercasing the string, folding widths, and putting strings into NFC form. Do those. Fixes #13835 Change-Id: Ia3de6159417cacec203b48e206e51d79f945df58 Reviewed-on: https://go-review.googlesource.com/29860 Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot Reviewed-by: Marcel van Lohuizen --- diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index c7e11498dd..fb31ac31c3 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -382,6 +382,8 @@ var pkgDeps = map[string][]string{ "golang_org/x/net/http2/hpack", "golang_org/x/net/idna", "golang_org/x/net/lex/httplex", + "golang_org/x/text/unicode/norm", + "golang_org/x/text/width", "internal/nettrace", "net/http/httptrace", }, diff --git a/src/net/http/http.go b/src/net/http/http.go index b34ae41ec5..258efbb152 100644 --- a/src/net/http/http.go +++ b/src/net/http/http.go @@ -6,6 +6,7 @@ package http import ( "strings" + "unicode/utf8" "golang_org/x/net/lex/httplex" ) @@ -41,3 +42,12 @@ func removeEmptyPort(host string) string { func isNotToken(r rune) bool { return !httplex.IsTokenRune(r) } + +func isASCII(s string) bool { + for i := 0; i < len(s); i++ { + if s[i] >= utf8.RuneSelf { + return false + } + } + return true +} diff --git a/src/net/http/http_test.go b/src/net/http/http_test.go index b95dab053c..c6c38ffcae 100644 --- a/src/net/http/http_test.go +++ b/src/net/http/http_test.go @@ -51,10 +51,18 @@ func TestCleanHost(t *testing.T) { {"www.google.com foo", "www.google.com"}, {"www.google.com/foo", "www.google.com"}, {" first character is a space", ""}, + {"[1::6]:8080", "[1::6]:8080"}, + + // Punycode: {"гофер.рф/foo", "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"}, + // Verify we convert to lowercase before punycode: + {"BÜCHER.de", "xn--bcher-kva.de"}, + {"BÜCHER.de:8080", "xn--bcher-kva.de:8080"}, + // Verify we normalize to NFC before punycode: + {"gophér.nfc", "xn--gophr-esa.nfc"}, // NFC input; no work needed + {"goph\u0065\u0301r.nfd", "xn--gophr-esa.nfd"}, // NFD input } for _, tt := range tests { got := cleanHost(tt.in) diff --git a/src/net/http/request.go b/src/net/http/request.go index bebf55ccc4..a27d13cb98 100644 --- a/src/net/http/request.go +++ b/src/net/http/request.go @@ -27,6 +27,8 @@ import ( "sync" "golang_org/x/net/idna" + "golang_org/x/text/unicode/norm" + "golang_org/x/text/width" ) const ( @@ -581,6 +583,19 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header, wai return nil } +func idnaASCII(v string) (string, error) { + if isASCII(v) { + return v, nil + } + // The idna package doesn't do everything from + // https://tools.ietf.org/html/rfc5895 so we do it here. + // TODO(bradfitz): should the idna package do this instead? + v = strings.ToLower(v) + v = width.Fold.String(v) + v = norm.NFC.String(v) + return idna.ToASCII(v) +} + // cleanHost cleans up the host sent in request's Host header. // // It both strips anything after '/' or ' ', and puts the value @@ -600,13 +615,13 @@ func cleanHost(in string) string { } host, port, err := net.SplitHostPort(in) if err != nil { // input was just a host - a, err := idna.ToASCII(in) + a, err := idnaASCII(in) if err != nil { return in // garbage in, garbage out } return a } - a, err := idna.ToASCII(host) + a, err := idnaASCII(host) if err != nil { return in // garbage in, garbage out } diff --git a/src/net/http/transport.go b/src/net/http/transport.go index cde7acac31..ed2b3a26ed 100644 --- a/src/net/http/transport.go +++ b/src/net/http/transport.go @@ -27,7 +27,6 @@ import ( "sync" "time" - "golang_org/x/net/idna" "golang_org/x/net/lex/httplex" ) @@ -1945,7 +1944,7 @@ var portMap = map[string]string{ // canonicalAddr returns url.Host but always with a ":port" suffix func canonicalAddr(url *url.URL) string { addr := url.Hostname() - if v, err := idna.ToASCII(addr); err == nil { + if v, err := idnaASCII(addr); err == nil { addr = v } port := url.Port()