chirp

package module
v0.0.0-...-8154db2 Latest Latest
Warning

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

Go to latest
Published: May 3, 2024 License: BSD-3-Clause Imports: 14 Imported by: 3

README

chirp

This repository defines Chirp, a lightweight remote procedure call protocol suitable for use over stream-oriented local transport mechanisms such as sockets and pipes. The packet format is byte-oriented and uses fixed header formats to minimize the amount of bit-level manipulation necessary to encode and decode packets.

The specification and its implementation are still in development and should not be considered ready for production use.

Documentation

Overview

Package chirp implements the Chirp v0 protocol.

Chirp is a lightweight remote procedure call protocol. Peers exchange binary packets over a shared reliable channel. The protocol is intended to be easy to implement in a variety of different languages and to perform efficiently over stream-oriented local transport mechanisms such as sockets and pipes. The packet format is byte-oriented and uses fixed header formats to minimize the amount of bit-level manipulation necessary to encode and decode packets.

Peers

The core type defined by this package is the Peer. Peers can simultaneously initiate and and service calls with another peer over a Channel.

To create a new, unstarted peer:

p := chirp.NewPeer()

To start the service routine, call the Start method with a channel connected to another peer:

p.Start(ch)

The peer runs until its Stop method is called, the channel is closed by the remote peer, or a protocol fatal error occurs. Call Wait to wait for the peer to exit an return its status:

if err := p.Wait(); err != nil {
   log.Fatalf("Peer failed: %v", err)
}

Channels

The Channel interface defines the ability to send and receive packets as defined by the Chirp specification. A Channel implementation must allow concurrent use by one sender and one receiver.

The channel package provides some basic implementations of this interface.

Calls

A call is a request-response exchange between two peers, consisting of a request and a corresponding response. This is the primary communication between peers. The peer that initiates the call is the caller, the peer that responds is the callee. Calls may propagate in either direction.

To define method handlers for inbound calls on the peer, use the Handle method to register a handler for the method ID:

func echo(ctx context.Context, req *chirp.Request) ([]byte, error) {
   return req.Data, nil
}

p.Handle("do-a-thing", echo)

To issue a call to the remote peer, use the Call method:

rsp, err := p.Call(ctx, "do-a-thing", []byte("some data"))
if err != nil {
   log.Fatalf("Call failed: %v", err)
}

Errors returned by p.Call have concrete type *chirp.CallError.

To call a method directly on the local peer, use the Exec method:

rsp, err := p.Exec(ctx, "do-a-thing", []byte("some data"))
if err != nil {
   log.Fatalf("Exec failed: %v", err)
}

Exec does not send any packets to the remote peer unless the method handler does so internally.

Custom Packet Handlers

To handle packet types other than Request, Response, and Cancel, the caller can use the SendPacket and HandlePacket methods of the Peer. SendPacket allows the caller to send an arbitrary packet to the peer. Peers that do not understand a packet type will silently discard it (per the spec).

HandlePacket registers a callback that will be invoked when a packet is received matching the specified type. If the callback reports an error or panics, it is treated as protocol fatal.

Index

Constants

View Source
const MaxMethodLen = 255

MaxMethodLen is the maximum permitted length for a method name.

Variables

This section is empty.

Functions

func SplitAddress

func SplitAddress(s string) (network, address string)

SplitAddress parses an address string to guess a network type and target.

The assignment of a network type uses the following heuristics:

If s does not have the form [host]:port, the network is assigned as "unix". The network "unix" is also assigned if port == "", port contains characters other than ASCII letters, digits, and "-", or if host contains a "/".

Otherwise, the network is assigned as "tcp". Note that this function does not verify whether the address is lexically valid.

Types

type CallError

type CallError struct {
	ErrorData
	Err      error     // nil for service errors
	Response *Response // set if a the error came from a call response
}

CallError is the concrete type of errors reported by the Call method of a Peer. For service errors, the Err field is nil and the ErrorData contains the error details. For errors arising from a response, the Response field contains the complete response message.

func (*CallError) Error

func (c *CallError) Error() string

Error satisfies the error interface.

func (*CallError) Unwrap

func (c *CallError) Unwrap() error

Unwrap reports the underlying error of c. If c.Err == nil, this is nil.

type Cancel

type Cancel struct {
	RequestID uint32
}

Cancel is the payload format for a Chirp v0 cancel request packet.

func (*Cancel) Decode

func (c *Cancel) Decode(data []byte) error

Decode decodes data into a Chirp v0 cancel payload.

func (Cancel) Encode

func (c Cancel) Encode() []byte

Encode encodes the cancel request data in binary format.

func (Cancel) String

func (c Cancel) String() string

String returns a human-friendly rendering of the cancellation.

type Channel

type Channel interface {
	// Send the packet in binary format to the receiver.
	Send(*Packet) error

	// Receive the next available packet from the channel.
	Recv() (*Packet, error)

	// Close the channel, causing any pending send or receive operations to
	// terminate and report an error. After a channel is closed, all further
	// operations on it must report an error.
	Close() error
}

A Channel is a reliable ordered stream of packets shared by two peers.

The methods of an implementation must be safe for concurrent use by one sender and one receiver.

type ErrorData

type ErrorData struct {
	Code    uint16
	Message string
	Data    []byte
}

ErrorData is the response data format for a service error response.

An ErrorData value satisfies the error interface, allowing a handler to return one to control the error code and ancillary data reported.

func (*ErrorData) Decode

func (e *ErrorData) Decode(data []byte) error

Decode decodes data into a Chirp v0 error data payload.

func (ErrorData) Encode

func (e ErrorData) Encode() []byte

Encode encodes the error data in binary format.

func (ErrorData) Error

func (e ErrorData) Error() string

Error implements the error interface, allowing an ErrorData value to be used as an error. This can be used by method handlers to control the error code and auxiliary data reported to the caller.

type Handler

type Handler func(context.Context, *Request) ([]byte, error)

A Handler processes a request from the remote peer. A handler can obtain the peer from its context argument using the ContextPeer helper.

By default, the error reported by a handler is returned to the caller with error code 0 and the text of the error as its message. A handler may return a value of concrete type ErrorData or *ErrorData to control the error code, message, and auxiliary error data.

type Packet

type Packet struct {
	Protocol byte
	Type     PacketType
	Payload  []byte
}

Packet is the parsed format of a Chirp v0 packet.

func (Packet) Encode

func (p Packet) Encode() []byte

Encode encodes p in binary format.

func (*Packet) ReadFrom

func (p *Packet) ReadFrom(r io.Reader) (int64, error)

ReadFrom reads a packet from r in binary format. It satisfies io.ReaderFrom.

func (*Packet) String

func (p *Packet) String() string

String returns a human-friendly rendering of the packet.

func (*Packet) WriteTo

func (p *Packet) WriteTo(w io.Writer) (int64, error)

WriteTo writes the packet to w in binary format. It satisfies io.WriterTo.

type PacketHandler

type PacketHandler func(context.Context, *Packet) error

A PacketHandler processes a packet from the remote peer. A packet handler can obtain the peer from its context argument using the ContextPeer helper. Any error reported by a packet handler is protocol fatal.

type PacketInfo

type PacketInfo struct {
	*Packet      // the packet being logged
	Sent    bool // whether the packet was sent (true) or received (false)
}

A PacketInfo combines a packet and a flag indicating whether the packet was sent or received.

func (PacketInfo) String

func (p PacketInfo) String() string

type PacketLogger

type PacketLogger func(pkt PacketInfo)

A PacketLogger logs a packet exchanged with the remote peer.

type PacketType

type PacketType byte

PacketType describes the structure type of a Chirp v0 packet.

All packet type values from 0 to 127 inclusive are reserved by the protocol and MUST NOT be used for any other purpose. Packet type values from 128-255 are reserved for use by the implementation.

const (
	PacketRequest  PacketType = 2 // The initial request for a call
	PacketCancel   PacketType = 3 // A cancellation signal for a pending call
	PacketResponse PacketType = 4 // The final response from a call

)

func (PacketType) String

func (p PacketType) String() string

type Peer

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

A Peer implements a Chirp v0 peer. A zero-valued Peer is ready for use, but must not be copied after any method has been called.

Call Start with a channel to start the service routine for the peer. Once started, a peer runs until Stop is called, the channel closes, or a protocol fatal error occurs. Use Wait to wait for the peer to exit and report its status.

Calling Stop terminates all method handlers and calls currently executing.

Call Handle to add handlers to the local peer. Use Call to invoke a call on the remote peer. Both of these methods are safe for concurrent use by multiple goroutines.

func ContextPeer

func ContextPeer(ctx context.Context) *Peer

ContextPeer returns the Peer associated with the given context, or nil if none is defined. The context passed to a method Handler has this value.

func NewPeer

func NewPeer() *Peer

NewPeer constructs a new unstarted peer.

func (*Peer) Call

func (p *Peer) Call(ctx context.Context, method string, data []byte) (_ *Response, err error)

Call sends a call to the remote peer for the specified method and data, and blocks until ctx ends or until the response is received. If ctx ends before the peer replies, the call will be automatically cancelled.

In case of any error, Call returns a nil *Response. The concrete type of the error value is *CallError, and if the error came from the remote peer the corresponding response can be recovered from its Response field.

func (*Peer) Clone

func (p *Peer) Clone() *Peer

Clone returns a new unstarted peer that has the same method handlers, packet handlers, and base context function as p. After cloning, further changes to either peer do not affect the other.

func (*Peer) Exec

func (p *Peer) Exec(ctx context.Context, method string, data []byte) ([]byte, error)

Exec executes the (local) handler on p for the method, if one exists. If no handler is defined for method, Exec reports an internal error with an empty result; otherwise it returns the result of calling the handler with the given data.

func (*Peer) Handle

func (p *Peer) Handle(method string, handler Handler) *Peer

Handle registers a handler for the specified method name. It is safe to call this while the peer is running. Passing a nil Handler removes any handler for the specified method. Handle returns p to permit chaining.

As a special case, if method == "" the handler is called for any request with a method name that does not have a more specific handler registered. If len(method) > MaxMethodLen, Handle will panic.

func (*Peer) HandlePacket

func (p *Peer) HandlePacket(ptype PacketType, handler PacketHandler) *Peer

HandlePacket registers a callback that will be invoked whenever the remote peer sends a packet with the specified type. This method will panic if a reserved packet type is specified. Passing a nil callback removes any handler for the specified packet type. HandlePacket returns p to permit chaining.

Packet handlers are invoked synchronously with the processing of packets sent by the remote peer, and there will be at most one packet handler active at a time. If a packet handler panics or reports an error, it is protocol fatal and will terminate the peer.

func (*Peer) LogPackets

func (p *Peer) LogPackets(log PacketLogger) *Peer

LogPackets registers a callback that will be invoked for each packet exchanged with the remote peer, regardless of type, including packets to be discarded.

Passing a nil callback disables packet logging. The packet logger is invoked synchronously with dispatch, prior to sending or calling a packet handler.

func (*Peer) Metrics

func (p *Peer) Metrics() *expvar.Map

Metrics returns a metrics map for the peer. It is safe for the caller to add additional metrics to the map while the peer is active.

func (*Peer) NewContext

func (p *Peer) NewContext(base func() context.Context) *Peer

NewContext registers a function that will be called to create a new base context for method and packet handlers. This allows request-specific host resources to be plumbed into a handler. If it is not set a background context is used.

func (*Peer) OnExit

func (p *Peer) OnExit(f func(error)) *Peer

OnExit registers a callback to be invoked when the peer terminates. The callback is executed synchronously during shutdown, with the same error value that would be reported by the Wait method.

Only one exit callback can be registered at a time; if f == nil the callback is removed.

func (*Peer) SendPacket

func (p *Peer) SendPacket(ptype PacketType, payload []byte) error

SendPacket sends a packet to the remote peer. Any error is protocol fatal. Any packet type can be sent, including reserved types. The caller is responsible for ensuring such packets have a valid payload.

func (*Peer) Start

func (p *Peer) Start(ch Channel) *Peer

Start starts the peer running on the given channel. The peer runs until the channel closes or a protocol fatal error occurs. Start does not block; call Wait to wait for the peer to exit and report its status.

func (*Peer) Stop

func (p *Peer) Stop() error

Stop closes the channel and terminates the peer. It blocks until the peer has exited and returns its status. After Stop completes it is safe to restart the peer with a new channel.

func (*Peer) Wait

func (p *Peer) Wait() error

Wait blocks until p terminates and reports the error that cause it to stop. After Wait completes it is safe to restart the peer with a new channel.

If p is not running, or has stopped because of a closed channel, Wait returns nil; otherwise it returns the error that triggered protocol failure.

type Request

type Request struct {
	RequestID uint32
	Method    string // at most MaxMethodLen bytes
	Data      []byte
}

Request is the payload format for a Chirp v0 request packet.

func (*Request) Decode

func (r *Request) Decode(data []byte) error

Decode decodes data into a Chirp v0 request payload.

func (Request) Encode

func (r Request) Encode() []byte

Encode encodes the request data in binary format. This method will panic if r.Method is longer than MaxMethodLen.

func (Request) String

func (r Request) String() string

String returns a human-friendly rendering of the request.

type Response

type Response struct {
	RequestID uint32
	Code      ResultCode
	Data      []byte
}

Response is the payload format for a Chirp v0 response packet.

func (*Response) Decode

func (r *Response) Decode(data []byte) error

Decode decodes data into a Chirp v0 response payload.

func (Response) Encode

func (r Response) Encode() []byte

Encode encodes the response data in binary format.

func (Response) String

func (r Response) String() string

String returns a human-friendly rendering of the response.

type ResultCode

type ResultCode byte

ResultCode describes the result status of a completed call. All result codes not defined here are reserved for future use by the protocol.

const (
	CodeSuccess       ResultCode = 0 // Call completed succesfully
	CodeUnknownMethod ResultCode = 1 // Requested an unknown method
	CodeDuplicateID   ResultCode = 2 // Duplicate request ID
	CodeCanceled      ResultCode = 3 // Call was canceled
	CodeServiceError  ResultCode = 4 // Call failed due to a service error
)

func (ResultCode) String

func (c ResultCode) String() string

Directories

Path Synopsis
Package channel provides implementations of the chirp.Channel interface.
Package channel provides implementations of the chirp.Channel interface.
cmd
chirp
Program chirp is a command-line utility for interacting with Chirp v0 peers.
Program chirp is a command-line utility for interacting with Chirp v0 peers.
Package packet provides support for encoding and decoding binary packet data.
Package packet provides support for encoding and decoding binary packet data.
Package peers provides support code for managing and testing peers.
Package peers provides support code for managing and testing peers.

Jump to

Keyboard shortcuts

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