Skip to content

Commit

Permalink
feat: accept raw block requests
Browse files Browse the repository at this point in the history
  • Loading branch information
rvagg committed Oct 7, 2023
1 parent 90ebb72 commit db11f80
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 22 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ require (
github.com/ipld/go-codec-dagpb v1.6.0
github.com/ipld/go-fixtureplate v0.0.2
github.com/ipld/go-ipld-prime v0.21.0
github.com/ipld/go-trustless-utils v0.3.1
github.com/ipld/go-trustless-utils v0.4.0
github.com/ipld/ipld/specs v0.0.0-20230927010225-ef4dbd703269
github.com/ipni/go-libipni v0.5.2
github.com/ipni/index-provider v0.14.2
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,8 @@ github.com/ipld/go-ipld-adl-hamt v0.0.0-20220616142416-9004dbd839e0/go.mod h1:od
github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH9C2E=
github.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ=
github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20230102063945-1a409dc236dd h1:gMlw/MhNr2Wtp5RwGdsW23cs+yCuj9k2ON7i9MiJlRo=
github.com/ipld/go-trustless-utils v0.3.1 h1:i7lPoo7HbThj93wJO1aow3UKdL6AV/jeTLXzTEBZDsE=
github.com/ipld/go-trustless-utils v0.3.1/go.mod h1:SQR5abLVb2YcZiy9QEsBhNyIPj6ubWISJnrpwNBdmpA=
github.com/ipld/go-trustless-utils v0.4.0 h1:ZSRvbfQG39BzGYeyHVmttTexZAEdA/grWdu6MB1kiJI=
github.com/ipld/go-trustless-utils v0.4.0/go.mod h1:SQR5abLVb2YcZiy9QEsBhNyIPj6ubWISJnrpwNBdmpA=
github.com/ipld/ipld/specs v0.0.0-20230927010225-ef4dbd703269 h1:MXBxKsw8geRJitw8f1dr3EfwbEV0WXVbEH7e/o3p+NI=
github.com/ipld/ipld/specs v0.0.0-20230927010225-ef4dbd703269/go.mod h1:WcT0DfRe+e2QFY0kcbsOnuT6jL5Q0JNZ83I5DHIdStg=
github.com/ipni/go-libipni v0.5.2 h1:9vaYOnR4dskd8p88NOboqI6yVqBwYPNCQ/zOaRSr59I=
Expand Down
61 changes: 44 additions & 17 deletions httpipfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/ipfs/go-cid"
"github.com/ipld/go-ipld-prime/datamodel"
"github.com/ipld/go-ipld-prime/linking"
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
trustlessutils "github.com/ipld/go-trustless-utils"
trustlesshttp "github.com/ipld/go-trustless-utils/http"
)
Expand Down Expand Up @@ -177,14 +178,16 @@ func NewHttpIpfsHandlerFunc(
return
}

// get the preferred `Accept` header if one exists; we should be able to
// handle whatever comes back from here, primarily we're looking for
// the `dups` parameter
accept, err := trustlesshttp.CheckFormat(req)
// get the preferred list of `Accept` headers if one exists; we should be
// able to handle whatever comes back from here.
// firsly we are looking for raw vs car, secondarily we're looking for the
// `dups` parameter if car.
accepts, err := trustlesshttp.CheckFormat(req)
if err != nil {
logError(http.StatusBadRequest, err)
return
}
accept := accepts[0]

fileName, err := trustlesshttp.ParseFilename(req)
if err != nil {
Expand All @@ -200,16 +203,30 @@ func NewHttpIpfsHandlerFunc(
return
}

dagScope, err := trustlesshttp.ParseScope(req)
if err != nil {
logError(http.StatusBadRequest, err)
return
}
var (
dagScope trustlessutils.DagScope = trustlessutils.DagScopeAll
byteRange *trustlessutils.ByteRange = nil
)

byteRange, err := trustlesshttp.ParseByteRange(req)
if err != nil {
logError(http.StatusBadRequest, err)
return
if accept.IsRaw() {
if path.Len() > 0 {
logError(http.StatusBadRequest, errors.New("path not supported for raw requests"))
return
}
} else {
accept = accept.WithMimeType(trustlesshttp.MimeTypeCar) // correct for application/* and */*

dagScope, err = trustlesshttp.ParseScope(req)
if err != nil {
logError(http.StatusBadRequest, err)
return
}

byteRange, err = trustlesshttp.ParseByteRange(req)
if err != nil {
logError(http.StatusBadRequest, err)
return
}
}

request := trustlessutils.Request{
Expand All @@ -228,7 +245,7 @@ func NewHttpIpfsHandlerFunc(
// called once we start writing blocks into the CAR (on the first Put())
res.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", fileName))
res.Header().Set("Cache-Control", trustlesshttp.ResponseCacheControlHeader)
res.Header().Set("Content-Type", accept.WithMimeType(trustlesshttp.MimeTypeCar).WithQuality(1).String())
res.Header().Set("Content-Type", accept.WithQuality(1).String())
etag := request.Etag()
if _, ok := res.(*gziphandler.GzipResponseWriter); ok {
// there are conditions where we may have a GzipResponseWriter but the
Expand All @@ -252,9 +269,19 @@ func NewHttpIpfsHandlerFunc(
}
}

if err := StreamCar(reqCtx, lsys, writer, request); err != nil {
logger.Debugw("error streaming CAR", "cid", rootCid, "err", err)
logError(http.StatusInternalServerError, err)
if accept.IsRaw() {
// send the raw block bytes as the response
if byts, err := lsys.LoadRaw(linking.LinkContext{Ctx: reqCtx}, cidlink.Link{Cid: rootCid}); err != nil {
logError(http.StatusInternalServerError, err)
} else if _, err := writer.Write(byts); err != nil {
logError(http.StatusInternalServerError, err)
}
} else {
// IsCar, so stream the CAR as the response
if err := StreamCar(reqCtx, lsys, writer, request); err != nil {
logger.Debugw("error streaming CAR", "cid", rootCid, "err", err)
logError(http.StatusInternalServerError, err)
}
}
}
}
Expand Down
30 changes: 28 additions & 2 deletions httpipfs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ func TestHttpIpfsHandler(t *testing.T) {
expectedStatusCode: http.StatusInternalServerError,
expectedBody: "failed to load root node: failed to load root CID: ipld: could not find bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi",
},
{
name: "bad raw request",
path: "/ipfs/bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi/path/not/allowed",
accept: trustlesshttp.MimeTypeRaw,
expectedStatusCode: http.StatusBadRequest,
expectedBody: "path not supported for raw requests",
},
} {
t.Run(testCase.name, func(t *testing.T) {
req := require.New(t)
Expand All @@ -107,7 +114,7 @@ func TestHttpIpfsHandler(t *testing.T) {
func TestHttpIpfsIntegration_Unixfs20mVariety(t *testing.T) {
req := require.New(t)

testCases, _, err := trustlesspathing.Unixfs20mVarietyCases()
testCases, rootCid, err := trustlesspathing.Unixfs20mVarietyCases()
req.NoError(err)
storage, closer, err := trustlesspathing.Unixfs20mVarietyReadableStorage()
req.NoError(err)
Expand All @@ -119,7 +126,9 @@ func TestHttpIpfsIntegration_Unixfs20mVariety(t *testing.T) {
lsys.SetReadStorage(storage)

handler := frisbii.NewHttpIpfs(context.Background(), lsys)
testServer := httptest.NewServer(handler)
mux := http.NewServeMux()
mux.Handle("/ipfs/", handler)
testServer := httptest.NewServer(mux)
defer testServer.Close()

for _, tc := range testCases {
Expand Down Expand Up @@ -152,6 +161,23 @@ func TestHttpIpfsIntegration_Unixfs20mVariety(t *testing.T) {
}
})
}

t.Run("raw block", func(t *testing.T) {
req := require.New(t)

request, err := http.NewRequest(http.MethodGet, testServer.URL+"/ipfs/"+rootCid.String(), nil)
req.NoError(err)
request.Header.Set("Accept", trustlesshttp.MimeTypeRaw)
res, err := http.DefaultClient.Do(request)
req.NoError(err)
req.Equal(http.StatusOK, res.StatusCode)
req.Equal(trustlesshttp.MimeTypeRaw, res.Header.Get("Content-Type"))
gotBlock, err := io.ReadAll(res.Body)
req.NoError(err)
expectBlock, err := storage.Get(context.Background(), rootCid.KeyString())
req.NoError(err)
req.Equal(expectBlock, gotBlock)
})
}

func TestHttpIpfsDuplicates(t *testing.T) {
Expand Down

0 comments on commit db11f80

Please sign in to comment.