From 2f2e7f2fd77ac264685d2e82f3e8538af857ab17 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 22 Jun 2023 14:23:36 +0200 Subject: [PATCH] feat(gateway): parse ipip-402 Accept Header params --- gateway/gateway.go | 13 ++++++++++-- gateway/handler_car.go | 26 +++++++++++++++++++++-- gateway/handler_car_test.go | 42 +++++++++++++++++++++++++++++++++++-- 3 files changed, 75 insertions(+), 6 deletions(-) diff --git a/gateway/gateway.go b/gateway/gateway.go index cc0babba13..d05d80e3ce 100644 --- a/gateway/gateway.go +++ b/gateway/gateway.go @@ -121,8 +121,10 @@ func (i ImmutablePath) IsValid() error { var _ path.Path = (*ImmutablePath)(nil) type CarParams struct { - Range *DagByteRange - Scope DagScope + Range *DagByteRange + Scope DagScope + Order DagOrder + Duplicates *bool } // DagByteRange describes a range request within a UnixFS file. "From" and @@ -189,6 +191,13 @@ const ( DagScopeBlock DagScope = "block" ) +type DagOrder string + +const ( + DagOrderDFS DagOrder = "dfs" + DagOrderUnknown DagOrder = "unk" +) + type ContentPathMetadata struct { PathSegmentRoots []cid.Cid LastSegment path.Resolved diff --git a/gateway/handler_car.go b/gateway/handler_car.go index bf7e0c5eb7..0f6f6e50dc 100644 --- a/gateway/handler_car.go +++ b/gateway/handler_car.go @@ -39,7 +39,7 @@ func (i *handler) serveCAR(ctx context.Context, w http.ResponseWriter, r *http.R return false } - params, err := getCarParams(r) + params, err := getCarParams(r, rq.responseParams) if err != nil { i.webError(w, r, err, http.StatusBadRequest) return false @@ -113,7 +113,7 @@ func (i *handler) serveCAR(ctx context.Context, w http.ResponseWriter, r *http.R return true } -func getCarParams(r *http.Request) (CarParams, error) { +func getCarParams(r *http.Request, formatParams map[string]string) (CarParams, error) { queryParams := r.URL.Query() rangeStr, hasRange := queryParams.Get(carRangeBytesKey), queryParams.Has(carRangeBytesKey) scopeStr, hasScope := queryParams.Get(carTerminalElementTypeKey), queryParams.Has(carTerminalElementTypeKey) @@ -140,6 +140,28 @@ func getCarParams(r *http.Request) (CarParams, error) { params.Scope = DagScopeAll } + switch order := DagOrder(formatParams["order"]); order { + case DagOrderUnknown, DagOrderDFS: + params.Order = order + case "": + params.Order = DagOrderUnknown + default: + return CarParams{}, fmt.Errorf("unsupported order %s", order) + } + + switch dups := formatParams["dups"]; dups { + case "y": + v := true + params.Duplicates = &v + case "n": + v := false + params.Duplicates = &v + case "": + // Acceptable, we do not set anything. + default: + return CarParams{}, fmt.Errorf("unsupported dups %s", dups) + } + return params, nil } diff --git a/gateway/handler_car_test.go b/gateway/handler_car_test.go index 858ccb85d9..54b71181d9 100644 --- a/gateway/handler_car_test.go +++ b/gateway/handler_car_test.go @@ -28,7 +28,7 @@ func TestCarParams(t *testing.T) { } for _, test := range tests { r := mustNewRequest(t, http.MethodGet, "http://example.com/?"+test.query, nil) - params, err := getCarParams(r) + params, err := getCarParams(r, map[string]string{}) if test.expectedError { assert.Error(t, err) } else { @@ -60,7 +60,7 @@ func TestCarParams(t *testing.T) { } for _, test := range tests { r := mustNewRequest(t, http.MethodGet, "http://example.com/?"+test.query, nil) - params, err := getCarParams(r) + params, err := getCarParams(r, map[string]string{}) if test.hasError { assert.Error(t, err) } else { @@ -73,6 +73,44 @@ func TestCarParams(t *testing.T) { } } }) + + t.Run("order and duplicates parsing", func(t *testing.T) { + t.Parallel() + + T := true + F := false + + tests := []struct { + acceptHeader string + expectedOrder DagOrder + expectedDuplicates *bool + }{ + {"application/vnd.ipld.car; order=dfs; dups=y", DagOrderDFS, &T}, + {"application/vnd.ipld.car; order=unk; dups=n", DagOrderUnknown, &F}, + {"application/vnd.ipld.car; order=unk", DagOrderUnknown, nil}, + {"application/vnd.ipld.car; dups=y", DagOrderUnknown, &T}, + {"application/vnd.ipld.car; dups=n", DagOrderUnknown, &F}, + {"application/vnd.ipld.car", DagOrderUnknown, nil}, + } + for _, test := range tests { + r := mustNewRequest(t, http.MethodGet, "http://example.com/", nil) + r.Header.Set("Accept", test.acceptHeader) + + mediaType, formatParams, err := customResponseFormat(r) + assert.NoError(t, err) + assert.Equal(t, carResponseFormat, mediaType) + + params, err := getCarParams(r, formatParams) + assert.NoError(t, err) + require.Equal(t, test.expectedOrder, params.Order) + + if test.expectedDuplicates == nil { + require.Nil(t, params.Duplicates) + } else { + require.Equal(t, *test.expectedDuplicates, *params.Duplicates) + } + } + }) } func TestGetCarEtag(t *testing.T) {