yarp

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

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

Go to latest
Published: May 7, 2022 License: LGPL-3.0 Imports: 16 Imported by: 1

README

yarp

YARP (Yet Another RPC Protocol) is a simple serialization format and RPC protocol. YARP is:

  • Lighter than JSON
  • Lighter than HTTP
  • Heavier than Protocol Buffers/gRPC, Cap'n'Proto, and MessagePack

And it does not intend to replace any of the former. YARP does not use sticky connections, making it load-balancer-friendly, and provides cleaner autogenerated code.

Why should I use YARP?

You shouldn't. YARP is a proof of concept and currently considered quite unstable; This may change over time, as it begins being used on real projects, but it still does not provide features found in Protobufs/gRPC. You are, however, welcome to use it as you deem fit. Feel free to open an issue in case you stumble in an odd behaviour.

How do I use YARP?

YARP is composed of three distinct parts:

  1. Message and Service Definitions
  2. Autogenerated code
  3. Services implementation

YARP's IDL is quite similar to Protobuf's, making it familiar out-of-box. First, define messages and services:

package io.libyarp;

message RandomBytesRequest {
    desired_length int8 = 0; # Fields indexes begin at zero.
}

message RandomBytesResponse {
    @repeated data uint8 = 0;
}

service RandomBytesService {
    generate_random_bytes(RandomBytesRequest) -> RandomBytesResponse;
}

Then, provide the definition to yarpc:

$ yarpc random_bytes_service.yarp --lang go --package rbs --out rbs.yarp.go

Finally, implement the service:

package rbs

import "context"
import "math/rand"
import "github.com/libyarp/yarp"

type RandomBytesImplementation struct{}

func (r RandomBytesImplementation) GenerateRandomBytes(ctx context.Context, headers yarp.Header, req *RandomBytesRequest) (*RandomBytesResponse, yarp.Header, error) {
	data := make([]byte, req.DesiredLength)
	if _, err := rand.Read(data); err != nil {
	    return nil, nil, err	
    }
    return &RandomBytesResponse{Data: data}, nil, nil	
}

func main() {
	s := yarp.NewServer("127.0.0.1:9027")
	srv := RandomBytesImplementation{}
	RegisterMessages()
	RegisterRandomBytesService(s, &srv)
	err := s.Start()
	if err != nil {
		// ...
	}
}

Protocol Documentation

TBW

Documentation

Index

Constants

View Source
const (
	// ErrorKindInternalError indicates that an internal error prevented the
	// server from performing the requested operation.
	ErrorKindInternalError = 0

	// ErrorKindManagedError indicates that the server returned a user-defined
	// error. See Identifier and UserData fields, and consult the service's
	// documentation for further information.
	ErrorKindManagedError = 1

	// ErrorKindRequestTimeout indicates that the server reached a timeout while
	// waiting for the client to transmit headers.
	ErrorKindRequestTimeout = 2

	// ErrorKindUnimplementedMethod indicates that the server does not implement
	// the requested method.
	ErrorKindUnimplementedMethod = 3

	// ErrorKindTypeMismatch indicates that the contract between the server and
	// client is out-of-sync, since they could not agree on a type for either
	// a request or response.
	ErrorKindTypeMismatch = 4

	// ErrorKindUnauthorized indicates that the server refused to perform an
	// operation due to lack of authorization. See Identifier and UserData
	// fields, along with the service's documentation for further information.
	ErrorKindUnauthorized = 5

	// ErrorKindBadRequest indicates that the server refused to continue the
	// operation due to a problem with the incoming request. See Identifier and
	// UserData fields, along with the service's documentation for further
	// information.
	ErrorKindBadRequest = 6
)

Variables

View Source
var ErrCorruptStream = fmt.Errorf("corrupt stream")

ErrCorruptStream indicates that the stream being processed is corrupt.

View Source
var ErrDuplicatedFieldIndex = fmt.Errorf("duplicated field index")

ErrDuplicatedFieldIndex indicates that a given structure contains one or more fields sharing the same index tag.

View Source
var ErrFieldGap = fmt.Errorf("structs must have no gaps between field indexes")

ErrFieldGap indicates that a given structure contains one or more gaps between fields indexes. The field list indexes must be contiguous.

View Source
var ErrIncompatibleStruct = fmt.Errorf("incompatible structure to encode; structures must implement StructValuer")

ErrIncompatibleStruct indicates that a struct that does not implement the StructValuer interface was provided.

View Source
var ErrIncompleteStruct = fmt.Errorf("incomplete structure definition; missing *Structure field")

ErrIncompleteStruct indicates that a struct lacking a *Structure field was provided.

View Source
var ErrInvalidTag = fmt.Errorf("invalid index tag")

ErrInvalidTag indicates that a structure tag contains an invalid value that could not be parsed as a number.

View Source
var ErrInvalidType = fmt.Errorf("invalid type in stream")

ErrInvalidType indicates that an invalid type was encountered in the stream, possibly indicating that it is corrupted.

View Source
var ErrMinFieldNotZero = fmt.Errorf("minimum field index should be zero")

ErrMinFieldNotZero indicates that a given structure does not have a field beginning at zero.

View Source
var ErrNonHomogeneousArray = fmt.Errorf("only homogeneous arrays are supported")

ErrNonHomogeneousArray indicates that an operation was attempted against a non-homogeneous array or slice. Only homogeneous arrays are supported by YARP.

View Source
var ErrServerClosed = fmt.Errorf("server closed")

ErrServerClosed indicates that the server was closed. This error is returned by Server's Start and StartListener methods when Shutdown is called.

View Source
var ErrSizeTooLarge = fmt.Errorf("size is too large")

ErrSizeTooLarge indicates that a message is either too large, or its stream is corrupted. The size limitation of 2GB was arbitrarily imposed to detect faulty messages.

View Source
var ErrUnknownStructType = fmt.Errorf("unknown struct type")

ErrUnknownStructType indicates that the message being parsed refers to an unknown struct type.

View Source
var ErrWantsStreamed = fmt.Errorf("method requires a streamed response")

ErrWantsStreamed indicates that the client and server implementation is possibly out-of-sync, since the server intends to return a streamed response whilst the client wants a single one.

Functions

func Encode

func Encode(v interface{}) (ret []byte, err error)

Encode takes an arbitrary value and encodes it into a byte slice.

func RegisterStructType

func RegisterStructType(v ...StructValuer)

RegisterStructType works like TryRegisterStructType, but panics instead of returning an error.

func TryRegisterStructType

func TryRegisterStructType(v ...StructValuer) error

TryRegisterStructType takes an arbitrary number of StructValuer instances, validates them, and registers them to be able to decode streams into their respective types. Returns an error in case a struct is invalid.

Types

type Client

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

func NewClient

func NewClient(address string, opts ...Option) *Client

func (*Client) DoRequest

func (c *Client) DoRequest(ctx context.Context, request Request, v interface{}) (interface{}, map[string]string, error)

func (*Client) DoRequestStreamed

func (c *Client) DoRequestStreamed(ctx context.Context, request Request, v interface{}) (<-chan interface{}, map[string]string, error)

type Error

type Error struct {
	Kind       ErrorKind
	Headers    Header
	Identifier string
	UserData   map[string]string
}

Error represents a handled error from the server or an underlying component. Error contains special fields that may be used to diagnose and/or handle the error, such as Kind (see ErrorKind documentation), an optional set of Headers, an optional Identifier provided by the service implementation, and an optional list of UserData key-values with potentially relevant data regarding the error. For further information, refer to the service's documentation.

func IsManagedError

func IsManagedError(err error) (bool, Error)

IsManagedError indicates whether a given error value can be converted to an Error instance, and returns it, in case conversion is possible.

func (*Error) Decode

func (e *Error) Decode(re io.Reader) error

func (Error) Encode

func (e Error) Encode() ([]byte, error)

func (Error) Error

func (e Error) Error() string

type ErrorKind

type ErrorKind uint

ErrorKind indicates one of the possible errors returned in a YARP stream.

type Header map[string]string

Header represents a list of headers present in requests and responses. It may be important to notice that headers manipulated by Get, Set, and Del have their keys automatically standardized using MIME Header standard. For further information regarding that conversion, see textproto.CanonicalMIMEHeaderKey.

func (Header) Clone

func (h Header) Clone() Header

Clone clones the current Header, returning a new instance.

func (Header) Del

func (h Header) Del(key string)

Del removes a given key from the header list

func (Header) Get

func (h Header) Get(key string) string

Get either returns a header with a given key, or an empty string, in case it is absent.

func (Header) Set

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

Set inserts or replaces a given key in the header map with a given value.

type IncompatibleTypeError

type IncompatibleTypeError struct {
	Received interface{}
	Wants    reflect.Type
}

IncompatibleTypeError indicates that the server returned an unexpected type as a response for a method.

func (IncompatibleTypeError) Error

func (i IncompatibleTypeError) Error() string

type MapValue

type MapValue struct {
	Keys   []interface{}
	Values []interface{}
}

MapValue represents a map that has not been transformed into a map[T]U. For each Keys[n], the associated value can be obtained through Values[n].

type Middleware

type Middleware func(req *RPCRequest) (*RPCRequest, error)

Middleware is a simple function that takes an RPCRequest, and either returns the same request and no error, in case the server should continue processing it, or an error, in case the server should stop processing it. Middlewares may update fields and the context present in the RPCRequest object, and return an updated value to be passed to the rest or the responder chain.

type OneOfValue

type OneOfValue struct {
	Index int
	Data  interface{}
}

OneOfValue represents an oneof value that has not been applied to a struct field. The value includes both the Index of the oneof field that should be set, and a Data value including the value that should be applied to such field.

type Option

type Option func(c *options)

Option represents an arbitrary option to be set on a Client or Server instance. See WithTimeout and WithTLS.

func WithTLS

func WithTLS(config *tls.Config) Option

WithTLS enables TLS support on Client and Server.

func WithTimeout

func WithTimeout(t time.Duration) Option

WithTimeout determines a timeout value for a given Client or Server, and has different meanings depending on where it is used: For Server, indicates how long the server may wait for client to provide headers for a request, disconnecting the client in case it fails to provide headers under that duration. For Client, the value is used as a Dial timeout for the underlying net.Dialer.

type RPCRequest

type RPCRequest struct {
	Method     string
	Identifier uint64
	MethodFQN  string
	Headers    Header
	// contains filtered or unexported fields
}

RPCRequest represents an incoming RPC request. Although fields like Method, and Identifier are available, changing them have no effect other than passing them down to other middlewares. Changing the context using WithContext, however, causes the new context to be provided to the target Handler. The same applies to Headers.

func (RPCRequest) Context

func (r RPCRequest) Context() context.Context

Context returns the context for the current RPCRequest. To update the context, use WithContext to obtain a new RPCRequest instance that must be returned from the Middleware function.

func (RPCRequest) WithContext

func (r RPCRequest) WithContext(ctx context.Context) *RPCRequest

WithContext returns a new RPCRequest instance using the given context as its context.

type Request

type Request struct {
	Method  uint64
	Headers map[string]string
}

Request represents an internal representation of an incoming request through a stream. Method indicates which handler should be called, and Headers contains any metadata sent by a client.

func (*Request) Decode

func (r *Request) Decode(re io.Reader) error

Decode reads from a given io.Reader the required bytes to compose a Request, and sets fields present in the receiver.

func (Request) Encode

func (r Request) Encode() ([]byte, error)

Encode encodes the Request header into a byte slice

type Response

type Response struct {
	Headers map[string]string
	Stream  bool
}

Response indicates the beginning of a response in a YARP stream. The response contains a set of arbitrary headers, followed by a boolean value indicating whether the server will begin to provide a stream response comprised of potentially multiple objects.

func (*Response) Decode

func (r *Response) Decode(re io.Reader) error

Decode reads all required bytes from a given io.Reader and fills the receiver's fields.

func (Response) Encode

func (r Response) Encode() ([]byte, error)

Encode encodes a given Response structure into a byte slice.

type Server

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

Server represents a server object capable of routing incoming connections and requests.

func NewServer

func NewServer(bind string, opts ...Option) *Server

NewServer creates a new YARP Server with a given bind address and options. When using Unix Domain Sockets, bind must begin with `unix://`, followed by the socket's path. Otherwise, an IP:PORT pair is required. For options, see WithTimeout and WithTLS. Returns a Server instance ready to listen for connections.

func (*Server) RegisterHandler

func (s *Server) RegisterHandler(k uint64, n string, handler interface{})

RegisterHandler registers a given handler identified by k, and named by n, having a given handler function. This function is not intended to be used directly by users, but rather for autogenerated code responsible for registering a given service on a Server instance.

func (*Server) Shutdown

func (s *Server) Shutdown(ctx context.Context)

Shutdown prevents the current Server from accepting new connections, and waits until all current clients disconnects, or the provided ctx expires. In case ctx expires before all clients are finished, remaining clients will be forcefully disconnected. Passing a context without a timeout waits indefinitely for clients to finish.

func (*Server) Start

func (s *Server) Start() error

Start creates a new net.Listener for the address provided to NewServer, and invokes StartListener with it. This function always returns an error, that may occur during the net.Listen (bind) operation, during the server execution, or an ErrServerClosed in case the server is shutdown.

func (*Server) StartListener

func (s *Server) StartListener(listener net.Listener) error

StartListener starts the select loop for a given net.Listener and delegates incoming connections to goroutines, invoking all required Middlewares and handler functions. This function always returns an error, be during the server execution, or when Shutdown is called.

func (*Server) Use

func (s *Server) Use(mid Middleware)

Use registers a given Middleware to be executed on new requests.

type StructValuer

type StructValuer interface {
	YarpID() uint64
	YarpPackage() string
	YarpStructName() string
}

StructValuer represents a struct that can be encoded into a YARP stream.

type Structure

type Structure struct {
	UnknownFields []UnknownField
}

Structure contains a list of UnknownFields obtained during the decoding process.

type Type

type Type int

Type represents the types present in a YARP stream.

const (
	// Invalid represents an unknown or corrupt type.
	Invalid Type = iota

	// Void represents a void (empty) type.
	Void

	// Scalar represents all signed and unsigned integer values, along with
	// booleans.
	Scalar

	// Float represents both 32 and 64-bit float values.
	Float

	// Array represents a list of a single type.
	Array

	// Struct represents a user-defined structure.
	Struct

	// String represents a UTF-8 character array
	String

	// Map represents an associative array between two types.
	Map

	// OneOf represents a field containing one of several possible types.
	OneOf
)

func Decode

func Decode(r io.Reader) (t Type, ret interface{}, err error)

Decode takes an io.Reader and attempts to decode it as either a primitive type, or a registered message. Decode returns an error in case the provided stream contains an unregistered message. Decode does not close r.

func (Type) String

func (i Type) String() string

type UnknownField

type UnknownField struct {
	Index int
	Type  Type
	Data  interface{}
}

UnknownField represents a field present in a stream, but not handled by the known structure type.

Jump to

Keyboard shortcuts

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