server

package
v0.0.0-...-22b4465 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 28, 2026 License: Apache-2.0, MIT Imports: 15 Imported by: 0

Documentation

Index

Examples

Constants

View Source
const DefaultMaxConcurrency = 100

DefaultMaxConcurrency is how many invocations of one request execute at the same time unless WithMaxConcurrency says otherwise. It is the value quic-go uses for MaxIncomingStreams and the floor RFC 9113 recommends for SETTINGS_MAX_CONCURRENT_STREAMS; x/net/http2 defaults to 250.

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 WithConcurrentEventListener

func WithConcurrentEventListener(listener EventListener) HTTPOption

WithConcurrentEventListener registers an EventListener whose OnRequestDecode runs on its own goroutine while the request's handlers execute, instead of before them. The server waits for it before any OnResponseEncode runs, so whatever it records is in place by the time the response leaves, and an error from it still fails the request, after the handlers have run. Its OnResponseEncode runs as it does for WithEventListener. The place for a listener that only observes the request, such as one that stores it, and whose work would otherwise delay every handler by its own duration.

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. Its OnRequestDecode runs before any handler does, so an error from it fails the request before anything executes: the place for a listener that gates execution.

func WithListenerPanicLogger

func WithListenerPanicLogger(logger ListenerPanicLogger) HTTPOption

WithListenerPanicLogger sets the function that records a concurrent event listener's recovered panic. The default prints the panic value and stack through the standard log package, as dispatcher.WithPanicLogger's does. Set it to route these panics wherever handler panics go. A nil logger panics.

func WithMaxConcurrency

func WithMaxConcurrency(n int) HTTPOption

WithMaxConcurrency caps how many invocations of one request execute at the same time. Every invocation still gets its own goroutine; the cap is a per-request semaphore that limits how many are admitted at once, the way an HTTP/2 or QUIC server caps concurrent streams per connection. It does not limit the server as a whole: each request in flight gets its own cap.

- WithMaxConcurrency(1) runs the invocations of a request serially, one after another. - WithMaxConcurrency(0) removes the cap. - A negative value panics.

Defaults to DefaultMaxConcurrency.

func WithPanicLogger

func WithPanicLogger(logger dispatcher.PanicLogger) HTTPOption

WithPanicLogger sets the function the server's dispatcher records recovered panics with, from handlers and from validation code alike. See dispatcher.WithPanicLogger for the default and the contract. A panic in a concurrent event listener is recorded by WithListenerPanicLogger instead. A nil logger panics.

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) Execute

func (s *HTTPServer) Execute(req execution.Request) (execution.Response, error)

Execute validates and executes a single invocation.

func (*HTTPServer) ExecuteBatch

func (s *HTTPServer) ExecuteBatch(req *batch.Request) (*batch.Response, error)

ExecuteBatch executes every invocation in the request that is addressed to this server and returns their receipts. Invocations addressed elsewhere are skipped and get no receipt. Each handler sees every token of the request as its metadata, and the tokens handlers attach to their responses are gathered into the metadata of the returned response.

Invocations execute concurrently, each on its own goroutine, with at most DefaultMaxConcurrency of them running at once unless WithMaxConcurrency sets another cap. The cap applies to this request alone: concurrent requests each get their own, so the server as a whole runs up to the cap times the number of requests in flight. Handlers must therefore be safe to call concurrently within one request, as they already must be across requests. Receipts and metadata are gathered in request order once every invocation has finished, so the response does not depend on which handler finished first.

func (*HTTPServer) Handle

func (s *HTTPServer) Handle(command ucan.Command, fn execution.HandlerFunc)

func (*HTTPServer) RoundTrip

func (s *HTTPServer) RoundTrip(r *http.Request) (*http.Response, error)

RoundTrip unpacks and executes an incoming request, returning the response.

func (*HTTPServer) ServeHTTP

func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request)

type ListenerPanicLogger

type ListenerPanicLogger func(request ucan.Container, value any)

ListenerPanicLogger is called with the request and the recovered value when a listener registered with WithConcurrentEventListener panics. The server has already decided the outcome by then: the request fails with an error that does not reveal the panic, since the error reaches the HTTP caller. The logger runs on the panicking goroutine inside the deferred recover, so runtime.Stack or runtime/debug.Stack called from it returns the stack of the panic.

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. Whether it is called before the request's handlers run or alongside them depends on how the listener is registered: see WithEventListener and WithConcurrentEventListener.

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

Directories

Path Synopsis
Package middleware holds authorization checks a server's routes can be served behind.
Package middleware holds authorization checks a server's routes can be served behind.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL