Skip to content

Commit

Permalink
fix(gateway): fix codec range requests
Browse files Browse the repository at this point in the history
  • Loading branch information
aschmahmann authored and hacdias committed Oct 2, 2023
1 parent 6c38d60 commit dcd7a19
Show file tree
Hide file tree
Showing 5 changed files with 24 additions and 12 deletions.
8 changes: 6 additions & 2 deletions gateway/blocks_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,10 @@ func (bb *BlocksBackend) Get(ctx context.Context, path ImmutablePath, ranges ...
}

Check warning on line 162 in gateway/blocks_backend.go

View check run for this annotation

Codecov / codecov/patch

gateway/blocks_backend.go#L161-L162

Added lines #L161 - L162 were not covered by tests

rootCodec := nd.Cid().Prefix().GetCodec()

// This covers both Raw blocks and terminal IPLD codecs like dag-cbor and dag-json
// Note: while only cbor, json, dag-cbor, and dag-json are currently supported by gateways this could change
// Note: For the raw codec we return just the relevant range rather than the entire block
if rootCodec != uint64(mc.DagPb) {
f := files.NewBytesFile(nd.RawData())

Expand All @@ -172,8 +174,10 @@ func (bb *BlocksBackend) Get(ctx context.Context, path ImmutablePath, ranges ...
return ContentPathMetadata{}, nil, err
}

Check warning on line 175 in gateway/blocks_backend.go

View check run for this annotation

Codecov / codecov/patch

gateway/blocks_backend.go#L174-L175

Added lines #L174 - L175 were not covered by tests

if err := seekToRangeStart(f, ra); err != nil {
return ContentPathMetadata{}, nil, err
if rootCodec == uint64(mc.Raw) {
if err := seekToRangeStart(f, ra); err != nil {
return ContentPathMetadata{}, nil, err
}

Check warning on line 180 in gateway/blocks_backend.go

View check run for this annotation

Codecov / codecov/patch

gateway/blocks_backend.go#L179-L180

Added lines #L179 - L180 were not covered by tests
}

return md, NewGetResponseFromReader(f, fileSize), nil
Expand Down
3 changes: 3 additions & 0 deletions gateway/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,9 @@ type IPFSBackend interface {
// file will still need magic bytes from the very beginning for content
// type sniffing).
// - A range request for a directory currently holds no semantic meaning.
// - For non-UnixFS (and non-raw data) such as terminal IPLD dag-cbor/json, etc. blocks the returned response
// bytes should be the complete block and returned as an [io.ReadSeekCloser] starting at the beginning of the
// block rather than as an [io.ReadCloser] that starts at the beginning of the range request.
//
// [HTTP Byte Ranges]: https://httpwg.org/specs/rfc9110.html#rfc.section.14.1.2
Get(context.Context, ImmutablePath, ...ByteRange) (ContentPathMetadata, *GetResponse, error)
Expand Down
2 changes: 1 addition & 1 deletion gateway/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func NewHandler(c Config, backend IPFSBackend) http.Handler {
return newHandlerWithMetrics(&c, backend)
}

// serveContent replies to the request using the content in the provided ReadSeeker
// serveContent replies to the request using the content in the provided Reader
// and returns the status code written and any error encountered during a write.
// It wraps httpServeContent (a close clone of http.ServeContent) which takes care of If-None-Match+Etag,
// Content-Length and range requests.
Expand Down
14 changes: 7 additions & 7 deletions gateway/handler_codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,10 @@ func (i *handler) serveCodec(ctx context.Context, w http.ResponseWriter, r *http
return false
}

Check warning on line 76 in gateway/handler_codec.go

View check run for this annotation

Codecov / codecov/patch

gateway/handler_codec.go#L75-L76

Added lines #L75 - L76 were not covered by tests

if !i.seekToStartOfFirstRange(w, r, data) {
return false
}

return i.renderCodec(ctx, w, r, rq, blockSize, data)
}

func (i *handler) renderCodec(ctx context.Context, w http.ResponseWriter, r *http.Request, rq *requestData, blockSize int64, blockData io.ReadCloser) bool {
func (i *handler) renderCodec(ctx context.Context, w http.ResponseWriter, r *http.Request, rq *requestData, blockSize int64, blockData io.ReadSeekCloser) bool {
resolvedPath := rq.pathMetadata.LastSegment
ctx, span := spanTrace(ctx, "Handler.RenderCodec", trace.WithAttributes(attribute.String("path", resolvedPath.String()), attribute.String("requestedContentType", rq.responseFormat)))
defer span.End()
Expand Down Expand Up @@ -239,9 +235,13 @@ func parseNode(blockCid cid.Cid, blockData io.Reader) *assets.ParsedNode {
}

// serveCodecRaw returns the raw block without any conversion
func (i *handler) serveCodecRaw(ctx context.Context, w http.ResponseWriter, r *http.Request, blockSize int64, blockData io.ReadCloser, contentPath ipath.Path, modtime, begin time.Time) bool {
func (i *handler) serveCodecRaw(ctx context.Context, w http.ResponseWriter, r *http.Request, blockSize int64, blockData io.ReadSeekCloser, contentPath ipath.Path, modtime, begin time.Time) bool {
// ServeContent will take care of
// If-None-Match+Etag, Content-Length and range requests
// If-None-Match+Etag, Content-Length and setting range request headers after we've already seeked to the start of
// the first range
if !i.seekToStartOfFirstRange(w, r, blockData) {
return false
}

Check warning on line 244 in gateway/handler_codec.go

View check run for this annotation

Codecov / codecov/patch

gateway/handler_codec.go#L243-L244

Added lines #L243 - L244 were not covered by tests
_, dataSent, _ := serveContent(w, r, modtime, blockSize, blockData)

if dataSent {
Expand Down
9 changes: 7 additions & 2 deletions gateway/handler_defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,19 @@ func (i *handler) serveDefaults(ctx context.Context, w http.ResponseWriter, r *h
case mc.Json, mc.DagJson, mc.Cbor, mc.DagCbor:
rq.logger.Debugw("serving codec", "path", rq.contentPath)
var blockSize int64
var dataToRender io.ReadCloser
var dataToRender io.ReadSeekCloser
if headResp != nil {
blockSize = headResp.bytesSize
dataToRender = nil

Check warning on line 105 in gateway/handler_defaults.go

View check run for this annotation

Codecov / codecov/patch

gateway/handler_defaults.go#L104-L105

Added lines #L104 - L105 were not covered by tests
} else {
blockSize = getResp.bytesSize
dataToRender = getResp.bytes
dataAsReadSeekCloser, ok := getResp.bytes.(io.ReadSeekCloser)
if !ok {
i.webError(w, r, fmt.Errorf("expected returned non-UnixFS data to be seekable"), http.StatusInternalServerError)
}

Check warning on line 111 in gateway/handler_defaults.go

View check run for this annotation

Codecov / codecov/patch

gateway/handler_defaults.go#L110-L111

Added lines #L110 - L111 were not covered by tests
dataToRender = dataAsReadSeekCloser
}

return i.renderCodec(r.Context(), w, r, rq, blockSize, dataToRender)
default:
rq.logger.Debugw("serving unixfs", "path", rq.contentPath)
Expand Down

0 comments on commit dcd7a19

Please sign in to comment.