transcoding

package
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Apr 16, 2024 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

Package transcoding implements standard methods for transcoding between non-gRPC and gRPC request and response messages. At this time, standard HTTP-to-gRPC transcoding specified in google/api/http.proto is implemented via StandardTranscoder with support for additional improvements, such as binding request and response bodies to deeply-nested fields, as well as a few different marshaler implementations: JSONMarshaler.

This package's interfaces act as adapters for easier development of standard web-based gRPC bridging handlers, which receive an incoming HTTP request with various path and query parameters, and then receive one or more messages, either over the HTTP body stream, or through other means, such as WebSocket messages after an upgrade.

Index

Constants

This section is empty.

Variables

View Source
var DefaultJSONMarshaler = &JSONMarshaler{

	MarshalOptions:   protojson.MarshalOptions{EmitDefaultValues: true},
	UnmarshalOptions: protojson.UnmarshalOptions{DiscardUnknown: true},
}

DefaultJSONMarshaler is used as one of the default marshalers in StandardTranscoderOpts, and can be used when it is needed to customize the list of marshalers without affecting the default ones. It uses the EmitDefaultValues and DiscardUnknown settings, replicating the default behaviour of gRPC-Gateway. This means that zero-values are included in the marshaled output, and unknown fields and enum values are discarded instead of returning an error.

Functions

This section is empty.

Types

type Decoder

type Decoder interface {
	Decode(protoreflect.Message, protoreflect.FieldDescriptor) error
}

Decoder is derived from a StreamMarshaler to be used for decoding messages from a stream. The decoder should handle any additional structural information in the stream placed by the encoder.

A Decoder shouldn't be expected to be concurrency-safe, because it is meant to be attached to a single stream/response.

type Encoder

type Encoder interface {
	Encode(protoreflect.Message, protoreflect.FieldDescriptor) error
}

Encoder is derived from a StreamMarshaler to be used for encoding messages to a stream. The encoder should place any additional structural information in the stream, such as a delimiter, so that the decoder of the same marshaler on the other side kind is able to decode the messages from the stream.

An Encoder shouldn't be expected to be concurrency-safe, because it is meant to be attached to a single stream/request.

type HTTPRequest

type HTTPRequest struct {
	Target  *bridgedesc.Target
	Service *bridgedesc.Service
	Method  *bridgedesc.Method
	Binding *bridgedesc.Binding

	RawRequest *http.Request
	PathParams map[string]string
}

HTTPRequest contains all the information an HTTPTranscoder could need to perform transcoding of the request and response messages associated with this HTTP request. No fields here should be nil, a transcoder might expect them all to be filled in.

type HTTPRequestTranscoder

type HTTPRequestTranscoder interface {
	Transcode([]byte, proto.Message) error
	ContentType(proto.Message) string
}

HTTPRequestTranscoder is responsible for transcoding request messages bound to a specific HTTP request.

Errors returned by Transcode should be gRPC status errors to differentiate between internal errors and invalid requests (by default errors should be interpreted as InvalidArgument), and they can additionally implement interface { HTTPStatus() int } to return a custom HTTP status code.

ContentType accepts a message since some requests might have dynamic content type mapping. By default, however, the StandardTranscoder doesn't support this, and always returns a static content type.

type HTTPResponseTranscoder

type HTTPResponseTranscoder interface {
	Transcode(proto.Message) ([]byte, error)
	ContentType(proto.Message) string
}

HTTPResponseTranscoder is responsible for transcoding response messages bound to a specific HTTP request.

Transcode should support not only the response message specified in the HTTPRequest from which this transcoder was bound, but also gRPC *google.golang.org/genproto/googleapis/rpc/status.Status messages, which are marshaled to return complete gRPC status codes along with the message and details.

Errors returned by Transcode can be gRPC status errors to set custom status info (by default errors should be interpreted as Internal), and they can additionally implement interface { HTTPStatus() int } to return a custom HTTP status code.

ContentType accepts a message since some responses might have dynamic content type mapping, such as google/api/httpbody.proto.

type HTTPTranscoder

type HTTPTranscoder interface {
	Bind(HTTPRequest) (HTTPRequestTranscoder, HTTPResponseTranscoder, error)
}

HTTPTranscoder is the interface required to be implemented by a transcoder suitable for transcoding requests originating from HTTP. The only method, Bind, should return two transcoders for transcoding the request and response messages, respectively, or an error, if no suitable transcoders for the request are available (e.g. the request specifies an unsupported Content-Type). Errors returned by Bind should preferrably be gRPC status errors, but they can additionally implement interface { HTTPStatus() int } to return a custom HTTP status code, such as 415 (UnsupportedMediaType).

type JSONMarshaler

type JSONMarshaler struct {
	protojson.MarshalOptions
	protojson.UnmarshalOptions
}

JSONMarshaler implements marshaling and unmarshaling of arbitrary protobuf message fields with custom type resolving, meant to be used for transcoding request and response messages from different targets. Marshaling/Unmarshaling is implemented as specified in the Proto3 JSON Mapping, and works with a few improvements compared to how it does in gRPC-Gateway:

  • It supports all kinds of float/double values as bodies, incluing "NaN", "Infinity", and "-Infinity", like specified in the official JSON Mapping;
  • Enum names can be used as bodies, compared to only the numeric representation supported by gRPC-Gateway.

func (*JSONMarshaler) ContentTypes

func (m *JSONMarshaler) ContentTypes() []string

ContentTypes returns a single-element slice containing "application/json".

func (*JSONMarshaler) Marshal

Marshal marshals the given protobuf message or field to JSON. If the field descriptor is nil, the whole message is marshaled, which equates to using protojson.Marshal.

func (*JSONMarshaler) NewDecoder

func (m *JSONMarshaler) NewDecoder(types bridgedesc.TypeResolver, r io.Reader) Decoder

NewDecoder initializes a new streaming JSON decoder which will use the specified type information for unmarshaling messages from the given writer.

func (*JSONMarshaler) NewEncoder

func (m *JSONMarshaler) NewEncoder(types bridgedesc.TypeResolver, w io.Writer) Encoder

NewEncoder initializes a new streaming JSON encoder which will use the specified type information for marshaling messages to the given writer.

func (*JSONMarshaler) Unmarshal

Unmarshal unmarshals the given JSON into a protobuf message or field. If the field descriptor is nil, the JSON is unmarshaled into the message itself, which equates to using protojson.Unmarshal. Unmarshal expects the message to be mutable, so nested messages should be retrieved by calling Mutable().

type Marshaler

type Marshaler interface {
	Marshal(bridgedesc.TypeResolver, protoreflect.Message, protoreflect.FieldDescriptor) ([]byte, error)
	Unmarshal(bridgedesc.TypeResolver, []byte, protoreflect.Message, protoreflect.FieldDescriptor) error
	// ContentTypes should return the MIME content types for which this marshaler can be used.
	ContentTypes() []string
}

Marshaler defines a converter interface for arbitrary protoreflect messages and their fields. At the minimum, a Marshaler should support converting single messages, but it can additionally support encoding and decoding to/from a stream by implementing StreamMarshaler, in which case it will be usable for message streams over protocols such as HTTP.

A Marshaler's methods must be concurrency-safe, since Marshal and Unmarshal on a single marshaler instance can be used for multiple concurrent requests.

type RequestStreamTranscoder

type RequestStreamTranscoder interface {
	Stream(io.Reader) TranscodedStream
}

RequestStreamTranscoder should be implemented by bound transcoders supporting receiving request messages over a binary stream, such as an HTTP request body.

type ResponseStreamTranscoder

type ResponseStreamTranscoder interface {
	Stream(io.Writer) TranscodedStream
}

ResponseStreamTranscoder should be implemented by bound transcoders supporting sending response messages over a binary stream, such as an HTTP response body.

type StandardTranscoder

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

StandardTranscoder is the standard HTTPTranscoder used by grpcbridge, implemented according to the standard HTTP-to-gRPC transcoding rules, specified in google/api/http.proto. Additionally to the specification, it supports binding request and response bodies to deeply-nested fields, not just the top-level ones.

It picks the marshaler to use for transcoding in StandardTranscoder.Bind using the Content-Type and Accept headers (for more information, see the documentation for Bind), which is how gRPC-Gateway functions, too.

If the picked marshaler supports streaming via StreamMarshaler, the returned transcoders will also support streaming. However, unlike gRPC-Gateway, the streaming transcoders will also support binding the original request's path and query parameters, which allows them to be used for cases like WebSockets without having to specify all the information in each message's body.

func NewStandardTranscoder

func NewStandardTranscoder(opts StandardTranscoderOpts) *StandardTranscoder

NewStandardTranscoder initializes a new StandardTranscoder with the specified options, which be used by it during Bind.

func (*StandardTranscoder) Bind

Bind constructs request and response message transcoders bound to a single specific HTTPRequest, and returns a status error of StatusUnsupportedMediaType if no marshalers were found for the specified Content-Type and Accept headers. If the request specified a Content-Type header, it will also be used as a default for the response message transcoder, unless a different MIME type is specified in the Accept header. However, if the request specified no Content-type, a default marshaler specified in StandardTranscoderOpts will be used. The returned transcoders' ContentType methods will return the value with which they have been matched.

If a chosen marshaler supports streaming via StreamMarshaler, the according transcoder will also implement the RequestStreamTranscoder and ResponseStreamTranscoder, which can be used to establish a transcoding stream if the request or response messages are to be streamed according to the method specification.

Bind expects *most* of the fields of HTTPRequest to be non-nil, for example, using the type resolver in HTTPRequest.Target for transcoding complex message types.

type StandardTranscoderOpts

type StandardTranscoderOpts struct {
	// Marshalers is the list of marshalers to use for transcoding.
	// If not set, the default marshaler list contains a [DefaultJSONMarshaler].
	Marshalers []Marshaler

	// Marshaler to use if the request does not specify a Content-Type header.
	// If not set, the default marshaler will be [DefaultJSONMarshaler].
	DefaultMarshaler Marshaler
}

StandardTranscoderOpts define all the optional settings which can be set for StandardTranscoder.

type StreamMarshaler

type StreamMarshaler interface {
	NewEncoder(bridgedesc.TypeResolver, io.Writer) Encoder
	NewDecoder(bridgedesc.TypeResolver, io.Reader) Decoder
}

StreamMarshaler defines additional methods for a Marshaler, which, if implemented, will allow encoding and decoding messages to/from a stream. StandardTranscoder.Bind checks if a Marshaler implements this interface, after which it can decide whether to return transcoders supporting only single-message encoding, or the streaming versions, RequestStreamTranscoder and ResponseStreamTranscoder.

type TranscodedStream

type TranscodedStream interface {
	Transcode(proto.Message) error
}

TranscodedStream is a stream of transcoded messages, which are being read or written to/from a stream. The same remarks apply to errors returned by the streaming Transcode as for HTTPRequestTranscoder/HTTPResponseTranscoder, but, additionally, a TranscodedStream wrapping an io.Reader should return an error satisifying errors.Is(err, io.EOF) when the stream has ended, unless a partial read occurs and the stream ends abruptly. In other words, a returned io.EOF indicates successful end of stream, and other errors should be used when that is not the case.

Jump to

Keyboard shortcuts

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