Commit a73020f8 authored by Brad Fitzpatrick's avatar Brad Fitzpatrick

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 <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarMarcel van Lohuizen <mpvl@golang.org>
parent 36c164ec
...@@ -382,6 +382,8 @@ var pkgDeps = map[string][]string{ ...@@ -382,6 +382,8 @@ var pkgDeps = map[string][]string{
"golang_org/x/net/http2/hpack", "golang_org/x/net/http2/hpack",
"golang_org/x/net/idna", "golang_org/x/net/idna",
"golang_org/x/net/lex/httplex", "golang_org/x/net/lex/httplex",
"golang_org/x/text/unicode/norm",
"golang_org/x/text/width",
"internal/nettrace", "internal/nettrace",
"net/http/httptrace", "net/http/httptrace",
}, },
......
...@@ -6,6 +6,7 @@ package http ...@@ -6,6 +6,7 @@ package http
import ( import (
"strings" "strings"
"unicode/utf8"
"golang_org/x/net/lex/httplex" "golang_org/x/net/lex/httplex"
) )
...@@ -41,3 +42,12 @@ func removeEmptyPort(host string) string { ...@@ -41,3 +42,12 @@ func removeEmptyPort(host string) string {
func isNotToken(r rune) bool { func isNotToken(r rune) bool {
return !httplex.IsTokenRune(r) 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
}
...@@ -51,10 +51,18 @@ func TestCleanHost(t *testing.T) { ...@@ -51,10 +51,18 @@ func TestCleanHost(t *testing.T) {
{"www.google.com foo", "www.google.com"}, {"www.google.com foo", "www.google.com"},
{"www.google.com/foo", "www.google.com"}, {"www.google.com/foo", "www.google.com"},
{" first character is a space", ""}, {" first character is a space", ""},
{"[1::6]:8080", "[1::6]:8080"},
// Punycode:
{"гофер.рф/foo", "xn--c1ae0ajs.xn--p1ai"}, {"гофер.рф/foo", "xn--c1ae0ajs.xn--p1ai"},
{"bücher.de", "xn--bcher-kva.de"}, {"bücher.de", "xn--bcher-kva.de"},
{"bücher.de:8080", "xn--bcher-kva.de:8080"}, {"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 { for _, tt := range tests {
got := cleanHost(tt.in) got := cleanHost(tt.in)
......
...@@ -27,6 +27,8 @@ import ( ...@@ -27,6 +27,8 @@ import (
"sync" "sync"
"golang_org/x/net/idna" "golang_org/x/net/idna"
"golang_org/x/text/unicode/norm"
"golang_org/x/text/width"
) )
const ( const (
...@@ -581,6 +583,19 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header, wai ...@@ -581,6 +583,19 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header, wai
return nil 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. // cleanHost cleans up the host sent in request's Host header.
// //
// It both strips anything after '/' or ' ', and puts the value // It both strips anything after '/' or ' ', and puts the value
...@@ -600,13 +615,13 @@ func cleanHost(in string) string { ...@@ -600,13 +615,13 @@ func cleanHost(in string) string {
} }
host, port, err := net.SplitHostPort(in) host, port, err := net.SplitHostPort(in)
if err != nil { // input was just a host if err != nil { // input was just a host
a, err := idna.ToASCII(in) a, err := idnaASCII(in)
if err != nil { if err != nil {
return in // garbage in, garbage out return in // garbage in, garbage out
} }
return a return a
} }
a, err := idna.ToASCII(host) a, err := idnaASCII(host)
if err != nil { if err != nil {
return in // garbage in, garbage out return in // garbage in, garbage out
} }
......
...@@ -27,7 +27,6 @@ import ( ...@@ -27,7 +27,6 @@ import (
"sync" "sync"
"time" "time"
"golang_org/x/net/idna"
"golang_org/x/net/lex/httplex" "golang_org/x/net/lex/httplex"
) )
...@@ -1945,7 +1944,7 @@ var portMap = map[string]string{ ...@@ -1945,7 +1944,7 @@ var portMap = map[string]string{
// canonicalAddr returns url.Host but always with a ":port" suffix // canonicalAddr returns url.Host but always with a ":port" suffix
func canonicalAddr(url *url.URL) string { func canonicalAddr(url *url.URL) string {
addr := url.Hostname() addr := url.Hostname()
if v, err := idna.ToASCII(addr); err == nil { if v, err := idnaASCII(addr); err == nil {
addr = v addr = v
} }
port := url.Port() port := url.Port()
......
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