httpgrpc

package
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Dec 3, 2021 License: MIT Imports: 38 Imported by: 4

Documentation

Overview

Package httpgrpc contains code for using HTTP 1.1 for GRPC calls. This is intended only for environments where real GRPC is not possible or prohibitively expensive, like Google App Engine. It could possibly be used to perform GRPC operations from a browser, however no client implementation, other than a Go client, is provided.

For servers, RPC handlers will be invoked directly from an HTTP request, optionally transiting through a server interceptor. Importantly, this does not transform the request and then proxy it on loopback to the actual GRPC server. So GRPC service handlers are dispatched directly from HTTP server handlers.

Caveats

There are couple of limitations when using this package:

  1. True bidi streams are not supported. The best that can be done are half-duplex bidi streams, where the client uploads its entire streaming request and then the server can reply with a streaming response. Interleaved reading and writing does not work with HTTP 1.1. (Even if there were clients that supported it, the Go HTTP server APIS do not -- once a server handler starts writing to the response body, the request body is closed and no more messages can be read from it).
  2. Client-side interceptors that interact with the *grpc.ClientConn, such as examining connection states or querying static method configs, will not work. No GRPC client connection is actually established and HTTP 1.1 calls will supply a nil *grpc.ClientConn to any interceptor.

Note that for environments like Google App Engine, which do not support streaming, use of streaming RPCs may result in high latency and high memory usage as entire streams must be buffered in memory. Use streams judiciously when inter-operating with App Engine.

This package does not attempt to block use of full-duplex streaming. So if HTTP 1.1 is used to invoke a bidi streaming method, the RPC will almost certainly fail because the server's sending of headers and the first response message will immediately close the request side for reading. So later attempts to read a request message will fail.

Anatomy of GRPC-over-HTTP

A unary RPC is the simplest: the request will be a POST message and the request path will be the base URL's path (if any) plus "/service.name/method" (where service.name and method represent the fully-qualified proto service name and the method name for the unary method being invoked). Request metadata are used directly as HTTP request headers. The request payload is the binary-encoded form of the request proto, and the content-type is "application/x-protobuf". The response includes the best match for an HTTP status code based on the GRPC status code. But the response also includes a special response header, "X-GRPC-Status", that encodes the actual GRPC status code and message in a "code:message" string format. The response body is the binary-encoded form of the response proto, but will be empty when the GRPC status code is not "OK". If the RPC failed and the error includes details, they are attached via one or more headers named "X-GRPC-Details". If more than one error detail is associated with the status, there will be more than one header, and they will be added to the response in the same order as they appear in the server-side status. The value for the details header is a base64-encoding google.protobuf.Any message, which contains the error detail message. If the handler sends trailers, not just headers, they are encoded as HTTP 1.1 headers, but their names are prefixed with "X-GRPC-Trailer-". This allows clients to recover headers and trailers independently, as the server handler intended them.

Streaming RPCs are a bit more complex. Since the payloads can include multiple messages, the content type is not "application/x-protobuf". It is instead "application/x-httpgrpc-proto+v1". The actual request and response bodies consist of a sequence of length-delimited proto messages, each of which is binary encoded. The length delimiter is a 32-bit prefix that indicates the size of the subsequent message. Response sequences have a special final message that is encoded with a negative size (e.g. if the message size were 15, it would be written as -15 on the wire in the 32-bit prefix). The type of this special final message is always HttpTrailer, whereas the types of all other messages in the sequence are that of the method's request proto. The HttpTrailer final message indicates the final disposition of the stream (e.g. a GRPC status code and error details) as well as any trailing metadata. Because the status code is not encoded until the end of the response payload, the HTTP status code (which is the first line of the reply) will be 200 OK.

For clients that support streaming, client and server streams both work over HTTP 1.1. However, bidirectional streaming methods can only work if they are "half-duplex", where the client fully sends all request messages and then the server fully sends all response messages (e.g. the invocation timeline can have no interleaving/overlapping of request and response messages).

Index

Constants

View Source
const (
	UnaryRpcContentType_V1  = "application/x-protobuf"
	StreamRpcContentType_V1 = "application/x-httpgrpc-proto+v1"
)

These are the content-types used for "version 1" (hopefully the only version ever?) of the gRPC-over-HTTP transport

View Source
const (
	// Non-standard and experimental; uses the `jsonpb.Marshaler` by default.
	// Only unary calls are supported; streams with JSON encoding are not supported.
	// Use `encoding.RegisterCodec` to override the default encoder with a custom encoder.
	ApplicationJson = "application/json"
)

Variables

View Source
var File_httpgrpc_proto protoreflect.FileDescriptor

Functions

func DefaultErrorRenderer added in v1.1.0

func DefaultErrorRenderer(ctx context.Context, st *status.Status, w http.ResponseWriter)

DefaultErrorRenderer translates the gRPC code in the given status to an HTTP error response. The following table shows how status codes are translated:

Canceled:         * 502 Bad Gateway
Unknown:            500 Internal Server Error
InvalidArgument:    400 Bad Request
DeadlineExceeded: * 504 Gateway Timeout
NotFound:           404 Not Found
AlreadyExists:      409 Conflict
PermissionDenied:   403 Forbidden
Unauthenticated:    401 Unauthorized
ResourceExhausted:  429 Too Many Requests
FailedPrecondition: 412 Precondition Failed
Aborted:            409 Conflict
OutOfRange:         422 Unprocessable Entity
Unimplemented:      501 Not Implemented
Internal:           500 Internal Server Error
Unavailable:        503 Service Unavailable
DataLoss:           500 Internal Server Error

* If the gRPC status indicates Canceled or DeadlineExceeded
  and the given request context ALSO indicates a context error
  (meaning that the request was cancelled by the client), then
  a 499 Client Closed Request code is used instead.

If any other gRPC status code is observed, it would get translated into a 500 Internal Server Error.

Note that OK is absent from the mapping because the error renderer will never be called for a non-error status.

This function uses http.Error to render the computed code (and corresponding status text) to the given ResponseWriter.

func HandleMethod

func HandleMethod(svr interface{}, serviceName string, desc *grpc.MethodDesc, unaryInt grpc.UnaryServerInterceptor, opts ...HandlerOption) http.HandlerFunc

HandleMethod returns an HTTP handler that will handle a unary RPC method by dispatching the given method on the given server.

func HandleServices

func HandleServices(mux Mux, basePath string, reg grpchan.HandlerMap, unaryInt grpc.UnaryServerInterceptor, streamInt grpc.StreamServerInterceptor, opts ...HandlerOption)

HandleServices uses the given mux to register handlers for all methods exposed by handlers registered in reg. They are registered using a path of "basePath/name.of.Service/Method". If non-nil interceptor(s) are provided then they will be used to intercept applicable RPCs before dispatch to the registered handler.

func HandleStream

func HandleStream(svr interface{}, serviceName string, desc *grpc.StreamDesc, streamInt grpc.StreamServerInterceptor, opts ...HandlerOption) http.HandlerFunc

HandleStream returns an HTTP handler that will handle a streaming RPC method by dispatching the given method on the given server.

Types

type Channel

type Channel struct {
	Transport http.RoundTripper
	BaseURL   *url.URL
}

Channel is used as a connection for GRPC requests issued over HTTP 1.1. The server endpoint is configured using the BaseURL field, and the Transport can also be configured. Both of those fields must be specified.

It implements version 1 of the GRPC-over-HTTP transport protocol.

func (*Channel) Invoke

func (ch *Channel) Invoke(ctx context.Context, methodName string, req, resp interface{}, opts ...grpc.CallOption) error

Invoke satisfies the grpchan.Channel interface and supports sending unary RPCs via the in-process channel.

func (*Channel) NewStream

func (ch *Channel) NewStream(ctx context.Context, desc *grpc.StreamDesc, methodName string, opts ...grpc.CallOption) (grpc.ClientStream, error)

NewStream satisfies the grpchan.Channel interface and supports sending streaming RPCs via the in-process channel.

type HandlerOption added in v1.1.0

type HandlerOption func(*handlerOpts)

HandlerOption is an option to customize some aspect of the HTTP handler behavior, such as rendering gRPC errors to HTTP responses.

func ErrorRenderer added in v1.1.0

func ErrorRenderer(errFunc func(reqCtx context.Context, st *status.Status, response http.ResponseWriter)) HandlerOption

ErrorRenderer returns a HandlerOption that will cause the handler to use the given function to render an error. It is only used for unary RPCs since streaming RPCs serialize a status message to the response trailer (in the HTTP body) instead.

The function should call methods on response in order to write an error response, including any response headers, the HTTP status code, and any response body.

If no such option is used, the handler will use DefaultErrorRenderer.

type HttpTrailer

type HttpTrailer struct {
	Metadata map[string]*TrailerValues `` /* 157-byte string literal not displayed */
	Code     int32                     `protobuf:"varint,2,opt,name=code,proto3" json:"code,omitempty"`
	Message  string                    `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"`
	Details  []*anypb.Any              `protobuf:"bytes,4,rep,name=details,proto3" json:"details,omitempty"`
	// contains filtered or unexported fields
}

HttpTrailer is the last message sent in a streaming GRPC-over-HTTP call, to encode the GRPC status code and any trailer metadata. This message is only used in GRPC responses, not in requests.

func (*HttpTrailer) Descriptor deprecated

func (*HttpTrailer) Descriptor() ([]byte, []int)

Deprecated: Use HttpTrailer.ProtoReflect.Descriptor instead.

func (*HttpTrailer) GetCode

func (x *HttpTrailer) GetCode() int32

func (*HttpTrailer) GetDetails

func (x *HttpTrailer) GetDetails() []*anypb.Any

func (*HttpTrailer) GetMessage

func (x *HttpTrailer) GetMessage() string

func (*HttpTrailer) GetMetadata

func (x *HttpTrailer) GetMetadata() map[string]*TrailerValues

func (*HttpTrailer) ProtoMessage

func (*HttpTrailer) ProtoMessage()

func (*HttpTrailer) ProtoReflect added in v1.1.0

func (x *HttpTrailer) ProtoReflect() protoreflect.Message

func (*HttpTrailer) Reset

func (x *HttpTrailer) Reset()

func (*HttpTrailer) String

func (x *HttpTrailer) String() string

type Mux

type Mux func(pattern string, handler func(http.ResponseWriter, *http.Request))

Mux is a function that can register a gRPC-over-HTTP handler. This is used to register handlers in bulk for an RPC service. Its signature matches that of the HandleFunc method of the http.ServeMux type, and it also matches that of the http.HandleFunc function (for registering handlers with the default mux).

Callers can provide custom Mux functions that further decorate the handler (for example, adding authentication checks, logging, error handling, etc).

type TrailerValues

type TrailerValues struct {
	Values []string `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"`
	// contains filtered or unexported fields
}

func (*TrailerValues) Descriptor deprecated

func (*TrailerValues) Descriptor() ([]byte, []int)

Deprecated: Use TrailerValues.ProtoReflect.Descriptor instead.

func (*TrailerValues) GetValues

func (x *TrailerValues) GetValues() []string

func (*TrailerValues) ProtoMessage

func (*TrailerValues) ProtoMessage()

func (*TrailerValues) ProtoReflect added in v1.1.0

func (x *TrailerValues) ProtoReflect() protoreflect.Message

func (*TrailerValues) Reset

func (x *TrailerValues) Reset()

func (*TrailerValues) String

func (x *TrailerValues) String() string

Jump to

Keyboard shortcuts

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