Skip to content

Commit 27d8c81

Browse files
committed
feat!: context passing
1 parent cfd1ba5 commit 27d8c81

11 files changed

Lines changed: 214 additions & 196 deletions

File tree

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ inv, _ := invocation.Invoke(signer, audience, capability, delegation.WithProofs(
6767
invocations := []invocation.Invocation{inv}
6868

6969
// send the invocation(s) to the service
70-
resp, _ := client.Execute(invocations, conn)
70+
resp, _ := client.Execute(context.Background(), invocations, conn)
7171

7272
// define datamodels for ok and error outcome
7373
type OkModel struct {
@@ -144,7 +144,7 @@ func createServer(signer principal.Signer) (server.ServerView, error) {
144144
testecho.Can(),
145145
server.Provide(
146146
testecho,
147-
func(cap ucan.Capability[TestEcho], inv invocation.Invocation, ctx server.InvocationContext) (TestEcho, receipt.Effects, error) {
147+
func(ctx context.Context, cap ucan.Capability[TestEcho], inv invocation.Invocation, ictx server.InvocationContext) (TestEcho, receipt.Effects, error) {
148148
return TestEcho{Echo: cap.Nb().Echo}, nil, nil
149149
},
150150
),
@@ -157,7 +157,7 @@ func main() {
157157
server, _ := createServer(signer)
158158

159159
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
160-
res, _ := server.Request(uhttp.NewHTTPRequest(r.Body, r.Header))
160+
res, _ := server.Request(r.Context(), uhttp.NewHTTPRequest(r.Body, r.Header))
161161

162162
for key, vals := range res.Headers() {
163163
for _, v := range vals {

client/connection.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package client
22

33
import (
4+
"context"
45
"crypto/sha256"
56
"fmt"
67
"hash"
@@ -101,7 +102,7 @@ type ExecutionResponse interface {
101102
Get(inv ucan.Link) (ucan.Link, bool)
102103
}
103104

104-
func Execute(invocations []invocation.Invocation, conn Connection) (ExecutionResponse, error) {
105+
func Execute(ctx context.Context, invocations []invocation.Invocation, conn Connection) (ExecutionResponse, error) {
105106
input, err := message.Build(invocations, nil)
106107
if err != nil {
107108
return nil, fmt.Errorf("building message: %s", err)
@@ -112,7 +113,7 @@ func Execute(invocations []invocation.Invocation, conn Connection) (ExecutionRes
112113
return nil, fmt.Errorf("encoding message: %s", err)
113114
}
114115

115-
res, err := conn.Channel().Request(req)
116+
res, err := conn.Channel().Request(ctx, req)
116117
if err != nil {
117118
return nil, fmt.Errorf("sending message: %s", err)
118119
}

server/handler.go

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package server
22

33
import (
4+
"context"
5+
46
"github.com/storacha/go-ucanto/core/invocation"
57
"github.com/storacha/go-ucanto/core/ipld"
68
"github.com/storacha/go-ucanto/core/receipt/fx"
@@ -12,47 +14,47 @@ import (
1214
"github.com/storacha/go-ucanto/validator"
1315
)
1416

15-
type HandlerFunc[C any, O ipld.Builder] func(capability ucan.Capability[C], invocation invocation.Invocation, context InvocationContext) (out O, fx fx.Effects, err error)
17+
type HandlerFunc[C any, O ipld.Builder] func(ctx context.Context, capability ucan.Capability[C], invocation invocation.Invocation, context InvocationContext) (out O, fx fx.Effects, err error)
1618

1719
// Provide is used to define given capability provider. It decorates the passed
1820
// handler and takes care of UCAN validation. It only calls the handler
1921
// when validation succeeds.
2022
func Provide[C any, O ipld.Builder](capability validator.CapabilityParser[C], handler HandlerFunc[C, O]) ServiceMethod[O] {
21-
return func(invocation invocation.Invocation, context InvocationContext) (transaction.Transaction[O, ipld.Builder], error) {
23+
return func(ctx context.Context, invocation invocation.Invocation, ictx InvocationContext) (transaction.Transaction[O, ipld.Builder], error) {
2224
vctx := validator.NewValidationContext(
23-
context.ID().Verifier(),
25+
ictx.ID().Verifier(),
2426
capability,
25-
context.CanIssue,
26-
context.ValidateAuthorization,
27-
context.ResolveProof,
28-
context.ParsePrincipal,
29-
context.ResolveDIDKey,
30-
context.AuthorityProofs()...,
27+
ictx.CanIssue,
28+
ictx.ValidateAuthorization,
29+
ictx.ResolveProof,
30+
ictx.ParsePrincipal,
31+
ictx.ResolveDIDKey,
32+
ictx.AuthorityProofs()...,
3133
)
3234

3335
// confirm the audience of the invocation is this service or any of the configured alternative audiences
34-
acceptedAudiences := schema.Literal(context.ID().DID().String())
35-
if len(context.AlternativeAudiences()) > 0 {
36-
altAudiences := make([]schema.Reader[string, string], 0, len(context.AlternativeAudiences()))
37-
for _, a := range context.AlternativeAudiences() {
36+
acceptedAudiences := schema.Literal(ictx.ID().DID().String())
37+
if len(ictx.AlternativeAudiences()) > 0 {
38+
altAudiences := make([]schema.Reader[string, string], 0, len(ictx.AlternativeAudiences()))
39+
for _, a := range ictx.AlternativeAudiences() {
3840
altAudiences = append(altAudiences, schema.Literal(a.DID().String()))
3941
}
4042

4143
acceptedAudiences = schema.Or(append(altAudiences, acceptedAudiences)...)
4244
}
4345

4446
if _, err := acceptedAudiences.Read(invocation.Audience().DID().String()); err != nil {
45-
expectedAudiences := append([]ucan.Principal{context.ID()}, context.AlternativeAudiences()...)
47+
expectedAudiences := append([]ucan.Principal{ictx.ID()}, ictx.AlternativeAudiences()...)
4648
audErr := NewInvalidAudienceError(invocation.Audience(), expectedAudiences...)
4749
return transaction.NewTransaction(result.Error[O, ipld.Builder](audErr)), nil
4850
}
4951

50-
auth, aerr := validator.Access(invocation, vctx)
52+
auth, aerr := validator.Access(ctx, invocation, vctx)
5153
if aerr != nil {
5254
return transaction.NewTransaction(result.Error[O, ipld.Builder](failure.FromError(aerr))), nil
5355
}
5456

55-
o, fx, herr := handler(auth.Capability(), invocation, context)
57+
o, fx, herr := handler(ctx, auth.Capability(), invocation, ictx)
5658
if herr != nil {
5759
return nil, herr
5860
}

server/options.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package server
22

33
import (
4+
"context"
5+
46
"github.com/storacha/go-ucanto/core/delegation"
57
"github.com/storacha/go-ucanto/core/invocation"
68
"github.com/storacha/go-ucanto/core/ipld"
@@ -29,8 +31,8 @@ type srvConfig struct {
2931

3032
func WithServiceMethod[O ipld.Builder](can string, handleFunc ServiceMethod[O]) Option {
3133
return func(cfg *srvConfig) error {
32-
cfg.service[can] = func(input invocation.Invocation, context InvocationContext) (transaction.Transaction[ipld.Builder, ipld.Builder], error) {
33-
tx, err := handleFunc(input, context)
34+
cfg.service[can] = func(ctx context.Context, input invocation.Invocation, invCtx InvocationContext) (transaction.Transaction[ipld.Builder, ipld.Builder], error) {
35+
tx, err := handleFunc(ctx, input, invCtx)
3436
if err != nil {
3537
return nil, err
3638
}

server/server.go

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package server
22

33
import (
4+
"context"
5+
"errors"
46
"fmt"
57
"net/http"
68
"os"
@@ -42,7 +44,7 @@ type InvocationContext interface {
4244
}
4345

4446
// ServiceMethod is an invocation handler.
45-
type ServiceMethod[O ipld.Builder] func(input invocation.Invocation, context InvocationContext) (transaction.Transaction[O, ipld.Builder], error)
47+
type ServiceMethod[O ipld.Builder] func(context.Context, invocation.Invocation, InvocationContext) (transaction.Transaction[O, ipld.Builder], error)
4648

4749
// Service is a mapping of service names to handlers, used to define a
4850
// service implementation.
@@ -68,7 +70,7 @@ type ServerView interface {
6870
Server
6971
transport.Channel
7072
// Run executes a single invocation and returns a receipt.
71-
Run(invocation ServiceInvocation) (receipt.AnyReceipt, error)
73+
Run(ctx context.Context, invocation ServiceInvocation) (receipt.AnyReceipt, error)
7274
}
7375

7476
// ErrorHandlerFunc allows non-result errors generated during handler execution
@@ -102,7 +104,7 @@ func NewServer(id principal.Signer, options ...Option) (ServerView, error) {
102104

103105
validateAuthorization := cfg.validateAuthorization
104106
if validateAuthorization == nil {
105-
validateAuthorization = func(auth validator.Authorization[any]) validator.Revoked {
107+
validateAuthorization = func(context.Context, validator.Authorization[any]) validator.Revoked {
106108
return nil
107109
}
108110
}
@@ -122,7 +124,7 @@ func NewServer(id principal.Signer, options ...Option) (ServerView, error) {
122124
resolveDIDKey = validator.FailDIDKeyResolution
123125
}
124126

125-
ctx := context{id, canIssue, validateAuthorization, resolveProof, parsePrincipal, resolveDIDKey, cfg.authorityProofs, cfg.altAudiences}
127+
ctx := serverContext{id, canIssue, validateAuthorization, resolveProof, parsePrincipal, resolveDIDKey, cfg.authorityProofs, cfg.altAudiences}
126128
svr := &server{id, cfg.service, ctx, codec, catch}
127129
return svr, nil
128130
}
@@ -132,7 +134,7 @@ func ParsePrincipal(str string) (principal.Verifier, error) {
132134
return verifier.Parse(str)
133135
}
134136

135-
type context struct {
137+
type serverContext struct {
136138
id principal.Signer
137139
canIssue validator.CanIssueFunc[any]
138140
validateAuthorization validator.RevocationCheckerFunc[any]
@@ -143,36 +145,36 @@ type context struct {
143145
altAudiences []ucan.Principal
144146
}
145147

146-
func (ctx context) ID() principal.Signer {
148+
func (ctx serverContext) ID() principal.Signer {
147149
return ctx.id
148150
}
149151

150-
func (ctx context) CanIssue(capability ucan.Capability[any], issuer did.DID) bool {
151-
return ctx.canIssue(capability, issuer)
152+
func (sctx serverContext) CanIssue(capability ucan.Capability[any], issuer did.DID) bool {
153+
return sctx.canIssue(capability, issuer)
152154
}
153155

154-
func (ctx context) ValidateAuthorization(auth validator.Authorization[any]) validator.Revoked {
155-
return ctx.validateAuthorization(auth)
156+
func (sctx serverContext) ValidateAuthorization(ctx context.Context, auth validator.Authorization[any]) validator.Revoked {
157+
return sctx.validateAuthorization(ctx, auth)
156158
}
157159

158-
func (ctx context) ResolveProof(proof ucan.Link) (delegation.Delegation, validator.UnavailableProof) {
159-
return ctx.resolveProof(proof)
160+
func (sctx serverContext) ResolveProof(ctx context.Context, proof ucan.Link) (delegation.Delegation, validator.UnavailableProof) {
161+
return sctx.resolveProof(ctx, proof)
160162
}
161163

162-
func (ctx context) ParsePrincipal(str string) (principal.Verifier, error) {
163-
return ctx.parsePrincipal(str)
164+
func (sctx serverContext) ParsePrincipal(str string) (principal.Verifier, error) {
165+
return sctx.parsePrincipal(str)
164166
}
165167

166-
func (ctx context) ResolveDIDKey(did did.DID) (did.DID, validator.UnresolvedDID) {
167-
return ctx.resolveDIDKey(did)
168+
func (sctx serverContext) ResolveDIDKey(ctx context.Context, did did.DID) (did.DID, validator.UnresolvedDID) {
169+
return sctx.resolveDIDKey(ctx, did)
168170
}
169171

170-
func (ctx context) AuthorityProofs() []delegation.Delegation {
171-
return ctx.authorityProofs
172+
func (sctx serverContext) AuthorityProofs() []delegation.Delegation {
173+
return sctx.authorityProofs
172174
}
173175

174-
func (ctx context) AlternativeAudiences() []ucan.Principal {
175-
return ctx.altAudiences
176+
func (sctx serverContext) AlternativeAudiences() []ucan.Principal {
177+
return sctx.altAudiences
176178
}
177179

178180
type server struct {
@@ -199,12 +201,12 @@ func (srv *server) Codec() transport.InboundCodec {
199201
return srv.codec
200202
}
201203

202-
func (srv *server) Request(request transport.HTTPRequest) (transport.HTTPResponse, error) {
203-
return Handle(srv, request)
204+
func (srv *server) Request(ctx context.Context, request transport.HTTPRequest) (transport.HTTPResponse, error) {
205+
return Handle(ctx, srv, request)
204206
}
205207

206-
func (srv *server) Run(invocation ServiceInvocation) (receipt.AnyReceipt, error) {
207-
return Run(srv, invocation)
208+
func (srv *server) Run(ctx context.Context, invocation ServiceInvocation) (receipt.AnyReceipt, error) {
209+
return Run(ctx, srv, invocation)
208210
}
209211

210212
func (srv *server) Catch(err HandlerExecutionError[any]) {
@@ -214,7 +216,7 @@ func (srv *server) Catch(err HandlerExecutionError[any]) {
214216
var _ transport.Channel = (*server)(nil)
215217
var _ ServerView = (*server)(nil)
216218

217-
func Handle(server Server, request transport.HTTPRequest) (transport.HTTPResponse, error) {
219+
func Handle(ctx context.Context, server Server, request transport.HTTPRequest) (transport.HTTPResponse, error) {
218220
selection, aerr := server.Codec().Accept(request)
219221
if aerr != nil {
220222
return thttp.NewHTTPResponse(aerr.Status(), strings.NewReader(aerr.Error()), aerr.Headers()), nil
@@ -225,15 +227,15 @@ func Handle(server Server, request transport.HTTPRequest) (transport.HTTPRespons
225227
return thttp.NewHTTPResponse(http.StatusBadRequest, strings.NewReader("The server failed to decode the request payload. Please format the payload according to the specified media type."), nil), nil
226228
}
227229

228-
result, err := Execute(server, msg)
230+
result, err := Execute(ctx, server, msg)
229231
if err != nil {
230232
return nil, err
231233
}
232234

233235
return selection.Encoder().Encode(result)
234236
}
235237

236-
func Execute(server Server, msg message.AgentMessage) (message.AgentMessage, error) {
238+
func Execute(ctx context.Context, server Server, msg message.AgentMessage) (message.AgentMessage, error) {
237239
br, err := blockstore.NewBlockReader(blockstore.WithBlocksIterator(msg.Blocks()))
238240
if err != nil {
239241
return nil, err
@@ -256,7 +258,7 @@ func Execute(server Server, msg message.AgentMessage) (message.AgentMessage, err
256258
wg.Add(1)
257259
go func(inv invocation.Invocation) {
258260
defer wg.Done()
259-
rcpt, err := Run(server, inv)
261+
rcpt, err := Run(ctx, server, inv)
260262
if err != nil {
261263
rerr = err
262264
return
@@ -276,7 +278,7 @@ func Execute(server Server, msg message.AgentMessage) (message.AgentMessage, err
276278
return message.Build(nil, rcpts)
277279
}
278280

279-
func Run(server Server, invocation ServiceInvocation) (receipt.AnyReceipt, error) {
281+
func Run(ctx context.Context, server Server, invocation ServiceInvocation) (receipt.AnyReceipt, error) {
280282
caps := invocation.Capabilities()
281283
// Invocation needs to have one single capability
282284
if len(caps) != 1 {
@@ -291,8 +293,11 @@ func Run(server Server, invocation ServiceInvocation) (receipt.AnyReceipt, error
291293
return receipt.Issue(server.ID(), result.NewFailure(err), ran.FromInvocation(invocation))
292294
}
293295

294-
tx, err := handle(invocation, server.Context())
296+
tx, err := handle(ctx, invocation, server.Context())
295297
if err != nil {
298+
if errors.Is(err, context.Canceled) {
299+
return nil, err
300+
}
296301
herr := NewHandlerExecutionError(err, cap)
297302
server.Catch(herr)
298303
return receipt.Issue(server.ID(), result.NewFailure(herr), ran.FromInvocation(invocation))

0 commit comments

Comments
 (0)