Skip to content

Commit 343ce35

Browse files
committed
fix: do not canonicalize Sec-WebSocket-* headers in apps
1 parent 85945af commit 343ce35

File tree

1 file changed

+28
-1
lines changed

1 file changed

+28
-1
lines changed

coderd/workspaceapps.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,23 @@ const (
3939
redirectURIQueryParam = "redirect_uri"
4040
)
4141

42+
// nonCanonicalHeaders is a map from "canonical" headers to the actual header we
43+
// should send to the app in the workspace. Some headers (such as the websocket
44+
// upgrade headers from RFC 6455) are not canonical according to the HTTP/1
45+
// spec. Golang has said that they will not add custom cases for these headers,
46+
// so we need to do it ourselves.
47+
//
48+
// Some apps our customers use are sensitive to the case of these headers.
49+
//
50+
// https://github.com/golang/go/issues/18495
51+
var nonCanonicalHeaders = map[string]string{
52+
"Sec-Websocket-Accept": "Sec-WebSocket-Accept",
53+
"Sec-Websocket-Extensions": "Sec-WebSocket-Extensions",
54+
"Sec-Websocket-Key": "Sec-WebSocket-Key",
55+
"Sec-Websocket-Protocol": "Sec-WebSocket-Protocol",
56+
"Sec-Websocket-Version": "Sec-WebSocket-Version",
57+
}
58+
4259
func (api *API) appHost(rw http.ResponseWriter, r *http.Request) {
4360
host := api.AppHostname
4461
if api.AccessURL.Port() != "" {
@@ -708,14 +725,24 @@ func (api *API) proxyWorkspaceApplication(proxyApp proxyApplication, rw http.Res
708725
return
709726
}
710727
defer release()
728+
proxy.Transport = conn.HTTPTransport()
711729

712730
// This strips the session token from a workspace app request.
713731
cookieHeaders := r.Header.Values("Cookie")[:]
714732
r.Header.Del("Cookie")
715733
for _, cookieHeader := range cookieHeaders {
716734
r.Header.Add("Cookie", httpapi.StripCoderCookies(cookieHeader))
717735
}
718-
proxy.Transport = conn.HTTPTransport()
736+
737+
// Convert canonicalized headers to their non-canonicalized counterparts.
738+
// See the comment on `nonCanonicalHeaders` for more information on why this
739+
// is necessary.
740+
for k, v := range r.Header {
741+
if n, ok := nonCanonicalHeaders[k]; ok {
742+
r.Header.Del(k)
743+
r.Header[n] = v
744+
}
745+
}
719746

720747
// end span so we don't get long lived trace data
721748
tracing.EndHTTPSpan(r, http.StatusOK, trace.SpanFromContext(ctx))

0 commit comments

Comments
 (0)