From 4e25225db729c3fbee01b229234f19cdf238fcea Mon Sep 17 00:00:00 2001 From: Egor Gorbunov Date: Tue, 22 Dec 2020 00:35:55 +0300 Subject: [PATCH] Do not lower header tokens in headerTokens() HTTP header values, as opposed to header keys, are case sensitive, but implementation of headerTokens() before that patch would return lowered values always. This old behavior could lead to chromium (v87) WebSocket rejecting connnection because negotiated subprotocol, returned in Sec-WebSocket-Protocol header (lowered be headerToken() function) would not match one sent by client, in case client specified value with capital letters. --- accept.go | 11 ++++------- accept_test.go | 6 ++++++ dial.go | 4 ++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/accept.go b/accept.go index 66379b5d..18536bdb 100644 --- a/accept.go +++ b/accept.go @@ -159,13 +159,13 @@ func verifyClientRequest(w http.ResponseWriter, r *http.Request) (errCode int, _ return http.StatusUpgradeRequired, fmt.Errorf("WebSocket protocol violation: handshake request must be at least HTTP/1.1: %q", r.Proto) } - if !headerContainsToken(r.Header, "Connection", "Upgrade") { + if !headerContainsTokenIgnoreCase(r.Header, "Connection", "Upgrade") { w.Header().Set("Connection", "Upgrade") w.Header().Set("Upgrade", "websocket") return http.StatusUpgradeRequired, fmt.Errorf("WebSocket protocol violation: Connection header %q does not contain Upgrade", r.Header.Get("Connection")) } - if !headerContainsToken(r.Header, "Upgrade", "websocket") { + if !headerContainsTokenIgnoreCase(r.Header, "Upgrade", "websocket") { w.Header().Set("Connection", "Upgrade") w.Header().Set("Upgrade", "websocket") return http.StatusUpgradeRequired, fmt.Errorf("WebSocket protocol violation: Upgrade header %q does not contain websocket", r.Header.Get("Upgrade")) @@ -309,11 +309,9 @@ func acceptWebkitDeflate(w http.ResponseWriter, ext websocketExtension, mode Com return copts, nil } -func headerContainsToken(h http.Header, key, token string) bool { - token = strings.ToLower(token) - +func headerContainsTokenIgnoreCase(h http.Header, key, token string) bool { for _, t := range headerTokens(h, key) { - if t == token { + if strings.EqualFold(t, token) { return true } } @@ -354,7 +352,6 @@ func headerTokens(h http.Header, key string) []string { for _, v := range h[key] { v = strings.TrimSpace(v) for _, t := range strings.Split(v, ",") { - t = strings.ToLower(t) t = strings.TrimSpace(t) tokens = append(tokens, t) } diff --git a/accept_test.go b/accept_test.go index 9b18d8e1..e114d1ad 100644 --- a/accept_test.go +++ b/accept_test.go @@ -224,6 +224,12 @@ func Test_selectSubprotocol(t *testing.T) { serverProtocols: []string{"echo2", "echo3"}, negotiated: "echo3", }, + { + name: "clientCasePresered", + clientProtocols: []string{"Echo1"}, + serverProtocols: []string{"echo1"}, + negotiated: "Echo1", + }, } for _, tc := range testCases { diff --git a/dial.go b/dial.go index 509882e0..7a7787ff 100644 --- a/dial.go +++ b/dial.go @@ -194,11 +194,11 @@ func verifyServerResponse(opts *DialOptions, copts *compressionOptions, secWebSo return nil, fmt.Errorf("expected handshake response status code %v but got %v", http.StatusSwitchingProtocols, resp.StatusCode) } - if !headerContainsToken(resp.Header, "Connection", "Upgrade") { + if !headerContainsTokenIgnoreCase(resp.Header, "Connection", "Upgrade") { return nil, fmt.Errorf("WebSocket protocol violation: Connection header %q does not contain Upgrade", resp.Header.Get("Connection")) } - if !headerContainsToken(resp.Header, "Upgrade", "WebSocket") { + if !headerContainsTokenIgnoreCase(resp.Header, "Upgrade", "WebSocket") { return nil, fmt.Errorf("WebSocket protocol violation: Upgrade header %q does not contain websocket", resp.Header.Get("Upgrade")) }