rerpc

package module
v0.0.0-...-d9c8795 Latest Latest
Warning

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

Go to latest
Published: Sep 2, 2021 License: MIT Imports: 26 Imported by: 0

README

reRPC

Build Report Card GoDoc

reRPC is a small framework for building HTTP APIs. You write a short API definition file and implement your application logic, and reRPC generates code to handle marshaling, routing, error handling, and content-type negotiation. It also generates an idiomatic, type-safe client.

reRPC is wire-compatible with both the gRPC and Twirp protocols, including full support for gRPC streaming. reRPC servers interoperate seamlessly with generated clients in more than a dozen languages, command-line tools like grpcurl, and proxies like Envoy and gRPC-Gateway. Thanks to Twirp's simple, human-readable JSON protocol, reRPC servers are also easy to debug with cURL.

Under the hood, reRPC is just protocol buffers and the standard library: no custom HTTP implementation, no new name resolution or load balancing APIs, and no surprises. Everything you already know about net/http still applies, and any package that works with an http.Server, http.Client, or http.Handler also works with reRPC.

For more on reRPC, including a walkthrough and a comparison to alternatives, see the docs.

A Small Example

Curious what all this looks like in practice? Here's a small h2c server:

package main

import (
  "net/http"

  "golang.org/x/net/http2"
  "golang.org/x/net/http2/h2c"

  pingpb "github.com/rerpc/rerpc/internal/ping/v1test" // generated
)

type PingServer struct {
  pingpb.UnimplementedPingServiceReRPC // returns errors from all methods
}

func main() {
  ping := &PingServer{}
  mux := rerpc.NewServeMux(
    pingpb.NewPingHandlerReRPC(ping),
    rerpc.NewBadRouteHandler(),
  )
  handler := h2c.NewHandler(mux, &http2.Server{})
  http.ListenAndServe(":8081", handler)
}

With that server running, you can make requests with any gRPC client or with cURL:

$ curl --request POST \
  --header "Content-Type: application/json" \
  http://localhost:8081/internal.ping.v1test.PingService/Ping

{"code":"unimplemented","msg":"internal.ping.v1test.PingService.Ping isn't implemented"}

You can find production-ready examples of servers and clients in the API documentation.

Status

This is the earliest of early alphas: APIs will break before the first stable release.

Support and Versioning

reRPC supports:

Within those parameters, reRPC follows semantic versioning.

Offered under the MIT license. This is a personal project developed in my spare time - it's not endorsed by, supported by, or (as far as I know) used by my current or former employers.

Documentation

Overview

Package rerpc is a small RPC framework built on protocol buffers and net/http. It's wire-compatible with gRPC and Twirp.

This documentation is intended to explain each type and function in isolation. For walkthroughs, comparisons to grpc-go and Twirp, and other narrative docs, see https://rerpc.github.io.

Example
package main

import (
	"context"
	"net/http"
	"time"

	"github.com/rerpc/rerpc"
	"github.com/rerpc/rerpc/health"
	pingpb "github.com/rerpc/rerpc/internal/ping/v1test"
	"github.com/rerpc/rerpc/reflection"
)

// ExamplePingServer implements some trivial business logic. The protobuf
// definition for this API is in internal/ping/v1test/ping.proto.
type ExamplePingServer struct {
	pingpb.UnimplementedPingServiceReRPC
}

// Ping implements pingpb.PingServiceReRPC.
func (*ExamplePingServer) Ping(ctx context.Context, req *pingpb.PingRequest) (*pingpb.PingResponse, error) {
	return &pingpb.PingResponse{Number: req.Number, Msg: req.Msg}, nil
}

func main() {
	// The business logic here is trivial, but the rest of the example is meant
	// to be somewhat realistic. This server has basic timeouts configured, and
	// it also exposes gRPC's server reflection and health check APIs.
	ping := &ExamplePingServer{}             // our business logic
	reg := rerpc.NewRegistrar()              // for gRPC reflection
	checker := health.NewChecker(reg)        // basic health checks
	limit := rerpc.ReadMaxBytes(1024 * 1024) // limit request size

	// NewServeMux returns a plain net/http *ServeMux. Since a mux is an
	// http.Handler, reRPC works with any Go HTTP middleware (e.g., net/http's
	// StripPrefix).
	mux := rerpc.NewServeMux(
		pingpb.NewPingServiceHandlerReRPC(ping, reg, limit), // business logic
		reflection.NewHandler(reg),                          // server reflection
		health.NewHandler(checker),                          // health checks
		rerpc.NewBadRouteHandler(),                          // Twirp-compatible 404s
	)

	// Timeouts, connection handling, TLS configuration, and other low-level
	// transport details are handled by net/http. Everything you already know (or
	// anything you learn) about hardening net/http Servers applies to reRPC
	// too. Keep in mind that any timeouts you set will also apply to streaming
	// RPCs!
	//
	// If you're not familiar with the many timeouts exposed by net/http, start with
	// https://blog.cloudflare.com/the-complete-guide-to-golang-net-http-timeouts/.
	srv := &http.Server{
		Addr:           ":http",
		Handler:        mux,
		ReadTimeout:    2500 * time.Millisecond,
		WriteTimeout:   5 * time.Second,
		MaxHeaderBytes: rerpc.MaxHeaderBytes,
	}
	// You could also use golang.org/x/net/http2/h2c to serve gRPC requests
	// without TLS.
	srv.ListenAndServeTLS("testdata/server.crt", "testdata/server.key")
}
Output:

Index

Examples

Constants

View Source
const (
	TypeDefaultGRPC = "application/grpc"
	TypeProtoGRPC   = "application/grpc+proto"
	TypeProtoTwirp  = "application/protobuf"
	TypeJSON        = "application/json"
)

ReRPC's supported HTTP Content-Types. Servers decide whether to use the gRPC or Twirp protocol based on the request's Content-Type. See the protocol documentation at https://rerpc.github.io for more information.

View Source
const (
	CompressionIdentity = "identity"
	CompressionGzip     = "gzip"
)

ReRPC's supported compression methods.

View Source
const (
	StreamTypeUnary         StreamType = 0b00
	StreamTypeClient                   = 0b01
	StreamTypeServer                   = 0b10
	StreamTypeBidirectional            = StreamTypeClient | StreamTypeServer
)
View Source
const MaxHeaderBytes = 1024 * 8

MaxHeaderBytes is 8KiB, gRPC's recommended maximum header size. To enforce this limit, set MaxHeaderBytes on your http.Server.

View Source
const (
	SupportsCodeGenV0 = iota
)

These constants are used in compile-time handshakes with reRPC's generated code.

View Source
const Version = "0.0.1"

Version is the semantic version of the reRPC module.

Variables

This section is empty.

Functions

func Errorf

func Errorf(c Code, template string, args ...interface{}) error

Errorf calls fmt.Errorf with the supplied template and arguments, then wraps the resulting error. If the code is CodeOK, the returned error is nil. Otherwise, the returned error will be an *Error.

func IsValidHeaderKey

func IsValidHeaderKey(key string) error

IsValidHeaderKey checks whether the supplied key is reserved for use by reRPC, gRPC, or Twirp. Keys are canonicalized using textproto.CanonicalMIMEHeaderKey before checking. Unreserved headers are available for use by applications, but exercise caution: setting widely-used HTTP headers (e.g., Transfer-Encoding, Content-Length) may break your application in unexpected and difficult-to-debug ways.

The signature of IsValidHeaderKey obeys semantic versioning, but the list of reserved headers may expand in minor releases to keep up with the evolution of the gRPC and Twirp protocols. To minimize the chance of breakage, applications should namespace their headers with a consistent prefix (e.g., "Google-Cloud-").

Currently, the following keys are reserved: Accept, Accept-Encoding, Accept-Post, Allow, Content-Encoding, Content-Type, Te, and Trailer. Empty keys, or keys prefixed with ":", "Grpc-", "Rerpc-", and "Twirp-" are also reserved.

Unreserved keys may only contain the following ASCII characters: a-z, A-Z, 0-9, "-" (hyphen-minus), "_" (underscore), and "." (period).

func IsValidHeaderValue

func IsValidHeaderValue(v string) error

IsValidHeaderValue checks whether the supplied string is a valid header value. The gRPC wire protocol is more restrictive than plain HTTP, so only space and printable ASCII is allowed.

func NewCallContext

func NewCallContext(ctx context.Context, spec Specification, req, res http.Header) context.Context

NewCallContext constructs a Metadata and attaches it to the supplied context. It's useful in tests that rely on CallMetadata.

func NewHandlerContext

func NewHandlerContext(ctx context.Context, spec Specification, req, res http.Header) context.Context

NewHandlerContext constructs a HandlerMetadata and attaches it to the supplied context. It's useful in tests that call HandlerMeta.

func NewServeMux

func NewServeMux(services ...[]*Handler) *http.ServeMux

NewServeMux mounts reRPC handlers on a mux. The signature is designed to work with with reRPC's generated code, which models each protobuf service as a slice of *Handlers.

func WithoutMetadata

func WithoutMetadata(ctx context.Context) context.Context

WithoutMetadata strips any Metadata from the context.

func Wrap

func Wrap(c Code, err error, details ...proto.Message) error

Wrap annotates any error with a status code and error details. If the code is CodeOK, the returned error is nil. Otherwise, the returned error will be an *Error.

Types

type CallOption

type CallOption interface {
	// contains filtered or unexported methods
}

A CallOption configures a reRPC client or a single call.

In addition to any options grouped in the documentation below, remember that Options are also valid CallOptions.

type Chain

type Chain struct {
	// contains filtered or unexported fields
}

A Chain composes multiple interceptors into one.

Example
outer := rerpc.UnaryInterceptorFunc(func(next rerpc.Func) rerpc.Func {
	return rerpc.Func(func(ctx context.Context, req interface{}) (interface{}, error) {
		fmt.Println("outer interceptor: before call")
		res, err := next(ctx, req)
		fmt.Println("outer interceptor: after call")
		return res, err
	})
})
inner := rerpc.UnaryInterceptorFunc(func(next rerpc.Func) rerpc.Func {
	return rerpc.Func(func(ctx context.Context, req interface{}) (interface{}, error) {
		fmt.Println("inner interceptor: before call")
		res, err := next(ctx, req)
		fmt.Println("inner interceptor: after call")
		return res, err
	})
})
// This interceptor prevents the client from making network requests in
// examples. Leave it out in real code!
short := ShortCircuit(rerpc.Errorf(rerpc.CodeUnimplemented, "no networking in examples"))
client := pingpb.NewPingServiceClientReRPC(
	"https://invalid-test-url",
	http.DefaultClient,
	rerpc.Intercept(rerpc.NewChain(outer, inner, short)),
)
client.Ping(context.Background(), &pingpb.PingRequest{})
Output:

outer interceptor: before call
inner interceptor: before call
inner interceptor: after call
outer interceptor: after call

func NewChain

func NewChain(interceptors ...Interceptor) *Chain

NewChain composes multiple interceptors into one. The first interceptor provided is the outermost layer of the onion: it acts first on the context and request, and last on the response and error.

func (*Chain) Wrap

func (c *Chain) Wrap(next Func) Func

Wrap implements Interceptor.

func (*Chain) WrapStream

func (c *Chain) WrapStream(next StreamFunc) StreamFunc

WrapStream implements Interceptor.

type Code

type Code uint32

A Code is one of gRPC's canonical status codes. There are no user-defined codes, so only the codes enumerated below are valid.

See the specification at https://github.com/grpc/grpc/blob/master/doc/statuscodes.md for detailed descriptions of each code and example usage.

const (
	CodeOK                 Code = 0  // success
	CodeCanceled           Code = 1  // canceled, usually by the user
	CodeUnknown            Code = 2  // unknown error
	CodeInvalidArgument    Code = 3  // argument invalid regardless of system state
	CodeDeadlineExceeded   Code = 4  // operation expired, may or may not have completed
	CodeNotFound           Code = 5  // entity not found
	CodeAlreadyExists      Code = 6  // entity already exists
	CodePermissionDenied   Code = 7  // operation not authorized
	CodeResourceExhausted  Code = 8  // quota exhausted
	CodeFailedPrecondition Code = 9  // argument invalid in current system state
	CodeAborted            Code = 10 // operation aborted
	CodeOutOfRange         Code = 11 // out of bounds, use instead of CodeFailedPrecondition
	CodeUnimplemented      Code = 12 // operation not implemented or disabled
	CodeInternal           Code = 13 // internal error, reserved for "serious errors"
	CodeUnavailable        Code = 14 // unavailable, client should back off and retry
	CodeDataLoss           Code = 15 // unrecoverable data loss or corruption
	CodeUnauthenticated    Code = 16 // request isn't authenticated

)

func CodeOf

func CodeOf(err error) Code

CodeOf returns the error's status code if it is or wraps a *rerpc.Error, CodeOK if the error is nil, and CodeUnknown otherwise.

func (Code) MarshalText

func (c Code) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler. Codes are marshaled in their numeric representations.

func (Code) String

func (c Code) String() string

func (*Code) UnmarshalText

func (c *Code) UnmarshalText(b []byte) error

UnmarshalText implements encoding.TextUnmarshaler. It accepts both numeric representations (as produced by MarshalText) and the all-caps strings from the gRPC specification. Note that the specification uses the British "CANCELLED" for CodeCanceled.

type Doer

type Doer interface {
	Do(*http.Request) (*http.Response, error)
}

Doer is the transport-level interface reRPC expects HTTP clients to implement. The standard library's http.Client implements Doer.

type Error

type Error struct {
	// contains filtered or unexported fields
}

An Error captures three pieces of information: a Code, a human-readable message, and an optional collection of arbitrary protobuf messages called "details" (more on those below). Servers send the code, message, and details over the wire to clients. reRPC's Error wraps a standard Go error, using the underlying error's Error() string as the message. Take care not to leak sensitive information from public APIs!

Protobuf service implementations and Interceptors should return Errors (using the Wrap or Errorf functions) rather than plain Go errors. If service implementations or Interceptors instead return a plain Go error, reRPC will use AsError to find an Error to send over the wire. If no Error can be found, reRPC will use CodeUnknown and the returned error's message.

Error codes and messages are explained in the gRPC documentation linked below. Unfortunately, error details were introduced before gRPC adopted a formal proposal process, so they're not clearly documented anywhere and may differ slightly between implementations. Roughly, they're an optional mechanism for servers, middleware, and proxies to send strongly-typed errors and localized messages to clients. Error details aren't exposed over the Twirp protocol.

See https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md and https://github.com/grpc/grpc/blob/master/doc/statuscodes.md for further details.

func AsError

func AsError(err error) (*Error, bool)

AsError uses errors.As to unwrap any error and look for a reRPC *Error.

func (*Error) AddDetail

func (e *Error) AddDetail(m proto.Message) error

AddDetail appends a message to the error's details.

func (*Error) Code

func (e *Error) Code() Code

Code returns the error's status code.

func (*Error) Details

func (e *Error) Details() []*anypb.Any

Details returns a deep copy of the error's details.

func (*Error) Error

func (e *Error) Error() string

func (*Error) SetDetails

func (e *Error) SetDetails(details ...proto.Message) error

SetDetails overwrites the error's details.

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap implements errors.Wrapper, which allows errors.Is and errors.As access to the underlying error.

type Func

type Func func(context.Context, interface{}) (interface{}, error)

Func is the generic signature of a unary RPC. Interceptors wrap Funcs.

The type of the request and response struct depend on the codec being used. When using protobuf, they'll always be proto.Message implementations.

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

A Handler is the server-side implementation of a single RPC defined by a protocol buffer service. It's the interface between the reRPC library and the code generated by the reRPC protoc plugin; most users won't ever need to deal with it directly.

To see an example of how Handler is used in the generated code, see the internal/ping/v1test package.

func NewBadRouteHandler

func NewBadRouteHandler(opts ...HandlerOption) []*Handler

NewBadRouteHandler always returns gRPC and Twirp's equivalent of the standard library's http.StatusNotFound. To be fully compatible with the Twirp specification, include this in your call to NewServeMux (so that it handles any requests for invalid protobuf methods).

func NewHandler

func NewHandler(
	stype StreamType,
	pkg, service, method string,
	implementation func(context.Context, StreamFunc),
	opts ...HandlerOption,
) *Handler

NewHandler constructs a Handler. The supplied package, service, and method names must be protobuf identifiers. For example, a handler for the URL "/acme.foo.v1.FooService/Bar" would have package "acme.foo.v1", service "FooService", and method "Bar".

Remember that NewHandler is usually called from generated code - most users won't need to deal with protobuf identifiers directly.

func (*Handler) Path

func (h *Handler) Path() string

Path returns the URL pattern to use when registering this handler. It's used by the generated code.

func (*Handler) ServeHTTP

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements http.Handler.

type HandlerOption

type HandlerOption interface {
	// contains filtered or unexported methods
}

A HandlerOption configures a Handler.

In addition to any options grouped in the documentation below, remember that Registrars and Options are also valid HandlerOptions.

func ServeTwirp

func ServeTwirp(enable bool) HandlerOption

ServeTwirp enables or disables support for Twirp's JSON and protobuf formats. Disable Twirp if you only want your handlers to speak the gRPC protocol.

By default, handlers support Twirp.

type Header struct {
	// contains filtered or unexported fields
}

Header provides access to HTTP headers and trailers. It's very similar to net/http's Header, but automatically validates with IsValidHeaderKey and IsValidHeaderValue.

The zero value of Header is safe to use.

func NewHeader

func NewHeader(raw http.Header) Header

NewHeader wraps an http.Header in reRPC's validation logic. Keys and values added directly to the http.Header aren't validated.

func (Header) Add

func (h Header) Add(key, value string) error

Add a key-value pair to the header, appending to any existing values associated with the key. Like the standard library's http.Header, keys are case-insensitive and canonicalized with textproto.CanonicalMIMEHeaderKey.

Attempting to add to an invalid key (as defined by IsValidHeaderKey) or supplying an invalid value (as defined by IsValidHeaderValue) returns an error. See IsValidHeaderKey for backward compatibility guarantees.

func (Header) Clone

func (h Header) Clone() http.Header

Clone returns a copy of the underlying HTTP headers, including all reserved keys.

func (Header) Del

func (h Header) Del(key string) error

Del deletes all values associated with the key. Like the standard library's http.Header, keys are case-insensitive and canonicalized with textproto.CanonicalMIMEHeaderKey.

Attempting delete an invalid key (as defined by IsValidHeaderKey) returns an error. See IsValidHeaderKey for backward compatibility guarantees.

func (Header) Get

func (h Header) Get(key string) string

Get returns the first value associated with the given key. Like the standard library's http.Header, keys are case-insensitive and canonicalized with textproto.CanonicalMIMEHeaderKey.

func (Header) GetBinary

func (h Header) GetBinary(key string) ([]byte, error)

GetBinary is similar to Get, but for binary values encoded according to the gRPC specification. Briefly, binary headers have keys ending in "-Bin" and base64-encoded values. GetBinary automatically appends the "-Bin" suffix to the supplied key and base64-decodes the value.

For details on gRPC's treatment of binary headers, see https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md.

func (Header) Set

func (h Header) Set(key, value string) error

Set the value associated with the given key, overwriting any existing values. Like the standard library's http.Header, keys are case-insensitive and canonicalized with textproto.CanonicalMIMEHeaderKey.

Attempting to set an invalid key (as defined by IsValidHeaderKey) or value (as defined by IsValidHeaderValue) returns an error. See IsValidHeaderKey for backward compatibility guarantees.

func (Header) SetBinary

func (h Header) SetBinary(key string, value []byte) error

SetBinary is similar to Set, but for binary values encoded according to the gRPC specification. Briefly, binary headers have keys ending in "-Bin" and base64-encoded values. Like grpc-go, SetBinary automatically appends the "-Bin" suffix to the supplied key and base64-encodes the value.

For details on gRPC's treatment of binary headers, see https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md.

func (Header) Values

func (h Header) Values(key string) []string

Values returns all values associated with the given key. Like the standard library's http.Header, keys are case-insensitive and canonicalized with textproto.CanonicalMIMEHeaderKey.

Unlike the standard library's http.Header.Values, the returned slice is a copy.

type Interceptor

type Interceptor interface {
	Wrap(Func) Func
	WrapStream(StreamFunc) StreamFunc
}

An Interceptor adds logic to a generated handler or client, like the decorators or middleware you may have seen in other libraries. Interceptors may replace the context, mutate the request, mutate the response, handle the returned error, retry, recover from panics, emit logs and metrics, or do nearly anything else.

The functions returned by Wrap and WrapStream must be safe to call concurrently. If chained carelessly, the interceptor's logic may run more than once - where possible, interceptors should be idempotent.

See Chain for an example of interceptor use.

func ConfiguredCallInterceptor

func ConfiguredCallInterceptor(opts []CallOption) Interceptor

ConfiguredCallInterceptor returns the Interceptor configured by a collection of call options (if any). It's used in generated code.

func ConfiguredHandlerInterceptor

func ConfiguredHandlerInterceptor(opts []HandlerOption) Interceptor

ConfiguredHandlerInterceptor returns the Interceptor configured by a collection of handler options (if any). It's used in generated code.

type Metadata

type Metadata struct {
	Spec Specification
	// contains filtered or unexported fields
}

Metadata provides a Specification and access to request and response headers for an in-progress client call or handler invocation.

func CallMetadata

func CallMetadata(ctx context.Context) (Metadata, bool)

CallMetadata retrieves Metadata from the supplied context. It only succeeds in client calls - in other settings, the returned bool will be false. If you're writing an Interceptor that uses different logic for servers and clients, you can use CallMetadata to check which logic to apply.

To test interceptors that use CallMetadata, pass them a context constructed by NewCallContext.

Example
logger := rerpc.UnaryInterceptorFunc(func(next rerpc.Func) rerpc.Func {
	return rerpc.Func(func(ctx context.Context, req interface{}) (interface{}, error) {
		if md, ok := rerpc.CallMetadata(ctx); ok {
			fmt.Println("calling", md.Spec.Method)
		}
		return next(ctx, req)
	})
})
// This interceptor prevents the client from making network requests in
// examples. Leave it out in real code!
short := ShortCircuit(rerpc.Errorf(rerpc.CodeUnimplemented, "no networking in examples"))
client := pingpb.NewPingServiceClientReRPC(
	"https://invalid-test-url",
	http.DefaultClient,
	rerpc.Intercept(rerpc.NewChain(logger, short)),
)
client.Ping(context.Background(), &pingpb.PingRequest{})
Output:

calling Ping

func HandlerMetadata

func HandlerMetadata(ctx context.Context) (Metadata, bool)

HandlerMetadata retrieves Metadata from the supplied context. It only succeeds in handler invocations (including protobuf service implementations) - in other settings, the returned bool will be false. If you're writing an Interceptor that uses different logic for servers and clients, you can use HandlerMetadata to check which logic to apply.

To test interceptors and service implementations that use HandlerMetadata, pass them a context constructed by NewHandlerContext.

func (Metadata) Request

func (m Metadata) Request() Header

Request returns the request headers.

func (Metadata) Response

func (m Metadata) Response() Header

Response returns the response headers. In client-side Interceptors, the response isn't populated until the request is sent to the server.

type Option

type Option interface {
	CallOption
	HandlerOption
}

Option implements both CallOption and HandlerOption, so it can be applied both client-side and server-side.

func Gzip

func Gzip(enable bool) Option

Gzip configures client and server compression strategies.

For handlers, enabling gzip compresses responses where it's likely to improve overall performance. By default, handlers use gzip if the client supports it, the uncompressed response message is >1 KiB, and the message is likely to compress well. Disabling gzip support instructs handlers to always send uncompressed responses.

For clients, enabling gzip compresses requests where it's likely to improve performance (using the same criteria as handlers). gRPC's compression negotiation is complex, but most first-party gRPC servers won't compress responses unless the client enables this option. Since not all servers support gzip compression, clients default to sending uncompressed requests.

func Intercept

func Intercept(interceptor Interceptor) Option

Intercept configures a client or handler to use the supplied Interceptor. Note that this Option replaces any previously-configured Interceptor - to compose Interceptors, use a Chain.

func OverrideProtobufPackage

func OverrideProtobufPackage(pkg string) Option

OverrideProtobufPackage replaces the protobuf package name set by the generated code. This affects URLs and any Specification retrieved from a call or handler context. Using this option is usually a bad idea, but it's occasionally necessary to prevent protobuf package collisions. (For example, reRPC uses this option to serve the health and reflection APIs without generating runtime conflicts with grpc-go.)

OverrideProtobufPackage does not change the data exposed by the reflection API. To prevent inconsistencies between the reflection data and the actual service URL, using this option disables reflection for the overridden service (though other services can still be introspected).

func ReadMaxBytes

func ReadMaxBytes(n int64) Option

ReadMaxBytes limits the performance impact of pathologically large messages sent by the other party. For handlers, ReadMaxBytes limits the size of message that the client can send. For clients, ReadMaxBytes limits the size of message that the server can respond with. Limits are applied before decompression and apply to each protobuf message, not to the stream as a whole.

Setting ReadMaxBytes to zero allows any message size. Both clients and handlers default to allowing any request size.

type Registrar

type Registrar struct {
	// contains filtered or unexported fields
}

A Registrar collects information to support gRPC server reflection when building handlers. Registrars are valid HandlerOptions.

func NewRegistrar

func NewRegistrar() *Registrar

NewRegistrar constructs an empty Registrar.

func (*Registrar) IsRegistered

func (r *Registrar) IsRegistered(service string) bool

IsRegistered checks whether a fully-qualified protobuf service name is registered. It's safe to call concurrently.

func (*Registrar) Services

func (r *Registrar) Services() []string

Services returns the fully-qualified names of the registered protobuf services. The returned slice is a copy, so it's safe for callers to modify. This method is safe to call concurrently.

type Specification

type Specification struct {
	Type    StreamType
	Package string // protobuf name, e.g. "acme.foo.v1"
	Service string // protobuf name, e.g. "FooService"
	Method  string // protobuf name, e.g. "Bar"

	Path                string
	ContentType         string
	RequestCompression  string
	ResponseCompression string
	ReadMaxBytes        int64
}

Specification is a description of a client call or a handler invocation.

Note that the Method, Service, and Package are protobuf names, not Go import paths or identifiers.

type Stream

type Stream interface {
	// Implementations must ensure that Context is safe to call concurrently. It
	// must not race with any other methods.
	Context() context.Context

	// Implementations must ensure that Send and CloseSend don't race with
	// Context, Receive, or CloseReceive. They may race with each other.
	Send(interface{}) error
	CloseSend(error) error

	// Implementations must ensure that Receive and CloseReceive don't race with
	// Context, Send, or CloseSend. They may race with each other.
	Receive(interface{}) error
	CloseReceive() error
}

Stream is a bidirectional stream of protobuf messages.

Stream implementations must support a limited form of concurrency: one goroutine may call Send and CloseSend, and another may call Receive and CloseReceive. Either goroutine may call Context.

type StreamFunc

type StreamFunc func(context.Context) Stream

StreamFunc is the generic signature of a streaming RPC. Interceptors wrap StreamFuncs.

func NewCall

func NewCall(
	ctx context.Context,
	doer Doer,
	stype StreamType,
	baseURL, pkg, service, method string,
	opts ...CallOption,
) (context.Context, StreamFunc)

NewCall returns the context and StreamFunc required to call a remote procedure. It's the interface between the reRPC library and the client code generated by protoc-gen-go-rerpc; most users won't ever need to deal with it directly.

To see an example of how NewCall is used in the generated code, see the internal/ping/v1test package.

type StreamType

type StreamType uint8

StreamType describes whether the client, server, neither, or both is streaming.

type UnaryInterceptorFunc

type UnaryInterceptorFunc func(Func) Func

A UnaryInterceptorFunc is a simple Interceptor implementation that only wraps unary RPCs. It has no effect on client, server, or bidirectional streaming RPCs. See CallMetadata for an example.

func (UnaryInterceptorFunc) Wrap

func (f UnaryInterceptorFunc) Wrap(next Func) Func

Wrap implements Interceptor by applying the interceptor function.

func (UnaryInterceptorFunc) WrapStream

func (f UnaryInterceptorFunc) WrapStream(next StreamFunc) StreamFunc

WrapStream implements Interceptor with a no-op.

Directories

Path Synopsis
cmd
protoc-gen-go-rerpc
protoc-gen-go-rerpc is a plugin for the protocol buffer compiler that generates Go code.
protoc-gen-go-rerpc is a plugin for the protocol buffer compiler that generates Go code.
Package health offers support for gRPC's health-checking APIs.
Package health offers support for gRPC's health-checking APIs.
internal
Package reflection offers support for gRPC's server reflection API.
Package reflection offers support for gRPC's server reflection API.
Package rerpctest contains testing utilities for reRPC, including a replacement for httptest.Server.
Package rerpctest contains testing utilities for reRPC, including a replacement for httptest.Server.

Jump to

Keyboard shortcuts

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