13 Go Guide
spotdemo4 edited this page 2026-07-11 11:35:52 -04:00

Go Guide

The Go runtime lives in trevrpc-go/ with module path trev.zip/llc/trevrpc/trevrpc-go. It provides client helpers, server routing, quic-go and WebTransport integration, shared wire types, status handling, metadata validation, and stream helpers.

Generated Service Shape

For the greeter example, generated Go code has the same shape as this hand-written example:

type GreeterServer interface {
	SayHello(context.Context, *HelloRequest) (*HelloReply, error)
	LotsOfReplies(context.Context, *HelloRequest) (trevrpc.MessageStream[*HelloReply], error)
	LotsOfGreetings(context.Context, trevrpc.MessageStream[*HelloRequest]) (*HelloReply, error)
	BidiHello(context.Context, trevrpc.MessageStream[*HelloRequest]) (trevrpc.MessageStream[*HelloReply], error)
}

Generated clients accept the routine *trevrpc.Channel through the trevrpc.Transport interface and optional default call options:

client := greeter.NewGreeterClient(
	channel,
	trevrpc.WithTimeout(5*time.Second),
)

Implement a Service

type greeterService struct{}

func (greeterService) SayHello(_ context.Context, request *greeter.HelloRequest) (*greeter.HelloReply, error) {
	return &greeter.HelloReply{Message: "hello " + request.Name}, nil
}

func (greeterService) LotsOfReplies(_ context.Context, request *greeter.HelloRequest) (trevrpc.MessageStream[*greeter.HelloReply], error) {
	return trevrpc.FromSlice(
		&greeter.HelloReply{Message: "hello " + request.Name},
		&greeter.HelloReply{Message: "welcome to TrevRPC over QUIC"},
	), nil
}

Client-streaming handlers can range over all request messages:

func (greeterService) LotsOfGreetings(_ context.Context, requests trevrpc.MessageStream[*greeter.HelloRequest]) (*greeter.HelloReply, error) {
	var names []string
	for request, err := range trevrpc.Messages(requests) {
		if err != nil {
			return nil, err
		}
		names = append(names, request.Name)
	}
	return &greeter.HelloReply{Message: "hello, " + strings.Join(names, ", ")}, nil
}

Register a Server

server := trevrpc.NewServer()
greeter.RegisterGreeterServer(server, greeterService{})

Attach authorization or metrics before serving:

server.SetAuthorizer(trevrpc.BearerAuthorizer("trevrpc-example-token"))
server.SetMetrics(myMetrics{})

Serve a quic-go listener:

listener, err := quic.ListenAddr("127.0.0.1:50051", tlsConfig, &quic.Config{})
if err != nil {
	log.Fatal(err)
}
defer listener.Close()

if err := trevrpc.ServeQUIC(context.Background(), listener, server); err != nil {
	log.Fatal(err)
}

TLS, certificates, client identity, and QUIC transport configuration remain the application's responsibility. Set NextProtos: []string{trevrpc.ALPN} in the TLS config.

To serve ordinary HTTP/3, set ServerOptions.EnableHTTP3 = true, advertise HTTP/3 ALPN, and pass server.Options() through QUICServerConfig. To also serve WebTransport, set EnableWebTransport, install a non-nil WebTransportAdmission, and use the same listener. See HTTP/3 and WebTransport.

Build a Client

channel, err := trevrpc.Dial(
	ctx,
	"127.0.0.1:50051",
	trevrpc.DialOptions{TLSConfig: tlsConfig},
)
if err != nil {
	log.Fatal(err)
}
defer channel.Close()

client := greeter.NewGreeterClient(
	channel,
	trevrpc.WithTimeout(5*time.Second),
	trevrpc.WithMetadata("authorization", []byte("Bearer trevrpc-example-token")),
)

response, err := client.SayHello(ctx, &greeter.HelloRequest{Name: "TrevRPC"})

trevrpc.Dial returns a long-lived *trevrpc.Channel. An address target uses native QUIC; an https:// target uses WebTransport. Every RPC snapshots one ready generation. If it dies, in-flight calls fail without retry, replay, resumption, or movement to another connection. New calls fail immediately with Unavailable while reconnecting; call channel.WaitUntilReady(ctx) before issuing work when waiting is intentional.

Routine channels use fixed reconnect defaults: 100 ms initial delay, multiplier 2, 20 percent jitter, and a 30 second cap. DialOptions does not expose reconnect-policy tuning.

For advanced integrations, benchmarks, diagnostics, or deterministic tests that explicitly own one established connection, use the raw transport:

conn, err := quic.DialAddr(ctx, "127.0.0.1:50051", tlsConfig, &quic.Config{})
if err != nil {
	log.Fatal(err)
}
defer conn.CloseWithError(0, "client done")

client := greeter.NewGreeterClient(trevrpc.Advanced.NewRawQUICClient(conn))

Client-streaming and bidirectional-streaming client methods return call objects:

greetings, err := client.LotsOfGreetings(ctx)
if err != nil {
	log.Fatal(err)
}
greetings.Send(&greeter.HelloRequest{Name: "first"})
greetings.Send(&greeter.HelloRequest{Name: "second"})
summary, err := greetings.CloseAndRecv()

bidi, err := client.BidiHello(ctx)
if err != nil {
	log.Fatal(err)
}
bidi.Send(&greeter.HelloRequest{Name: "first"})
reply, err := bidi.Recv()
bidi.CloseSend()

Generated clients also expose FromStream helpers for fixed or prebuilt request streams:

summary, err := client.LotsOfGreetingsFromStream(
	ctx,
	trevrpc.FromSlice(
		&greeter.HelloRequest{Name: "first"},
		&greeter.HelloRequest{Name: "second"},
	),
)

For routine WebTransport, pass the HTTPS URL directly to Dial:

channel, err := trevrpc.Dial(
	ctx,
	"https://127.0.0.1:50051/trevrpc",
	trevrpc.DialOptions{TLSConfig: tlsConfig},
)

Advanced APIs are grouped under the trevrpc.Advanced namespace value. NewRawQUICClient, DialRawWebTransport, and NewRawWebTransportClient create raw single-connection transports. trevrpc.Advanced.Channel(channel).AddPath operates on the current native QUIC generation, and RawWebTransportSession exposes the current WebTransport session. Raw transports do not reconnect.

Where TrevRPC controls QUIC setup, 0-RTT application data is disabled. TLS resumption may shorten a reconnect handshake but never changes RPC semantics.

Use per-call options to override client defaults:

response, err := client.SayHello(
	ctx,
	&greeter.HelloRequest{Name: "TrevRPC"},
	trevrpc.WithMetadata("authorization", []byte("Bearer trevrpc-example-token")),
)

Client Options

trevrpc.CallOption controls per-call behavior.

Option Default
Timeout None
Max unary response body 4 MiB
Max response messages 4096
Max cumulative response stream body 16 MiB
Stream idle timeout 30 seconds
Metadata Empty

Important option helpers:

  • WithTimeout and WithoutTimeout.
  • WithMaxResponseBodySize.
  • WithMaxResponseMessages and WithoutMaxResponseMessages.
  • WithMaxResponseStreamBodySize and WithoutMaxResponseStreamBodySize.
  • WithStreamIdleTimeout and WithoutStreamIdleTimeout.
  • WithMetadata and WithMetadataMap.

Generated clients merge constructor-level options first, then method-level options.

Server Options

trevrpc.ServerOptions controls server limits.

Option Default
Max frame size 4 MiB
Max concurrent connections 256
Max concurrent streams per connection 64
Max concurrent requests 1024
Graceful shutdown timeout 30 seconds
Initial request timeout 10 seconds
Max stream messages 4096
Max cumulative stream body 16 MiB
Stream idle timeout 30 seconds
Ordinary HTTP/3 enabled false
HTTP/3 path /trevrpc
HTTP/3 admission Nil; otherwise-valid requests accepted
WebTransport enabled false
WebTransport admission Nil; WebTransport sessions are rejected

Set options with server.SetOptions(options).

Streams

Go streams implement:

type MessageStream[T any] interface {
	Recv() (T, error)
	Close() error
}

Generated client-streaming and bidirectional-streaming clients return call objects with Send; client-streaming calls finish with CloseAndRecv, and bidirectional calls use Recv plus CloseSend. For fixed request streams, generated FromStream helpers avoid the interactive send path.

Helpful constructors and adapters:

  • EmptyStream[T]() creates an empty stream.
  • FromSlice(values...) creates a stream from in-memory values.
  • SingleMessageStream(value) creates a one-message stream.
  • EncodeStream(stream) converts protobuf messages into byte bodies.
  • DecodeStream(stream, newMessage) converts byte bodies into protobuf messages.
  • StatusStream(status) creates a stream that returns one status error.

Statuses and Errors

Go uses *trevrpc.Status, which implements error. Status codes match canonical gRPC numeric values.

Use constructors such as:

  • trevrpc.InvalidArgument("message").
  • trevrpc.DeadlineExceeded("message").
  • trevrpc.ResourceExhausted("message").
  • trevrpc.Unauthenticated("message").
  • trevrpc.Unimplemented("message").

trevrpc.StatusFromError preserves *trevrpc.Status, maps frame-too-large errors to ResourceExhausted, and maps generic errors to Internal.

Observability

Server metrics use the trevrpc.Metrics interface:

type Metrics interface {
	RPCStarted(RPCStarted)
	RPCFinished(RPCFinished)
}

Metrics callbacks run inline on the RPC task and should not block.