triple_protocol

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Dec 28, 2024 License: Apache-2.0 Imports: 40 Imported by: 0

Documentation

Overview

Package triple is a slim RPC framework built on Protocol Buffers and net/http. In addition to supporting its own protocol, Triple handlers and clients are wire-compatible with gRPC, including streaming.

This documentation is intended to explain each type and function in isolation. Walkthroughs, FAQs, and other narrative docs are available on the dubbo-go website, and there's a working demonstration service on Github.

Example (Client)
logger := log.New(os.Stdout, "" /* prefix */, 0 /* flags */)
// Unfortunately, pkg.go.dev can't run examples that actually use the
// network. To keep this example runnable, we'll use an HTTP server and
// client that communicate over in-memory pipes. The client is still a plain
// *http.Client!
httpClient := examplePingServer.Client()

// By default, clients use the GRPC protocol. Add triple_protocol.WithTriple() or
// triple_protocol.WithGRPCWeb() to switch protocols.
client := pingv1connect.NewPingServiceClient(
	httpClient,
	examplePingServer.URL(),
)
response := tri.NewResponse(&pingv1.PingResponse{})
if err := client.Ping(
	context.Background(),
	tri.NewRequest(&pingv1.PingRequest{Number: 42}),
	response,
); err != nil {
	logger.Println("error:", err)
	return
}
logger.Println("response content-type:", response.Header().Get("Content-Type"))
logger.Println("response message:", response.Msg)
Output:

response content-type: application/grpc+proto
response message: number:42
Example (Handler)
package main

import (
	"context"
	"net/http"
)

import (
	triple "gitee.com/git4chen/dubbo-go/protocol/triple/triple_protocol"
	pingv1 "gitee.com/git4chen/dubbo-go/protocol/triple/triple_protocol/internal/gen/proto/connect/ping/v1"
	"gitee.com/git4chen/dubbo-go/protocol/triple/triple_protocol/internal/gen/proto/connect/ping/v1/pingv1connect"
)

// ExamplePingServer implements some trivial business logic. The Protobuf
// definition for this API is in proto/triple/ping/v1/ping.proto.
type ExamplePingServer struct {
	pingv1connect.UnimplementedPingServiceHandler
}

// Ping implements pingv1connect.PingServiceHandler.
func (*ExamplePingServer) Ping(
	_ context.Context,
	request *triple.Request,
) (*triple.Response, error) {
	msg := request.Msg.(*pingv1.PingRequest)
	return triple.NewResponse(&pingv1.PingResponse{
		Number: msg.Number,
		Text:   msg.Text,
	}), nil
}

func main() {
	// protoc-gen-triple-go generates constructors that return plain net/http
	// Handlers, so they're compatible with most Go HTTP routers and middleware
	// (for example, net/http's StripPrefix). Each handler automatically supports
	// the Connect, gRPC, and gRPC-Web protocols.
	mux := http.NewServeMux()
	mux.Handle(
		pingv1connect.NewPingServiceHandler(
			&ExamplePingServer{}, // our business logic
		),
	)
	// You can serve gRPC's health and server reflection APIs using
	// github.com/bufbuild/triple-grpchealth-go and
	// github.com/bufbuild/triple-grpcreflect-go.
	_ = http.ListenAndServeTLS(
		"localhost:8080",
		"internal/testdata/server.crt",
		"internal/testdata/server.key",
		mux,
	)
	// To serve HTTP/2 requests without TLS (as many gRPC clients expect), import
	// golang.org/x/net/http2/h2c and golang.org/x/net/http2 and change to:
	// _ = http.ListenAndServe(
	// 	"localhost:8080",
	// 	h2c.NewHandler(mux, &http2.Server{}),
	// )
}
Output:

Index

Examples

Constants

View Source
const (
	TripleContentType    = "application/grpc+proto"
	TripleUserAgent      = "grpc-go/1.35.0-dev"
	TripleServiceVersion = "tri-service-version"
	TripleAttachement    = "tri-attachment"
	TripleServiceGroup   = "tri-service-group"
	TripleRequestID      = "tri-req-id"
	TripleTraceID        = "tri-trace-traceid"
	TripleTraceRPCID     = "tri-trace-rpcid"
	TripleTraceProtoBin  = "tri-trace-proto-bin"
	TripleUnitInfo       = "tri-unit-info"
)

These keys are for compatible usage

View Source
const (
	ProtocolTriple  = "triple"
	ProtocolGRPC    = "grpc"
	ProtocolGRPCWeb = "grpcweb"
)

The names of the Triple, gRPC, and gRPC-Web protocols (as exposed by [Peer.Protocol]). Additional protocols may be added in the future.

View Source
const (
	IsAtLeastVersion0_0_1 = true
	IsAtLeastVersion0_1_0 = true
	IsAtLeastVersion1_6_0 = true
)

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

View Source
const Version = "0.1.0"

Version is the semantic version of the triple module.

Variables

This section is empty.

Functions

func AppendToOutgoingContext

func AppendToOutgoingContext(ctx context.Context, kv ...string) context.Context

AppendToOutgoingContext merges kv pairs from user and existing headers. It is used for passing headers to server-side. It is like grpc.AppendToOutgoingContext. Please refer to https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md#sending-metadata.

func DecodeBinaryHeader

func DecodeBinaryHeader(data string) ([]byte, error)

DecodeBinaryHeader base64-decodes the data. It can decode padded or unpadded values. Following usual HTTP semantics, multiple base64-encoded values may be joined with a comma. When receiving such comma-separated values, split them with strings.Split before calling DecodeBinaryHeader.

Binary headers sent using the Triple, gRPC, and gRPC-Web protocols have keys ending in "-Bin".

func EncodeBinaryHeader

func EncodeBinaryHeader(data []byte) string

EncodeBinaryHeader base64-encodes the data. It always emits unpadded values.

In the Triple, gRPC, and gRPC-Web protocols, binary headers must have keys ending in "-Bin".

func ExtractFromOutgoingContext

func ExtractFromOutgoingContext(ctx context.Context) http.Header

func FromIncomingContext

func FromIncomingContext(ctx context.Context) (http.Header, bool)

FromIncomingContext retrieves headers passed by client-side. It is like grpc.FromIncomingContext. it must call after append/setOutgoingContext to return current value Please refer to https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md#receiving-metadata-1.

func IsEnded

func IsEnded(err error) bool

IsEnded is a convenient function indicating the end of stream. It is introduced to not expose io.EOF to beginners. Please refer to https://github.com/apache/dubbo-go/pull/2416#discussion_r1318558801

func IsWireError

func IsWireError(err error) bool

IsWireError checks whether the error was returned by the server, as opposed to being synthesized by the client.

Clients may find this useful when deciding how to propagate errors. For example, an RPC-to-HTTP proxy might expose a server-sent CodeUnknown as an HTTP 500 but a client-synthesized CodeUnknown as a 503.

func MaxBytesHandler

func MaxBytesHandler(h http.Handler, n int64) http.Handler

MaxBytesHandler is the same as http.MaxBytesHandler for compatibility since there is another MaxBytesHandler in maxbytes_low_version.go.

func NewOutgoingContext

func NewOutgoingContext(ctx context.Context, data http.Header) context.Context

NewOutgoingContext sets headers entirely. If there are existing headers, they would be replaced. It is used for passing headers to server-side. It is like grpc.NewOutgoingContext. Please refer to https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md#sending-metadata.

func SendHeader

func SendHeader(ctx context.Context, header http.Header) error

SendHeader is used for setting response headers in server-side and send them directly. It is like grpc.SendHeader(ctx, header). Please refer to https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md#unary-call-2.

func SetHeader

func SetHeader(ctx context.Context, header http.Header) error

SetHeader is used for setting response header in server-side. It is like grpc.SendHeader(ctx, header) but not send header. Please refer to https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md#unary-call-2.

func SetTrailer

func SetTrailer(ctx context.Context, trailer http.Header) error

SetTrailer is used for setting response trailers in server-side. It is like grpc.SetTrailer(ctx, header). Please refer to https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md#unary-call-2.

Types

type AnyRequest

type AnyRequest interface {
	Any() interface{}
	Spec() Spec
	Peer() Peer
	Header() http.Header
	// contains filtered or unexported methods
}

AnyRequest is the common method set of every Request, regardless of type parameter. It's used in unary interceptors.

Headers and trailers beginning with "Triple-" and "Grpc-" are reserved for use by the gRPC and Triple protocols: applications may read them but shouldn't write them.

To preserve our ability to add methods to this interface without breaking backward compatibility, only types defined in this package can implement AnyRequest.

type AnyResponse

type AnyResponse interface {
	Any() interface{}
	Header() http.Header
	Trailer() http.Header
	// contains filtered or unexported methods
}

AnyResponse is the common method set of every Response, regardless of type parameter. It's used in unary interceptors.

Headers and trailers beginning with "Triple-" and "Grpc-" are reserved for use by the Triple and Grpc protocols: applications may read them but shouldn't write them.

To preserve our ability to add methods to this interface without breaking backward compatibility, only types defined in this package can implement AnyResponse.

type BidiStream

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

BidiStream is the handler's view of a bidirectional streaming RPC.

It's constructed as part of Handler invocation, but doesn't currently have an exported constructor.

func (*BidiStream) Conn

func (b *BidiStream) Conn() StreamingHandlerConn

Conn exposes the underlying StreamingHandlerConn. This may be useful if you'd prefer to wrap the connection in a different high-level API.

func (*BidiStream) ExportableHeader

func (b *BidiStream) ExportableHeader() http.Header

ExportableHeader returns the headers could be exported to users.

func (*BidiStream) Peer

func (b *BidiStream) Peer() Peer

Peer describes the client for this RPC.

func (*BidiStream) Receive

func (b *BidiStream) Receive(msg interface{}) error

Receive a message. When the client is done sending messages, Receive will return an error that wraps io.EOF.

func (*BidiStream) RequestHeader

func (b *BidiStream) RequestHeader() http.Header

RequestHeader returns the headers received from the client.

func (*BidiStream) ResponseHeader

func (b *BidiStream) ResponseHeader() http.Header

ResponseHeader returns the response headers. Headers are sent with the first call to Send.

Headers beginning with "Triple-" and "Grpc-" are reserved for use by the Triple and gRPC protocols. Applications shouldn't write them.

func (*BidiStream) ResponseTrailer

func (b *BidiStream) ResponseTrailer() http.Header

ResponseTrailer returns the response trailers. Handlers may write to the response trailers at any time before returning.

Trailers beginning with "Triple-" and "Grpc-" are reserved for use by the Triple and gRPC protocols. Applications shouldn't write them.

func (*BidiStream) Send

func (b *BidiStream) Send(msg interface{}) error

Send a message to the client. The first call to Send also sends the response headers.

func (*BidiStream) Spec

func (b *BidiStream) Spec() Spec

Spec returns the specification for the RPC.

type BidiStreamForClient

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

BidiStreamForClient is the client's view of a bidirectional streaming RPC.

It's returned from Client.CallBidiStream, but doesn't currently have an exported constructor function.

func (*BidiStreamForClient) CloseRequest

func (b *BidiStreamForClient) CloseRequest() error

CloseRequest closes the send side of the stream.

func (*BidiStreamForClient) CloseResponse

func (b *BidiStreamForClient) CloseResponse() error

CloseResponse closes the receive side of the stream.

func (*BidiStreamForClient) Conn

Conn exposes the underlying StreamingClientConn. This may be useful if you'd prefer to wrap the connection in a different high-level API.

func (*BidiStreamForClient) Peer

func (b *BidiStreamForClient) Peer() Peer

Peer describes the server for the RPC.

func (*BidiStreamForClient) Receive

func (b *BidiStreamForClient) Receive(msg interface{}) error

Receive a message. When the server is done sending messages and no other errors have occurred, Receive will return an error that wraps io.EOF.

func (*BidiStreamForClient) RequestHeader

func (b *BidiStreamForClient) RequestHeader() http.Header

RequestHeader returns the request headers. Headers are sent with the first call to Send.

Headers beginning with "Triple-" and "Grpc-" are reserved for use by the Triple and gRPC protocols. Applications shouldn't write them.

func (*BidiStreamForClient) ResponseHeader

func (b *BidiStreamForClient) ResponseHeader() http.Header

ResponseHeader returns the headers received from the server. It blocks until the first call to Receive returns.

func (*BidiStreamForClient) ResponseTrailer

func (b *BidiStreamForClient) ResponseTrailer() http.Header

ResponseTrailer returns the trailers received from the server. Trailers aren't fully populated until Receive() returns an error wrapping io.EOF.

func (*BidiStreamForClient) Send

func (b *BidiStreamForClient) Send(msg interface{}) error

Send a message to the server. The first call to Send also sends the request headers. To send just the request headers without a body, call Send with a nil pointer.

If the server returns an error, Send returns an error that wraps io.EOF. Clients should check for EOF using the standard library's errors.Is and call Receive to retrieve the error.

func (*BidiStreamForClient) Spec

func (b *BidiStreamForClient) Spec() Spec

Spec returns the specification for the RPC.

type Client

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

Client is a reusable, concurrency-safe client for a single procedure. Depending on the procedure's type, use the CallUnary, CallClientStream, CallServerStream, or CallBidiStream method.

By default, clients use the gRPC protocol with the binary Protobuf Codec, ask for gzipped responses, and send uncompressed requests. To use the Triple, use the WithTriple options.

func NewClient

func NewClient(httpClient HTTPClient, url string, options ...ClientOption) *Client

NewClient constructs a new Client.

func (*Client) CallBidiStream

func (c *Client) CallBidiStream(ctx context.Context) (*BidiStreamForClient, error)

CallBidiStream calls a bidirectional streaming procedure.

func (*Client) CallClientStream

func (c *Client) CallClientStream(ctx context.Context) (*ClientStreamForClient, error)

CallClientStream calls a client streaming procedure.

func (*Client) CallServerStream

func (c *Client) CallServerStream(ctx context.Context, request *Request) (*ServerStreamForClient, error)

CallServerStream calls a server streaming procedure.

func (*Client) CallUnary

func (c *Client) CallUnary(ctx context.Context, request *Request, response *Response) error

CallUnary calls a request-response procedure.

type ClientOption

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

A ClientOption configures a Client.

In addition to any options grouped in the documentation below, remember that any Option is also a valid ClientOption.

func WithAcceptCompression

func WithAcceptCompression(
	name string,
	newDecompressor func() Decompressor,
	newCompressor func() Compressor,
) ClientOption

WithAcceptCompression makes a compression algorithm available to a client. Clients ask servers to compress responses using any of the registered algorithms. The first registered algorithm is treated as the least preferred, and the last registered algorithm is the most preferred.

It's safe to use this option liberally: servers will ignore any compression algorithms they don't support. To compress requests, pair this option with WithSendCompression. To remove support for a previously-registered compression algorithm, use WithAcceptCompression with nil decompressor and compressor constructors.

Clients accept gzipped responses by default, using a compressor backed by the standard library's gzip package with the default compression level. Use WithSendGzip to compress requests with gzip.

Calling WithAcceptCompression with an empty name is a no-op.

func WithClientOptions

func WithClientOptions(options ...ClientOption) ClientOption

WithClientOptions composes multiple ClientOptions into one.

func WithHessian2

func WithHessian2() ClientOption

todo(DMwangnima): add comment

func WithMsgPack

func WithMsgPack() ClientOption

func WithProtoJSON

func WithProtoJSON() ClientOption

WithProtoJSON configures a client to send JSON-encoded data instead of binary Protobuf. It uses the standard Protobuf JSON mapping as implemented by google.golang.org/protobuf/encoding/protojson: fields are named using lowerCamelCase, zero values are omitted, missing required fields are errors, enums are emitted as strings, etc.

func WithSendCompression

func WithSendCompression(name string) ClientOption

WithSendCompression configures the client to use the specified algorithm to compress request messages. If the algorithm has not been registered using WithAcceptCompression, the client will return errors at runtime.

Because some servers don't support compression, clients default to sending uncompressed requests.

func WithSendGzip

func WithSendGzip() ClientOption

WithSendGzip configures the client to gzip requests. Since clients have access to a gzip compressor by default, WithSendGzip doesn't require WithSendCompression.

Some servers don't support gzip, so clients default to sending uncompressed requests.

func WithTimeout

func WithTimeout(timeout time.Duration) ClientOption

WithTimeout configures the default timeout of client call including unary and stream. If you want to specify the timeout of a specific request, please use context.WithTimeout, then default timeout would be overridden.

func WithTriple

func WithTriple() ClientOption

WithTriple configures clients to use the Triple protocol.

type ClientStream

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

ClientStream is the handler's view of a client streaming RPC.

It's constructed as part of Handler invocation, but doesn't currently have an exported constructor.

func (*ClientStream) Conn

Conn exposes the underlying StreamingHandlerConn. This may be useful if you'd prefer to wrap the connection in a different high-level API.

func (*ClientStream) Err

func (c *ClientStream) Err() error

Err returns the first non-EOF error that was encountered by Receive.

func (*ClientStream) Msg

func (c *ClientStream) Msg() interface{}

Msg returns the most recent message unmarshaled by a call to Receive.

func (*ClientStream) Peer

func (c *ClientStream) Peer() Peer

Peer describes the client for this RPC.

func (*ClientStream) Receive

func (c *ClientStream) Receive(msg interface{}) bool

Receive advances the stream to the next message, which will then be available through the Msg method. It returns false when the stream stops, either by reaching the end or by encountering an unexpected error. After Receive returns false, the Err method will return any unexpected error encountered.

func (*ClientStream) RequestHeader

func (c *ClientStream) RequestHeader() http.Header

RequestHeader returns the headers received from the client.

func (*ClientStream) Spec

func (c *ClientStream) Spec() Spec

Spec returns the specification for the RPC.

type ClientStreamForClient

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

ClientStreamForClient is the client's view of a client streaming RPC.

It's returned from Client.CallClientStream, but doesn't currently have an exported constructor function.

func (*ClientStreamForClient) CloseAndReceive

func (c *ClientStreamForClient) CloseAndReceive(response *Response) error

CloseAndReceive closes the send side of the stream and waits for the response.

func (*ClientStreamForClient) Conn

Conn exposes the underlying StreamingClientConn. This may be useful if you'd prefer to wrap the connection in a different high-level API.

func (*ClientStreamForClient) Peer

func (c *ClientStreamForClient) Peer() Peer

Peer describes the server for the RPC.

func (*ClientStreamForClient) RequestHeader

func (c *ClientStreamForClient) RequestHeader() http.Header

RequestHeader returns the request headers. Headers are sent to the server with the first call to Send.

Headers beginning with "Triple-" and "Grpc-" are reserved for use by the Triple and gRPC protocols. Applications shouldn't write them.

func (*ClientStreamForClient) Send

func (c *ClientStreamForClient) Send(request interface{}) error

Send a message to the server. The first call to Send also sends the request headers.

If the server returns an error, Send returns an error that wraps io.EOF. Clients should check for case using the standard library's errors.Is or IsEnded and unmarshal the error using CloseAndReceive.

func (*ClientStreamForClient) Spec

func (c *ClientStreamForClient) Spec() Spec

Spec returns the specification for the RPC.

type Code

type Code uint32

A Code is one of the Triple protocol's error codes. There are no user-defined codes, so only the codes enumerated below are valid. In both name and semantics, these codes match the gRPC status codes.

The descriptions below are optimized for brevity rather than completeness. See the [Triple protocol specification] for detailed descriptions of each code and example usage.

todo(DMwangnima): add specification to dubbo-go official site [Triple protocol specification]: https://connect.build/docs/protocol

const (

	// CodeCanceled indicates that the operation was canceled, typically by the
	// caller.
	CodeCanceled Code = 1

	// CodeUnknown indicates that the operation failed for an unknown reason.
	CodeUnknown Code = 2

	// CodeInvalidArgument indicates that client supplied an invalid argument.
	CodeInvalidArgument Code = 3

	// CodeDeadlineExceeded indicates that deadline expired before the operation
	// could complete.
	CodeDeadlineExceeded Code = 4

	// CodeNotFound indicates that some requested entity (for example, a file or
	// directory) was not found.
	CodeNotFound Code = 5

	// CodeAlreadyExists indicates that client attempted to create an entity (for
	// example, a file or directory) that already exists.
	CodeAlreadyExists Code = 6

	// CodePermissionDenied indicates that the caller doesn't have permission to
	// execute the specified operation.
	CodePermissionDenied Code = 7

	// CodeResourceExhausted indicates that some resource has been exhausted. For
	// example, a per-user quota may be exhausted or the entire file system may
	// be full.
	CodeResourceExhausted Code = 8

	// CodeFailedPrecondition indicates that the system is not in a state
	// required for the operation's execution.
	CodeFailedPrecondition Code = 9

	// CodeAborted indicates that operation was aborted by the system, usually
	// because of a concurrency issue such as a sequencer check failure or
	// transaction abort.
	CodeAborted Code = 10

	// CodeOutOfRange indicates that the operation was attempted past the valid
	// range (for example, seeking past end-of-file).
	CodeOutOfRange Code = 11

	// CodeUnimplemented indicates that the operation isn't implemented,
	// supported, or enabled in this service.
	CodeUnimplemented Code = 12

	// CodeInternal indicates that some invariants expected by the underlying
	// system have been broken. This code is reserved for serious errors.
	CodeInternal Code = 13

	// CodeUnavailable indicates that the service is currently unavailable. This
	// is usually temporary, so clients can back off and retry idempotent
	// operations.
	CodeUnavailable Code = 14

	// CodeDataLoss indicates that the operation has resulted in unrecoverable
	// data loss or corruption.
	CodeDataLoss Code = 15

	// CodeUnauthenticated indicates that the request does not have valid
	// authentication credentials for the operation.
	CodeUnauthenticated Code = 16

	CodeBizError Code = 17
)

func CodeOf

func CodeOf(err error) Code

CodeOf returns the error's status code if it is or wraps an *Error and CodeUnknown otherwise.

func (Code) MarshalText

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

MarshalText implements encoding.TextMarshaler.

func (Code) String

func (c Code) String() string

func (*Code) UnmarshalText

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

UnmarshalText implements encoding.TextUnmarshaler.

type Codec

type Codec interface {
	// Name returns the name of the Codec.
	//
	// This may be used as part of the Content-Type within HTTP. For example,
	// with gRPC this is the content subtype, so "application/grpc+proto" will
	// map to the Codec with name "proto".
	//
	// Names must not be empty.
	Name() string
	// Marshal marshals the given message.
	//
	// Marshal may expect a specific type of message, and will error if this type
	// is not given.
	Marshal(interface{}) ([]byte, error)
	// Unmarshal unmarshals the given message.
	//
	// Unmarshal may expect a specific type of message, and will error if this
	// type is not given.
	Unmarshal([]byte, interface{}) error
}

Codec marshals structs (typically generated from a schema) to and from bytes.

type Compressor

type Compressor interface {
	io.Writer

	// Close flushes any buffered data to the underlying sink, then closes the
	// Compressor. It must not close the underlying sink.
	Close() error

	// Reset discards the Compressor's internal state, if any, and prepares it to
	// write compressed data to a new sink.
	Reset(io.Writer)
}

A Compressor is a reusable wrapper that compresses data written to an underlying sink. The standard library's *gzip.Writer implements Compressor.

type Decompressor

type Decompressor interface {
	io.Reader

	// Close closes the Decompressor, but not the underlying data source. It may
	// return an error if the Decompressor wasn't read to EOF.
	Close() error

	// Reset discards the Decompressor's internal state, if any, and prepares it
	// to read from a new source of compressed data.
	Reset(io.Reader) error
}

A Decompressor is a reusable wrapper that decompresses an underlying data source. The standard library's *gzip.Reader implements Decompressor.

type Error

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

An Error captures four key pieces of information: a Code, an underlying Go error, a map of metadata, and an optional collection of arbitrary Protobuf messages called "details" (more on those below). Servers send the code, the underlying error's Error() output, the metadata, and details over the wire to clients. Remember that the underlying error's message will be sent to clients - take care not to leak sensitive information from public APIs!

Service implementations and interceptors should return errors that can be cast to an *Error (using the standard library's errors.As). If the returned error can't be cast to an *Error, triple will use CodeUnknown and the returned error's message.

Error details are an optional mechanism for servers, interceptors, and proxies to attach arbitrary Protobuf messages to the error code and message. They're a clearer and more performant alternative to HTTP header microformats. See [the documentation on errors] for more details.

todo(DMwangnima): add error documentation to dubbo-go official website [the documentation on errors]: https://connect.build/docs/go/errors

func NewError

func NewError(c Code, underlying error) *Error

NewError annotates any Go error with a status code.

func NewWireError

func NewWireError(c Code, underlying error) *Error

NewWireError is similar to NewError, but the resulting *Error returns true when tested with IsWireError.

This is useful for clients trying to propagate partial failures from streaming RPCs. Often, these RPCs include error information in their response messages (for example, gRPC server reflection and OpenTelemtetry's OTLP). Clients propagating these errors up the stack should use NewWireError to clarify that the error code, message, and details (if any) were explicitly sent by the server rather than inferred from a lower-level networking error or timeout.

func (*Error) AddDetail

func (e *Error) AddDetail(d *ErrorDetail)

AddDetail appends 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() []*ErrorDetail

Details returns the error's details.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Message

func (e *Error) Message() string

Message returns the underlying error message. It may be empty if the original error was created with a status code and a nil error.

Example
package main

import (
	"errors"
	"fmt"

	tri "gitee.com/git4chen/dubbo-go/protocol/triple/triple_protocol"
)

func main() {
	err := fmt.Errorf(
		"another: %w",
		tri.NewError(tri.CodeUnavailable, errors.New("failed to foo")),
	)
	if connectErr := (&tri.Error{}); errors.As(err, &connectErr) {
		fmt.Println("underlying error message:", connectErr.Message())
	}

}
Output:

underlying error message: failed to foo

func (*Error) Meta

func (e *Error) Meta() http.Header

Meta allows the error to carry additional information as key-value pairs.

Metadata attached to errors returned by unary handlers is always sent as HTTP headers, regardless of the protocol. Metadata attached to errors returned by streaming handlers may be sent as HTTP headers, HTTP trailers, or a block of in-body metadata, depending on the protocol in use and whether or not the handler has already written messages to the stream.

When clients receive errors, the metadata contains the union of the HTTP headers and the protocol-specific trailers (either HTTP trailers or in-body metadata).

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap allows errors.Is and errors.As access to the underlying error.

type ErrorDetail

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

An ErrorDetail is a self-describing Protobuf message attached to an *Error. Error details are sent over the network to clients, which can then work with strongly-typed data rather than trying to parse a complex error message. For example, you might use details to send a localized error message or retry parameters to the client.

The google.golang.org/genproto/googleapis/rpc/errdetails package contains a variety of Protobuf messages commonly used as error details.

func NewErrorDetail

func NewErrorDetail(msg proto.Message) (*ErrorDetail, error)

NewErrorDetail constructs a new error detail. If msg is an *anypb.Any then it is used as is. Otherwise, it is first marshaled into an *anypb.Any value. This returns an error if msg cannot be marshaled.

func (*ErrorDetail) Bytes

func (d *ErrorDetail) Bytes() []byte

Bytes returns a copy of the Protobuf-serialized detail.

func (*ErrorDetail) Type

func (d *ErrorDetail) Type() string

Type is the fully-qualified name of the detail's Protobuf message (for example, acme.foo.v1.FooDetail).

func (*ErrorDetail) Value

func (d *ErrorDetail) Value() (proto.Message, error)

Value uses the Protobuf runtime's package-global registry to unmarshal the Detail into a strongly-typed message. Typically, clients use Go type assertions to cast from the proto.Message interface to concrete types.

type ErrorWriter

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

An ErrorWriter writes errors to an http.ResponseWriter in the format expected by an RPC client. This is especially useful in server-side net/http middleware, where you may wish to handle requests from RPC and non-RPC clients with the same code.

ErrorWriters are safe to use concurrently.

Example
package main

import (
	"errors"
	"io"
	"log"
	"net/http"
)

import (
	tri "gitee.com/git4chen/dubbo-go/protocol/triple/triple_protocol"
)

// NewHelloHandler is an example HTTP handler. In a real application, it might
// handle RPCs, requests for HTML, or anything else.
func NewHelloHandler() http.Handler {
	return http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
		io.WriteString(response, "Hello, world!")
	})
}

// NewAuthenticatedHandler is an example of middleware that works with both RPC
// and non-RPC clients.
func NewAuthenticatedHandler(handler http.Handler) http.Handler {
	errorWriter := tri.NewErrorWriter()
	return http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
		// Dummy authentication logic.
		if request.Header.Get("Token") == "super-secret" {
			handler.ServeHTTP(response, request)
			return
		}
		defer request.Body.Close()
		defer io.Copy(io.Discard, request.Body)
		if errorWriter.IsSupported(request) {
			// Send a protocol-appropriate error to RPC clients, so that they receive
			// the right code, message, and any metadata or error details.
			unauthenticated := tri.NewError(tri.CodeUnauthenticated, errors.New("invalid token"))
			errorWriter.Write(response, request, unauthenticated)
		} else {
			// Send an error to non-RPC clients.
			response.WriteHeader(http.StatusUnauthorized)
			io.WriteString(response, "invalid token")
		}
	})
}

func main() {
	mux := http.NewServeMux()
	mux.Handle("/", NewHelloHandler())
	srv := &http.Server{
		Addr:    ":8080",
		Handler: NewAuthenticatedHandler(mux),
	}
	if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
		log.Fatalln(err)
	}
}
Output:

func NewErrorWriter

func NewErrorWriter(opts ...HandlerOption) *ErrorWriter

NewErrorWriter constructs an ErrorWriter. To properly recognize supported RPC Content-Types in net/http middleware, you must pass the same HandlerOptions to NewErrorWriter and any wrapped Triple handlers.

func (*ErrorWriter) IsSupported

func (w *ErrorWriter) IsSupported(request *http.Request) bool

IsSupported checks whether a request is using one of the ErrorWriter's supported RPC protocols.

func (*ErrorWriter) Write

func (w *ErrorWriter) Write(response http.ResponseWriter, request *http.Request, err error) error

Write an error, using the format appropriate for the RPC protocol in use. Callers should first use IsSupported to verify that the request is using one of the ErrorWriter's supported RPC protocols.

Write does not read or close the request body.

type ExpectedCodecNameOption

type ExpectedCodecNameOption struct {
	ExpectedCodecName string
}

type HTTPClient

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

HTTPClient is the interface triple expects HTTP clients to implement. The standard library's *http.Client implements HTTPClient.

type Handler

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

A Handler is the server-side implementation of a single RPC defined by a service schema.

By default, Handlers support the Triple, gRPC, and gRPC-Web protocols with the binary Protobuf and JSON codecs. They support gzip compression using the standard library's compress/gzip.

func NewBidiStreamHandler

func NewBidiStreamHandler(
	procedure string,
	streamFunc func(context.Context, *BidiStream) error,
	options ...HandlerOption,
) *Handler

NewBidiStreamHandler constructs a Handler for a bidirectional streaming procedure.

func NewClientStreamHandler

func NewClientStreamHandler(
	procedure string,
	streamFunc func(context.Context, *ClientStream) (*Response, error),
	options ...HandlerOption,
) *Handler

NewClientStreamHandler constructs a Handler for a client streaming procedure.

func NewCompatStreamHandler

func NewCompatStreamHandler(
	procedure string,
	srv interface{},
	typ StreamType,
	streamFunc func(srv interface{}, stream grpc.ServerStream) error,
	options ...HandlerOption,
) *Handler

func NewCompatUnaryHandler

func NewCompatUnaryHandler(
	procedure string,
	method string,
	srv interface{},
	unary MethodHandler,
	options ...HandlerOption,
) *Handler

func NewServerStreamHandler

func NewServerStreamHandler(
	procedure string,
	reqInitFunc func() interface{},
	streamFunc func(context.Context, *Request, *ServerStream) error,
	options ...HandlerOption,
) *Handler

NewServerStreamHandler constructs a Handler for a server streaming procedure.

func NewUnaryHandler

func NewUnaryHandler(
	procedure string,
	reqInitFunc func() interface{},
	unary func(context.Context, *Request) (*Response, error),
	options ...HandlerOption,
) *Handler

NewUnaryHandler constructs a Handler for a request-response procedure.

func (*Handler) ServeHTTP

func (h *Handler) ServeHTTP(responseWriter http.ResponseWriter, request *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 any Option is also a HandlerOption.

func WithCompression

func WithCompression(
	name string,
	newDecompressor func() Decompressor,
	newCompressor func() Compressor,
) HandlerOption

WithCompression configures handlers to support a compression algorithm. Clients may send messages compressed with that algorithm and/or request compressed responses. The Compressor and Decompressor produced by the supplied constructors must use the same algorithm. Internally, Triple pools compressors and decompressors.

By default, handlers support gzip using the standard library's compress/gzip package at the default compression level. To remove support for a previously-registered compression algorithm, use WithCompression with nil decompressor and compressor constructors.

Calling WithCompression with an empty name is a no-op.

func WithHandlerOptions

func WithHandlerOptions(options ...HandlerOption) HandlerOption

WithHandlerOptions composes multiple HandlerOptions into one.

func WithRecover

func WithRecover(handle func(context.Context, Spec, http.Header, interface{}) error) HandlerOption

WithRecover adds an interceptor that recovers from panics. The supplied function receives the context, Spec, request headers, and the recovered value (which may be nil). It must return an error to send back to the client. It may also log the panic, emit metrics, or execute other error-handling logic. Handler functions must be safe to call concurrently.

To preserve compatibility with net/http's semantics, this interceptor doesn't handle panics with http.ErrAbortHandler.

By default, handlers don't recover from panics. Because the standard library's http.Server recovers from panics by default, this option isn't usually necessary to prevent crashes. Instead, it helps servers collect RPC-specific data during panics and send a more detailed error to clients.

func WithRequireTripleProtocolHeader

func WithRequireTripleProtocolHeader() HandlerOption

WithRequireTripleProtocolHeader configures the Handler to require requests using the Triple RPC protocol to include the Triple-Protocol-Version header. This ensures that HTTP proxies and net/http middleware can easily identify valid Triple requests, even if they use a common Content-Type like application/json. However, it makes ad-hoc requests with tools like cURL more laborious.

This option has no effect if the client uses the gRPC or gRPC-Web protocols.

type IdempotencyLevel

type IdempotencyLevel int

An IdempotencyLevel is a value that declares how "idempotent" an RPC is. This value can affect RPC behaviors, such as determining whether it is safe to retry a request, or what kinds of request modalities are allowed for a given procedure.

const (
	// IdempotencyUnknown is the default idempotency level. A procedure with
	// this idempotency level may not be idempotent. This is appropriate for
	// any kind of procedure.
	IdempotencyUnknown IdempotencyLevel = 0

	// IdempotencyNoSideEffects is the idempotency level that specifies that a
	// given call has no side-effects. This is equivalent to [RFC 9110 § 9.2.1]
	// "safe" methods in terms of semantics. This procedure should not mutate
	// any state. This idempotency level is appropriate for queries, or anything
	// that would be suitable for an HTTP GET request. In addition, due to the
	// lack of side-effects, such a procedure would be suitable to retry and
	// expect that the results will not be altered by preceding attempts.
	//
	// [RFC 9110 § 9.2.1]: https://www.rfc-editor.org/rfc/rfc9110.html#section-9.2.1
	IdempotencyNoSideEffects IdempotencyLevel = 1

	// IdempotencyIdempotent is the idempotency level that specifies that a
	// given call is "idempotent", such that multiple instances of the same
	// request to this procedure would have the same side-effects as a single
	// request. This is equivalent to [RFC 9110 § 9.2.2] "idempotent" methods.
	// This level is a subset of the previous level. This idempotency level is
	// appropriate for any procedure that is safe to retry multiple times
	// and be guaranteed that the response and side-effects will not be altered
	// as a result of multiple attempts, for example, entity deletion requests.
	//
	// [RFC 9110 § 9.2.2]: https://www.rfc-editor.org/rfc/rfc9110.html#section-9.2.2
	IdempotencyIdempotent IdempotencyLevel = 2
)

func (IdempotencyLevel) String

func (i IdempotencyLevel) String() string

type Interceptor

type Interceptor interface {
	WrapUnary(UnaryFunc) UnaryFunc
	WrapUnaryHandler(UnaryHandlerFunc) UnaryHandlerFunc
	WrapStreamingClient(StreamingClientFunc) StreamingClientFunc
	WrapStreamingHandler(StreamingHandlerFunc) StreamingHandlerFunc
}

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 requests and responses, handle errors, retry, recover from panics, emit logs and metrics, or do nearly anything else.

The returned functions must be safe to call concurrently.

type MethodHandler

type MethodHandler func(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error)

type Option

type Option interface {
	ClientOption
	HandlerOption
}

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

func WithCodec

func WithCodec(codec Codec) Option

WithCodec registers a serialization method with a client or handler. Handlers may have multiple codecs registered, and use whichever the client chooses. Clients may only have a single codec.

By default, handlers and clients support binary Protocol Buffer data using google.golang.org/protobuf/proto. Handlers also support JSON by default, using the standard Protobuf JSON mapping. Users with more specialized needs may override the default codecs by registering a new codec under the "proto" or "json" names. When supplying a custom "proto" codec, keep in mind that some unexported, protocol-specific messages are serialized using Protobuf - take care to fall back to the standard Protobuf implementation if necessary.

Registering a codec with an empty name is a no-op.

func WithCompressMinBytes

func WithCompressMinBytes(min int) Option

WithCompressMinBytes sets a minimum size threshold for compression: regardless of compressor configuration, messages smaller than the configured minimum are sent uncompressed.

The default minimum is zero. Setting a minimum compression threshold may improve overall performance, because the CPU cost of compressing very small messages usually isn't worth the small reduction in network I/O.

func WithExpectedCodecName

func WithExpectedCodecName(ExpectedCodecName string) Option

func WithGroup

func WithGroup(group string) Option

func WithIdempotency

func WithIdempotency(idempotencyLevel IdempotencyLevel) Option

todo(DMwangnima): consider how to expose this functionality to users WithIdempotency declares the idempotency of the procedure. This can determine whether a procedure call can safely be retried, and may affect which request modalities are allowed for a given procedure call.

In most cases, you should not need to manually set this. It is normally set by the code generator for your schema. For protobuf schemas, it can be set like this:

rpc Ping(PingRequest) returns (PingResponse) {
  option idempotency_level = NO_SIDE_EFFECTS;
}

func WithInterceptors

func WithInterceptors(interceptors ...Interceptor) Option

WithInterceptors configures a client or handler's interceptor stack. Repeated WithInterceptors options are applied in order, so

WithInterceptors(A) + WithInterceptors(B, C) == WithInterceptors(A, B, C)

Unary interceptors compose like an onion. 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.

Stream interceptors also behave like an onion: the first interceptor provided is the outermost wrapper for the StreamingClientConn or StreamingHandlerConn. It's the first to see sent messages and the last to see received messages.

Applied to client and handler, WithInterceptors(A, B, ..., Y, Z) produces:

 client.Send()       client.Receive()
       |                   ^
       v                   |
    A ---                 --- A
    B ---                 --- B
    : ...                 ... :
    Y ---                 --- Y
    Z ---                 --- Z
       |                   ^
       v                   |
  = = = = = = = = = = = = = = = =
               network
  = = = = = = = = = = = = = = = =
       |                   ^
       v                   |
    A ---                 --- A
    B ---                 --- B
    : ...                 ... :
    Y ---                 --- Y
    Z ---                 --- Z
       |                   ^
       v                   |
handler.Receive()   handler.Send()
       |                   ^
       |                   |
       '-> handler logic >-'

Note that in clients, Send handles the request message(s) and Receive handles the response message(s). For handlers, it's the reverse. Depending on your interceptor's logic, you may need to wrap one method in clients and the other in handlers.

Example
logger := log.New(os.Stdout, "" /* prefix */, 0 /* flags */)
outer := triple.UnaryInterceptorFunc(
	func(next triple.UnaryFunc) triple.UnaryFunc {
		return triple.UnaryFunc(func(ctx context.Context, req triple.AnyRequest, res triple.AnyResponse) error {
			logger.Println("outer interceptor: before call")
			err := next(ctx, req, res)
			logger.Println("outer interceptor: after call")
			return err
		})
	},
)
inner := triple.UnaryInterceptorFunc(
	func(next triple.UnaryFunc) triple.UnaryFunc {
		return triple.UnaryFunc(func(ctx context.Context, req triple.AnyRequest, res triple.AnyResponse) error {
			logger.Println("inner interceptor: before call")
			err := next(ctx, req, res)
			logger.Println("inner interceptor: after call")
			return err
		})
	},
)
client := pingv1connect.NewPingServiceClient(
	examplePingServer.Client(),
	examplePingServer.URL(),
	triple.WithInterceptors(outer, inner),
)
if err := client.Ping(context.Background(), triple.NewRequest(&pingv1.PingRequest{}), triple.NewResponse(&pingv1.PingResponse{})); err != nil {
	logger.Println("error:", err)
	return
}
Output:

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

func WithOptions

func WithOptions(options ...Option) Option

WithOptions composes multiple Options into one.

func WithReadMaxBytes

func WithReadMaxBytes(max int) Option

WithReadMaxBytes limits the performance impact of pathologically large messages sent by the other party. For handlers, WithReadMaxBytes limits the size of a message that the client can send. For clients, WithReadMaxBytes limits the size of a message that the server can respond with. Limits apply to each Protobuf message, not to the stream as a whole.

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

Handlers may also use http.MaxBytesHandler to limit the total size of the HTTP request stream (rather than the per-message size). Triple handles http.MaxBytesError specially, so clients still receive errors with the appropriate error code and informative messages.

func WithSendMaxBytes

func WithSendMaxBytes(max int) Option

WithSendMaxBytes prevents sending messages too large for the client/handler to handle without significant performance overhead. For handlers, WithSendMaxBytes limits the size of a message that the handler can respond with. For clients, WithSendMaxBytes limits the size of a message that the client can send. Limits apply to each message, not to the stream as a whole.

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

func WithVersion

func WithVersion(version string) Option

type Peer

type Peer struct {
	Addr     string
	Protocol string
	Query    url.Values // server-only
}

Peer describes the other party to an RPC.

When accessed client-side, Addr contains the host or host:port from the server's URL. When accessed server-side, Addr contains the client's address in IP:port format.

On both the client and the server, Protocol is the RPC protocol in use. Currently, it's either ProtocolTriple, ProtocolGRPC, or todo: Should we support ProtocolGRPCWeb? ProtocolGRPCWeb, but additional protocols may be added in the future.

Query contains the query parameters for the request. For the server, this will reflect the actual query parameters sent. For the client, it is unset.

type Request

type Request struct {
	Msg interface{}
	// contains filtered or unexported fields
}

Request is a wrapper around a generated request message. It provides access to metadata like headers and the RPC specification, as well as strongly-typed access to the message itself.

func NewRequest

func NewRequest(message interface{}) *Request

NewRequest wraps a generated request message.

func (*Request) Any

func (r *Request) Any() interface{}

Any returns the concrete request message as an empty interface, so that *Request implements the AnyRequest interface.

func (*Request) Header

func (r *Request) Header() http.Header

Header returns the HTTP headers for this request. Headers beginning with "Triple-" and "Grpc-" are reserved for use by the Triple and gRPC protocols: applications may read them but shouldn't write them.

func (*Request) Peer

func (r *Request) Peer() Peer

Peer describes the other party for this RPC.

func (*Request) Spec

func (r *Request) Spec() Spec

Spec returns a description of this RPC.

type Response

type Response struct {
	Msg interface{}
	// contains filtered or unexported fields
}

Response is a wrapper around a generated response message. It provides access to metadata like headers and trailers, as well as strongly-typed access to the message itself.

func NewResponse

func NewResponse(message interface{}) *Response

NewResponse wraps a generated response message.

func (*Response) Any

func (r *Response) Any() interface{}

Any returns the concrete response message as an empty interface, so that *Response implements the AnyResponse interface.

func (*Response) Header

func (r *Response) Header() http.Header

Header returns the HTTP headers for this response. Headers beginning with "Triple-" and "Grpc-" are reserved for use by the Triple and gRPC protocols: applications may read them but shouldn't write them.

func (*Response) Trailer

func (r *Response) Trailer() http.Header

Trailer returns the trailers for this response. Depending on the underlying RPC protocol, trailers may be sent as HTTP trailers or a protocol-specific block of in-body metadata.

Trailers beginning with "Triple-" and "Grpc-" are reserved for use by the Triple and gRPC protocols: applications may read them but shouldn't write them.

type Server

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

func NewServer

func NewServer(addr string) *Server

func (*Server) GracefulStop

func (s *Server) GracefulStop(ctx context.Context) error

func (*Server) RegisterBidiStreamHandler

func (s *Server) RegisterBidiStreamHandler(
	procedure string,
	stream func(context.Context, *BidiStream) error,
	options ...HandlerOption,
) error

func (*Server) RegisterClientStreamHandler

func (s *Server) RegisterClientStreamHandler(
	procedure string,
	stream func(context.Context, *ClientStream) (*Response, error),
	options ...HandlerOption,
) error

func (*Server) RegisterCompatStreamHandler

func (s *Server) RegisterCompatStreamHandler(
	procedure string,
	srv interface{},
	typ StreamType,
	streamFunc func(srv interface{}, stream grpc.ServerStream) error,
	options ...HandlerOption,
) error

func (*Server) RegisterCompatUnaryHandler

func (s *Server) RegisterCompatUnaryHandler(
	procedure string,
	method string,
	srv interface{},
	unary MethodHandler,
	options ...HandlerOption,
) error

func (*Server) RegisterServerStreamHandler

func (s *Server) RegisterServerStreamHandler(
	procedure string,
	reqInitFunc func() interface{},
	stream func(context.Context, *Request, *ServerStream) error,
	options ...HandlerOption,
) error

func (*Server) RegisterUnaryHandler

func (s *Server) RegisterUnaryHandler(
	procedure string,
	reqInitFunc func() interface{},
	unary func(context.Context, *Request) (*Response, error),
	options ...HandlerOption,
) error

func (*Server) Run

func (s *Server) Run() error

func (*Server) Stop

func (s *Server) Stop() error

type ServerStream

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

ServerStream is the handler's view of a server streaming RPC.

It's constructed as part of Handler invocation, but doesn't currently have an exported constructor.

func (*ServerStream) Conn

Conn exposes the underlying StreamingHandlerConn. This may be useful if you'd prefer to wrap the connection in a different high-level API.

func (*ServerStream) ResponseHeader

func (s *ServerStream) ResponseHeader() http.Header

ResponseHeader returns the response headers. Headers are sent with the first call to Send.

Headers beginning with "Triple-" and "Grpc-" are reserved for use by the Triple and gRPC protocols. Applications shouldn't write them.

func (*ServerStream) ResponseTrailer

func (s *ServerStream) ResponseTrailer() http.Header

ResponseTrailer returns the response trailers. Handlers may write to the response trailers at any time before returning.

Trailers beginning with "Triple-" and "Grpc-" are reserved for use by the Triple and gRPC protocols. Applications shouldn't write them.

func (*ServerStream) Send

func (s *ServerStream) Send(msg interface{}) error

Send a message to the client. The first call to Send also sends the response headers.

type ServerStreamForClient

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

ServerStreamForClient is the client's view of a server streaming RPC.

It's returned from Client.CallServerStream, but doesn't currently have an exported constructor function.

func (*ServerStreamForClient) Close

func (s *ServerStreamForClient) Close() error

Close the receive side of the stream.

func (*ServerStreamForClient) Conn

Conn exposes the underlying StreamingClientConn. This may be useful if you'd prefer to wrap the connection in a different high-level API.

func (*ServerStreamForClient) Err

func (s *ServerStreamForClient) Err() error

Err returns the first non-EOF error that was encountered by Receive.

func (*ServerStreamForClient) Msg

func (s *ServerStreamForClient) Msg() interface{}

Msg returns the most recent message unmarshaled by a call to Receive.

func (*ServerStreamForClient) Receive

func (s *ServerStreamForClient) Receive(msg interface{}) bool

Receive advances the stream to the next message, which will then be available through the Msg method. It returns false when the stream stops, either by reaching the end or by encountering an unexpected error. After Receive returns false, the Err method will return any unexpected error encountered. todo(DMwangnima): add classic usage

func (*ServerStreamForClient) ResponseHeader

func (s *ServerStreamForClient) ResponseHeader() http.Header

ResponseHeader returns the headers received from the server. It blocks until the first call to Receive returns.

func (*ServerStreamForClient) ResponseTrailer

func (s *ServerStreamForClient) ResponseTrailer() http.Header

ResponseTrailer returns the trailers received from the server. Trailers aren't fully populated until Receive() returns an error wrapping io.EOF.

type Spec

type Spec struct {
	StreamType       StreamType
	Procedure        string // for example, "/acme.foo.v1.FooService/Bar"
	IsClient         bool   // otherwise we're in a handler
	IdempotencyLevel IdempotencyLevel
}

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

If you're using Protobuf, protoc-gen-triple-go generates a constant for the fully-qualified Procedure corresponding to each RPC in your schema.

type StreamType

type StreamType uint8

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

const (
	StreamTypeUnary  StreamType = 0b00
	StreamTypeClient StreamType = 0b01
	StreamTypeServer StreamType = 0b10
	StreamTypeBidi              = StreamTypeClient | StreamTypeServer
)

type StreamingClientConn

type StreamingClientConn interface {
	// Spec and Peer must be safe to call concurrently with all other methods.
	Spec() Spec
	Peer() Peer

	// Send, RequestHeader, and CloseRequest may race with each other, but must
	// be safe to call concurrently with all other methods.
	Send(interface{}) error
	RequestHeader() http.Header
	CloseRequest() error

	// Receive, ResponseHeader, ResponseTrailer, and CloseResponse may race with
	// each other, but must be safe to call concurrently with all other methods.
	Receive(interface{}) error
	ResponseHeader() http.Header
	ResponseTrailer() http.Header
	CloseResponse() error
}

StreamingClientConn is the client's view of a bidirectional message exchange. Interceptors for streaming RPCs may wrap StreamingClientConn.

StreamingClientConn write request headers to the network with the first call to Send. Any subsequent mutations are effectively no-ops. When the server is done sending data, the StreamingClientConn's Receive method returns an error wrapping io.EOF. Clients should check for this using the standard library's errors.Is or IsEnded. If the server encounters an error during processing, subsequent calls to the StreamingClientConn's Send method will return an error wrapping io.EOF; clients may then call Receive to unmarshal the error.

Headers and trailers beginning with "Triple-" and "Grpc-" are reserved for use by the gRPC and Triple protocols: applications may read them but shouldn't write them.

StreamingClientConn implementations provided by this module guarantee that all returned errors can be cast to *Error using the standard library's errors.As.

In order to support bidirectional streaming RPCs, all StreamingClientConn implementations must support limited concurrent use. See the comments on each group of methods for details.

type StreamingClientFunc

type StreamingClientFunc func(context.Context, Spec) StreamingClientConn

StreamingClientFunc is the generic signature of a streaming RPC from the client's perspective. Interceptors may wrap StreamingClientFuncs.

type StreamingHandlerConn

type StreamingHandlerConn interface {
	Spec() Spec
	Peer() Peer

	Receive(interface{}) error
	RequestHeader() http.Header
	ExportableHeader() http.Header

	Send(interface{}) error
	ResponseHeader() http.Header
	ResponseTrailer() http.Header
}

StreamingHandlerConn is the server's view of a bidirectional message exchange. Interceptors for streaming RPCs may wrap StreamingHandlerConns.

Like the standard library's http.ResponseWriter, StreamingHandlerConns write response headers to the network with the first call to Send. Any subsequent mutations are effectively no-ops. Handlers may mutate response trailers at any time before returning. When the client has finished sending data, Receive returns an error wrapping io.EOF. Handlers should check for this using the standard library's errors.Is.

Headers and trailers beginning with "Triple-" and "Grpc-" are reserved for use by the gRPC and Triple protocols: applications may read them but shouldn't write them.

StreamingHandlerConn implementations provided by this module guarantee that all returned errors can be cast to *Error using the standard library's errors.As.

StreamingHandlerConn implementations do not need to be safe for concurrent use.

type StreamingHandlerFunc

type StreamingHandlerFunc func(context.Context, StreamingHandlerConn) error

StreamingHandlerFunc is the generic signature of a streaming RPC from the handler's perspective. Interceptors may wrap StreamingHandlerFuncs.

type UnaryFunc

type UnaryFunc func(context.Context, AnyRequest, AnyResponse) error

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

The type of the request and response structs depend on the codec being used. When using Protobuf, request.Any() and response.Any() will always be [proto.Message] implementations.

type UnaryHandlerFunc

type UnaryHandlerFunc func(ctx context.Context, request AnyRequest) (AnyResponse, error)

UnaryHandlerFunc is the generic signature of a unary RPC from the handler's perspective. Interceptors may wrap UnaryHandlerFuncs.

type UnaryInterceptorFunc

type UnaryInterceptorFunc func(UnaryFunc) UnaryFunc

UnaryInterceptorFunc is a simple Interceptor implementation that only wraps unary RPCs from client's perspective. It has no effect on server side unary RPC and streaming RPCs.

Example
logger := log.New(os.Stdout, "" /* prefix */, 0 /* flags */)
loggingInterceptor := triple.UnaryInterceptorFunc(
	func(next triple.UnaryFunc) triple.UnaryFunc {
		return triple.UnaryFunc(func(ctx context.Context, request triple.AnyRequest, response triple.AnyResponse) error {
			logger.Println("calling:", request.Spec().Procedure)
			logger.Println("request:", request.Any())
			err := next(ctx, request, response)
			if err != nil {
				logger.Println("error:", err)
			} else {
				logger.Println("response:", response.Any())
			}
			return err
		})
	},
)
client := pingv1connect.NewPingServiceClient(
	examplePingServer.Client(),
	examplePingServer.URL(),
	triple.WithInterceptors(loggingInterceptor),
)
if err := client.Ping(context.Background(), triple.NewRequest(&pingv1.PingRequest{Number: 42}), triple.NewResponse(&pingv1.PingResponse{})); err != nil {
	logger.Println("error:", err)
	return
}
Output:

calling: /connect.ping.v1.PingService/Ping
request: number:42
response: number:42

func (UnaryInterceptorFunc) WrapStreamingClient

func (f UnaryInterceptorFunc) WrapStreamingClient(next StreamingClientFunc) StreamingClientFunc

WrapStreamingClient implements Interceptor with a no-op.

func (UnaryInterceptorFunc) WrapStreamingHandler

func (f UnaryInterceptorFunc) WrapStreamingHandler(next StreamingHandlerFunc) StreamingHandlerFunc

WrapStreamingHandler implements Interceptor with a no-op.

func (UnaryInterceptorFunc) WrapUnary

func (f UnaryInterceptorFunc) WrapUnary(next UnaryFunc) UnaryFunc

WrapUnary implements Interceptor by applying the interceptor function.

func (UnaryInterceptorFunc) WrapUnaryHandler

func (f UnaryInterceptorFunc) WrapUnaryHandler(next UnaryHandlerFunc) UnaryHandlerFunc

WrapUnaryHandler implements Interceptor with a no-op.

Directories

Path Synopsis
internal
assert
Package assert is a minimal assert package using reflection.
Package assert is a minimal assert package using reflection.
gen/proto/connect/ping/v1/pingv1connect
The connect.ping.v1 package contains an echo service designed to test the connect-go implementation.
The connect.ping.v1 package contains an echo service designed to test the connect-go implementation.

Jump to

Keyboard shortcuts

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