From a9658f6554287d7d8298e468361e02908916f288 Mon Sep 17 00:00:00 2001 From: Alan Shaw Date: Thu, 24 Sep 2026 17:11:56 +0100 Subject: [PATCH] fix: fallout from ucantone request context fix --- go.mod | 2 +- go.sum | 4 +-- ucan/retrieval/client_test.go | 49 +++++++++++++++++++++++++++++++++++ ucan/retrieval/transport.go | 18 ++++++++----- 4 files changed, 63 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index ee8367e..da5a5b7 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.27.0 require ( github.com/alanshaw/dag-json-gen v0.0.9 github.com/fil-forge/automobile v0.0.1 - github.com/fil-forge/ucantone v0.0.0-20260827134420-25cf8340b9a1 + github.com/fil-forge/ucantone v0.0.0-20260924160040-c31dec73d9b3 github.com/filecoin-project/go-data-segment v0.0.1 github.com/filecoin-project/go-fil-commcid v0.3.1 github.com/filecoin-project/go-fil-commp-hashhash v0.4.0 diff --git a/go.sum b/go.sum index 5cd770c..75d4860 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/alanshaw/dag-json-gen v0.0.9 h1:q59Ra1mQB6HnFGI1ilFxsZvF1CqGN0XDlUWq1 github.com/alanshaw/dag-json-gen v0.0.9/go.mod h1:v1YBZcS4B355MqxtyQr+fGNbEhm0CzHd+gOqOO/MZ+I= github.com/fil-forge/automobile v0.0.1 h1:9xB3yc4l5b9EdRJSJcNwudgBFNHoMPEAdcb7GfobLhA= github.com/fil-forge/automobile v0.0.1/go.mod h1:TsO7jlO8ykJZY5tF8j4GsUcu3F02lEzxO7ULoB61hRA= -github.com/fil-forge/ucantone v0.0.0-20260827134420-25cf8340b9a1 h1:Tzy3lZ7+LAyVK4+qyWM97UMOtnXx05Wdn+rcTDLdBDE= -github.com/fil-forge/ucantone v0.0.0-20260827134420-25cf8340b9a1/go.mod h1:aX35jlUs3hnWAhigjO8z9kMPiYnkW3XOW8iVoJyz7cg= +github.com/fil-forge/ucantone v0.0.0-20260924160040-c31dec73d9b3 h1:Ta5ODMWR9mM4zMfRp/K1unAKvnt7f5dhehRA+YHiv7w= +github.com/fil-forge/ucantone v0.0.0-20260924160040-c31dec73d9b3/go.mod h1:aX35jlUs3hnWAhigjO8z9kMPiYnkW3XOW8iVoJyz7cg= github.com/filecoin-project/go-data-segment v0.0.1 h1:1wmDxOG4ubWQm3ZC1XI5nCon5qgSq7Ra3Rb6Dbu10Gs= github.com/filecoin-project/go-data-segment v0.0.1/go.mod h1:H0/NKbsRxmRFBcLibmABv+yFNHdmtl5AyplYLnb0Zv4= github.com/filecoin-project/go-fil-commcid v0.3.1 h1:4EfxpHSlvtkOqa9weG2Yt5kxFmPib2xU7Uc9Lbqk7fs= diff --git a/ucan/retrieval/client_test.go b/ucan/retrieval/client_test.go index fac5922..6313c9f 100644 --- a/ucan/retrieval/client_test.go +++ b/ucan/retrieval/client_test.go @@ -173,8 +173,57 @@ func TestClient(t *testing.T) { require.Equal(t, 1, httpClient.Transport.(*countingTransport).count) }) + + t.Run("the request carries the caller's context", func(t *testing.T) { + alice := testutil.RandomIssuer(t) + serviceURL, service := startTestServer(t, func(req execution.Request, res execution.Response) error { + return res.SetSuccess(datamodel.Map{}) + }) + + type key struct{} + var got any + httpClient := &http.Client{Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) { + got = r.Context().Value(key{}) + return http.DefaultTransport.RoundTrip(r) + })} + client, err := retrieval.NewClient(serviceURL, retrieval.WithHTTPClient(httpClient)) + require.NoError(t, err) + + inv, err := contentRetrieve.Invoke(alice, alice.DID(), &datamodel.Map{}, invocation.WithAudience(service.DID())) + require.NoError(t, err) + + ctx := context.WithValue(t.Context(), key{}, "caller") + res, err := client.Execute(execution.NewRequest(ctx, inv)) + require.NoError(t, err) + if hc, ok := res.Metadata().(*retrieval.HTTPHeaderResponseContainer); ok && hc.Body != nil { + require.NoError(t, hc.Body.Close()) + } + require.Equal(t, "caller", got) + }) + + t.Run("a canceled context aborts the request", func(t *testing.T) { + alice := testutil.RandomIssuer(t) + serviceURL, service := startTestServer(t, func(req execution.Request, res execution.Response) error { + t.Error("the request reached the server") + return res.SetSuccess(datamodel.Map{}) + }) + client, err := retrieval.NewClient(serviceURL) + require.NoError(t, err) + + inv, err := contentRetrieve.Invoke(alice, alice.DID(), &datamodel.Map{}, invocation.WithAudience(service.DID())) + require.NoError(t, err) + + ctx, cancel := context.WithCancel(t.Context()) + cancel() + _, err = client.Execute(execution.NewRequest(ctx, inv)) + require.ErrorIs(t, err, context.Canceled) + }) } +type roundTripFunc func(*http.Request) (*http.Response, error) + +func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) } + type recordingListener struct { encoded ucan.Container decoded ucan.Container diff --git a/ucan/retrieval/transport.go b/ucan/retrieval/transport.go index bd90fbd..74c9442 100644 --- a/ucan/retrieval/transport.go +++ b/ucan/retrieval/transport.go @@ -1,6 +1,7 @@ package retrieval import ( + "context" "fmt" "io" "net/http" @@ -91,10 +92,10 @@ type HTTPHeaderOutboundCodec struct{} var _ transport.OutboundCodec[*http.Request, *http.Response] = (*HTTPHeaderOutboundCodec)(nil) -func (h *HTTPHeaderOutboundCodec) Encode(c ucan.Container) (*http.Request, error) { +func (h *HTTPHeaderOutboundCodec) Encode(ctx context.Context, c ucan.Container) (*http.Request, error) { method := http.MethodGet headers := http.Header{} - var body io.ReadCloser + var body io.Reader if hc, ok := c.(*HTTPHeaderRequestContainer); ok { if hc.Method != "" { method = hc.Method @@ -102,13 +103,16 @@ func (h *HTTPHeaderOutboundCodec) Encode(c ucan.Container) (*http.Request, error if hc.Header != nil { headers = hc.Header } - body = hc.Body + if hc.Body != nil { + body = hc.Body + } } - req := &http.Request{ - Method: method, - Body: body, - Header: headers, + // The URL is the transport's to set. + req, err := http.NewRequestWithContext(ctx, method, "", body) + if err != nil { + return nil, fmt.Errorf("creating request: %w", err) } + req.Header = headers ctBytes, err := container.Encode(container.Base64Gzip, c) if err != nil { return nil, fmt.Errorf("encoding container: %w", err)