Skip to content

Commit 6ac0716

Browse files
lidelStebalien
authored andcommitted
fix(gateway): curl without redirect on localhost
When request is sent to http://localhost:8080/ipfs/$cid response has HTTP 301 status code and "Location" header with redirect destination at $cid.ipfs.localhost:8080 Redirect is followed by browsersi, but not by commandline tools. Status 301 is ignored by curl in default mode: it will print response and won't follow redirect, user needs to add -L for that. To fix curl, we return correct payload in body of HTTP 301 response, but set Clear-Site-Data header to ensure Origin sandbox can't be abused. This requires a surgical workaround: If Location header is present in ResponseWriter's Header map, we ensure http.ServeContent() returns HTTP 301 Context: ipfs/kubo#6982 License: MIT Signed-off-by: Marcin Rataj <[email protected]> This commit was moved from ipfs/kubo@f9567a0
1 parent df09179 commit 6ac0716

File tree

2 files changed

+38
-2
lines changed

2 files changed

+38
-2
lines changed

gateway/core/corehttp/gateway_handler.go

+20
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,25 @@ type gatewayHandler struct {
3838
api coreiface.CoreAPI
3939
}
4040

41+
// StatusResponseWriter enables us to override HTTP Status Code passed to
42+
// WriteHeader function inside of http.ServeContent. Decision is based on
43+
// presence of HTTP Headers such as Location.
44+
type statusResponseWriter struct {
45+
http.ResponseWriter
46+
}
47+
48+
func (sw *statusResponseWriter) WriteHeader(code int) {
49+
// Check if we need to adjust Status Code to account for scheduled redirect
50+
// This enables us to return payload along with HTTP 301
51+
// for subdomain redirect in web browsers while also returning body for cli
52+
// tools which do not follow redirects by default (curl, wget).
53+
redirect := sw.ResponseWriter.Header().Get("Location")
54+
if redirect != "" && code == http.StatusOK {
55+
code = http.StatusMovedPermanently
56+
}
57+
sw.ResponseWriter.WriteHeader(code)
58+
}
59+
4160
func newGatewayHandler(c GatewayConfig, api coreiface.CoreAPI) *gatewayHandler {
4261
i := &gatewayHandler{
4362
config: c,
@@ -366,6 +385,7 @@ func (i *gatewayHandler) serveFile(w http.ResponseWriter, req *http.Request, nam
366385
}
367386
w.Header().Set("Content-Type", ctype)
368387

388+
w = &statusResponseWriter{w}
369389
http.ServeContent(w, req, name, modtime, content)
370390
}
371391

gateway/core/corehttp/hostname.go

+18-2
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,24 @@ func HostnameOption() ServeOption {
9595
// Yes, redirect if applicable
9696
// Example: dweb.link/ipfs/{cid} → {cid}.ipfs.dweb.link
9797
if newURL, ok := toSubdomainURL(r.Host, r.URL.Path, r); ok {
98-
http.Redirect(w, r, newURL, http.StatusMovedPermanently)
99-
return
98+
// Just to be sure single Origin can't be abused in
99+
// web browsers that ignored the redirect for some
100+
// reason, Clear-Site-Data header clears browsing
101+
// data (cookies, storage etc) associated with
102+
// hostname's root Origin
103+
// Note: we can't use "*" due to bug in Chromium:
104+
// https://bugs.chromium.org/p/chromium/issues/detail?id=898503
105+
w.Header().Set("Clear-Site-Data", "\"cookies\", \"storage\"")
106+
107+
// Set "Location" header with redirect destination.
108+
// It is ignored by curl in default mode, but will
109+
// be respected by user agents that follow
110+
// redirects by default, namely web browsers
111+
w.Header().Set("Location", newURL)
112+
113+
// Note: we continue regular gateway processing:
114+
// HTTP Status Code http.StatusMovedPermanently
115+
// will be set later, in statusResponseWriter
100116
}
101117
}
102118

0 commit comments

Comments
 (0)