Skip to content

Commit 1ac242a

Browse files
committed
fix: defer CAR response headers until after GetCAR succeeds
move header setting after GetCAR call to ensure proper HTTP status codes (404/410) are returned when paths don't exist, instead of sending 200 with X-Stream-Error header this allows gateway-conformance tests to properly handle non-existing paths in CAR responses
1 parent 8814ef6 commit 1ac242a

File tree

1 file changed

+13
-9
lines changed

1 file changed

+13
-9
lines changed

gateway/handler_car.go

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func (i *handler) serveCAR(ctx context.Context, w http.ResponseWriter, r *http.R
4646
return false
4747
}
4848

49-
// Set Content-Disposition
49+
// Prepare the Content-Disposition header value
5050
var name string
5151
if urlFilename := r.URL.Query().Get("filename"); urlFilename != "" {
5252
name = urlFilename
@@ -57,27 +57,31 @@ func (i *handler) serveCAR(ctx context.Context, w http.ResponseWriter, r *http.R
5757
}
5858
name += ".car"
5959
}
60-
setContentDispositionHeader(w, name, "attachment")
61-
62-
// Set Cache-Control (same logic as for a regular files)
63-
addCacheControlHeaders(w, r, rq.contentPath, rq.ttl, rq.lastMod, rootCid, carResponseFormat)
6460

65-
// Generate the CAR Etag.
61+
// Generate the CAR Etag (needed for early termination check)
6662
etag := getCarEtag(rq.immutablePath, params, rootCid)
67-
w.Header().Set("Etag", etag)
6863

69-
// Terminate early if Etag matches. We cannot rely on handleIfNoneMatch since
70-
// since it does not contain the parameters information we retrieve here.
64+
// Check If-None-Match before calling GetCAR to save resources
7165
if etagMatch(r.Header.Get("If-None-Match"), etag) {
66+
// We can set cache headers even for 304 Not Modified
67+
addCacheControlHeaders(w, r, rq.contentPath, rq.ttl, rq.lastMod, rootCid, carResponseFormat)
68+
w.Header().Set("Etag", etag)
7269
w.WriteHeader(http.StatusNotModified)
7370
return false
7471
}
7572

73+
// Call GetCAR BEFORE setting response headers
74+
// This allows proper error handling with correct HTTP status codes
7675
md, carFile, err := i.backend.GetCAR(ctx, rq.immutablePath, params)
7776
if !i.handleRequestErrors(w, r, rq.contentPath, err) {
7877
return false
7978
}
8079
defer carFile.Close()
80+
81+
// Now that we have successful GetCAR, set all the headers
82+
setContentDispositionHeader(w, name, "attachment")
83+
addCacheControlHeaders(w, r, rq.contentPath, rq.ttl, rq.lastMod, rootCid, carResponseFormat)
84+
w.Header().Set("Etag", etag)
8185
setIpfsRootsHeader(w, rq, &md)
8286

8387
// Make it clear we don't support range-requests over a car stream

0 commit comments

Comments
 (0)