From 9ec7f2b210af61f2323ccb6528f861fc363f7713 Mon Sep 17 00:00:00 2001 From: Maciej Borzecki Date: Wed, 30 Nov 2016 12:21:47 +0100 Subject: [PATCH] rest/request: skip empty strings in values of Access-Control-Request-Headers WebKit browsers may send a preflight CORS request setting Access-Control-Request-Headers to an empty value, what is in turn interpreted as an empty string on Golang's http server side. An example scenario that triggers this behavior in Chrome is doing a xhr POST request and setting progress callback. Request headers reported by browser's developer tools are then the following: :authority:docker.mender.io:8080 :method:OPTIONS :path:/api/integrations/0.1/deployments/images :scheme:https accept:*/* accept-encoding:gzip, deflate, sdch, br accept-language:en-US,en;q=0.8,pl;q=0.6 access-control-request-headers: <--- empty value here access-control-request-method:POST dnt:1 origin:http://localhost:9999 referer:http://localhost:9999/test.html user-agent:Mozilla/5.0 (X11; Linux x86_64) ... It is unclear whether in such case, the client wants to send no headers in the actual request or just a bug in client's code. Since the original request is cured and repacked into CorsInfo it makes sense to skip Access-Control-Request-Headers values that are empty. Signed-off-by: Maciej Borzecki --- rest/request.go | 3 +++ rest/request_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/rest/request.go b/rest/request.go index 9d1d792..f3113ef 100644 --- a/rest/request.go +++ b/rest/request.go @@ -120,6 +120,9 @@ func (r *Request) GetCorsInfo() *CorsInfo { reqHeaders := []string{} rawReqHeaders := r.Header[http.CanonicalHeaderKey("Access-Control-Request-Headers")] for _, rawReqHeader := range rawReqHeaders { + if len(rawReqHeader) == 0 { + continue + } // net/http does not handle comma delimited headers for us for _, reqHeader := range strings.Split(rawReqHeader, ",") { reqHeaders = append(reqHeaders, http.CanonicalHeaderKey(strings.TrimSpace(reqHeader))) diff --git a/rest/request_test.go b/rest/request_test.go index 78c0a2c..4186fee 100644 --- a/rest/request_test.go +++ b/rest/request_test.go @@ -148,3 +148,41 @@ func TestCorsInfoPreflightCors(t *testing.T) { t.Error("OriginUrl must be set") } } + +func TestCorsInfoEmptyAccessControlRequestHeaders(t *testing.T) { + req := defaultRequest("OPTIONS", "http://localhost", nil, t) + req.Request.Header.Set("Origin", "http://another.host") + + // make it a preflight request + req.Request.Header.Set("Access-Control-Request-Method", "PUT") + + // WebKit based browsers may send `Access-Control-Request-Headers:` with + // no value, in which case, the header will be present in requests + // Header map, but its value is an empty string. + req.Request.Header.Set("Access-Control-Request-Headers", "") + corsInfo := req.GetCorsInfo() + if corsInfo == nil { + t.Error("Expected non nil CorsInfo") + } + if corsInfo.IsCors == false { + t.Error("This is a CORS request") + } + if len(corsInfo.AccessControlRequestHeaders) > 0 { + t.Error("Access-Control-Request-Headers should have been removed") + } + + req.Request.Header.Set("Access-Control-Request-Headers", "") + corsInfo = req.GetCorsInfo() + if corsInfo == nil { + t.Error("Expected non nil CorsInfo") + } + if corsInfo.IsCors == false { + t.Error("This is a CORS request") + } + if corsInfo.IsPreflight == false { + t.Error("This is a Preflight request") + } + if len(corsInfo.AccessControlRequestHeaders) > 0 { + t.Error("Empty Access-Control-Request-Headers header should have been removed") + } +}