Documentation
¶
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type EventListener ¶
type EventListener interface {
RequestDecodeListener
ResponseEncodeListener
}
EventListener observes both halves of the request/response round trip a server handles: a request being decoded and a response being encoded. Register one with WithEventListener.
type HTTPOption ¶
type HTTPOption func(cfg *httpServerConfig)
HTTPOption is an option configuring a UCAN HTTP server.
func WithEventListener ¶
func WithEventListener(listener EventListener) HTTPOption
WithEventListener registers an EventListener to observe the server's requests and responses as they are decoded and encoded.
func WithHTTPCodec ¶
func WithHTTPCodec(codec transport.InboundCodec[*http.Request, *http.Response]) HTTPOption
func WithReceiptTimestamps ¶
func WithReceiptTimestamps(enabled bool) HTTPOption
WithReceiptTimestamps configures the server to issue receipts with issuance timestamps or not.
func WithValidationOptions ¶
func WithValidationOptions(options ...validator.Option) HTTPOption
type HTTPServer ¶
type HTTPServer struct {
// contains filtered or unexported fields
}
func NewHTTP ¶
func NewHTTP(id ucan.Issuer, options ...HTTPOption) *HTTPServer
NewHTTP creates a new server capable of handling UCAN invocations over HTTP.
func (*HTTPServer) Handle ¶
func (s *HTTPServer) Handle(command ucan.Command, fn execution.HandlerFunc)
func (*HTTPServer) RoundTrip ¶
RoundTrip unpacks and executes an incoming request, returning the response.
func (*HTTPServer) ServeHTTP ¶
func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request)
type RequestDecodeListener ¶
type RequestDecodeListener interface {
OnRequestDecode(ctx context.Context, container ucan.Container) error
}
RequestDecodeListener is an observer with a function that is called after an execution request has been decoded by the codec.
type ResponseEncodeListener ¶
type ResponseEncodeListener interface {
OnResponseEncode(ctx context.Context, container ucan.Container) error
}
ResponseEncodeListener is an observer with a function that is called before an execution response is encoded by the codec.
type Route ¶
type Route struct {
Command ucan.Command
Handler execution.HandlerFunc
}
Route maps a command to the handler that executes it. A Route can be carried as a value — e.g. collected via dependency injection — and applied to a server later with HTTPServer.Handle:
for _, r := range routes {
srv.Handle(r.Command, r.Handler)
}
func NewRoute ¶
func NewRoute(cmd ucan.Command, fn execution.HandlerFunc) Route
NewRoute builds a Route from a command and a handler.
Example ¶
ExampleNewRoute bundles commands with their handlers as server.Route values and registers them on a server in one place. Because a Route's command and handler come from the same binding, their argument and result types cannot drift apart. Collecting routes as values also lets independent subsystems each contribute their own and hand them to the server for registration.
package main
import (
"context"
"fmt"
"net/http"
"net/url"
"github.com/fil-forge/ucantone/binding"
"github.com/fil-forge/ucantone/client"
"github.com/fil-forge/ucantone/execution"
"github.com/fil-forge/ucantone/multikey/ed25519"
"github.com/fil-forge/ucantone/server"
tdm "github.com/fil-forge/ucantone/testutil/datamodel"
"github.com/fil-forge/ucantone/ucan/command"
"github.com/fil-forge/ucantone/ucan/invocation"
)
// echo is the /example/echo command bound to the Go types of its arguments and
// result. Handlers and clients both derive their types from it.
var echo = binding.Bind[*tdm.TestObject, *tdm.TestObject2](command.MustParse("/example/echo"))
// ExampleNewRoute bundles commands with their handlers as [server.Route] values
// and registers them on a server in one place. Because a Route's command and
// handler come from the same binding, their argument and result types cannot
// drift apart. Collecting routes as values also lets independent subsystems
// each contribute their own and hand them to the server for registration.
func main() {
// Each subsystem contributes routes; here, the echo command and a handler
// that returns the argument bytes as a string.
routes := []server.Route{
echo.Route(func(req *binding.Request[*tdm.TestObject], res *binding.Response[*tdm.TestObject2]) error {
args := req.Task().Arguments()
return res.SetSuccess(&tdm.TestObject2{Str: string(args.Bytes)})
}),
}
service, _ := ed25519.GenerateIssuer()
srv := server.NewHTTP(service)
for _, r := range routes {
srv.Handle(r.Command, r.Handler)
}
// Drive the server in-process by using it as the client's HTTP transport.
endpoint, _ := url.Parse("http://echo.example")
c, _ := client.NewHTTP(endpoint, client.WithHTTPClient(&http.Client{Transport: srv}))
// A client invokes the command with typed arguments and unpacks the typed
// result from the receipt.
alice, _ := ed25519.GenerateIssuer()
inv, _ := echo.Invoke(alice, alice.DID(), &tdm.TestObject{Bytes: []byte("hi")}, invocation.WithAudience(service.DID()))
resp, _ := c.Execute(execution.NewRequest(context.Background(), inv))
out, _ := echo.Unpack(resp.Receipt())
fmt.Println(out.Str)
}
Output: hi