Documentation ¶
Overview ¶
Package frisbee is the core package for using the frisbee messaging framework. The frisbee framework is a messaging framework designed around the aspect of "bring your own protocol", and can be used by simply defining your packet types and their accompanying logic.
This package provides methods for defining packet types and logic, as well as functionality for implementing frisbee servers and clients. Useful features like automatic heartbeats and automatic reconnections are provided as well.
In depth documentation and examples can be found at https://loopholelabs.io/docs/frisbee
An Echo Example ¶
As a starting point, a very basic echo server:
package main import ( "github.com/loopholelabs/frisbee-go" "github.com/loopholelabs/frisbee-go/pkg/packet" "github.com/rs/zerolog/log" "os" "os/signal" ) const PING = uint16(10) const PONG = uint16(11) func handlePing(_ *frisbee.Async, incoming *packet.Packet) (outgoing *packet.Packet, action frisbee.Action) { if incoming.Metadata.ContentLength > 0 { log.Printf("Server Received Metadata: %s\n", incoming.Content) incoming.Metadata.Operation = PONG outgoing = incoming } return } func main() { handlerTable := make(frisbee.ServerRouter) handlerTable[PING] = handlePing exit := make(chan os.Signal) signal.Notify(exit, os.Interrupt) s := frisbee.NewServer(":8192", handlerTable, 0) err := s.Start() if err != nil { panic(err) } <-exit err = s.Shutdown() if err != nil { panic(err) } }
And an accompanying echo client:
package main import ( "fmt" "github.com/loopholelabs/frisbee-go" "github.com/loopholelabs/frisbee-go/pkg/packet" "github.com/rs/zerolog/log" "os" "os/signal" "time" ) const PING = uint16(10) const PONG = uint16(11) func handlePong(incoming *packet.Packet) (outgoing *packet.Packet, action frisbee.Action) { if incoming.Metadata.ContentLength > 0 { log.Printf("Client Received Metadata: %s\n", incoming.Content) } return } func main() { handlerTable := make(frisbee.ClientRouter) handlerTable[PONG] = handlePong exit := make(chan os.Signal) signal.Notify(exit, os.Interrupt) c, err := frisbee.NewClient("127.0.0.1:8192", handlerTable) if err != nil { panic(err) } err = c.ConnectAsync() if err != nil { panic(err) } go func() { i := 0 p := packet.Get() p.Metadata.Operation = PING for { p.Write([]byte(fmt.Sprintf("ECHO MESSAGE: %d", i))) p.Metadata.ContentLength = uint32(len(p.Content)) err := c.WritePacket(p) if err != nil { panic(err) } i++ time.Sleep(time.Second) } }() <-exit err = c.Close() if err != nil { panic(err) } }
(Examples taken from https://github.com/loopholelabs/frisbee-examples/)
This example is a simple echo client/server, where the client will repeatedly send packets to the server, and the server will echo them back. Its purpose is to describe the flow of packets from Frisbee Client to Server, as well as give an example of how a Frisbee application must be implemented.
Index ¶
- Constants
- Variables
- type Action
- type Async
- func (c *Async) Close() error
- func (c *Async) CloseChannel() <-chan struct{}
- func (c *Async) Closed() bool
- func (c *Async) ConnectionState() (tls.ConnectionState, error)
- func (c *Async) Error() error
- func (c *Async) Flush() error
- func (c *Async) Handshake() error
- func (c *Async) HandshakeContext(ctx context.Context) error
- func (c *Async) LocalAddr() net.Addr
- func (c *Async) Logger() *zerolog.Logger
- func (c *Async) NewStream(id uint16) (stream *Stream)
- func (c *Async) Raw() net.Conn
- func (c *Async) ReadPacket() (*packet.Packet, error)
- func (c *Async) RemoteAddr() net.Addr
- func (c *Async) SetDeadline(t time.Time) error
- func (c *Async) SetNewStreamHandler(handler NewStreamHandler)
- func (c *Async) SetReadDeadline(t time.Time) error
- func (c *Async) SetWriteDeadline(t time.Time) error
- func (c *Async) WriteBufferSize() int
- func (c *Async) WritePacket(p *packet.Packet) error
- type Client
- func (c *Client) Close() error
- func (c *Client) CloseChannel() <-chan struct{}
- func (c *Client) Closed() bool
- func (c *Client) Connect(addr string, streamHandler ...NewStreamHandler) error
- func (c *Client) Error() error
- func (c *Client) Flush() error
- func (c *Client) FromConn(conn net.Conn, streamHandler ...NewStreamHandler) error
- func (c *Client) Logger() *zerolog.Logger
- func (c *Client) Raw() (net.Conn, error)
- func (c *Client) SetNewStreamHandler(handler NewStreamHandler)
- func (c *Client) Stream(id uint16) *Stream
- func (c *Client) WritePacket(p *packet.Packet) error
- type Conn
- type Handler
- type HandlerTable
- type NewStreamHandler
- type Option
- type Options
- type Server
- func (s *Server) GetHandlerTable() HandlerTable
- func (s *Server) Logger() *zerolog.Logger
- func (s *Server) ServeConn(conn net.Conn)
- func (s *Server) SetBaseContext(f func() context.Context) error
- func (s *Server) SetConcurrency(concurrency uint64)
- func (s *Server) SetHandlerTable(handlerTable HandlerTable) error
- func (s *Server) SetOnClosed(f func(*Async, error)) error
- func (s *Server) SetPreWrite(f func()) error
- func (s *Server) SetStreamHandler(f func(*Async, *Stream)) error
- func (s *Server) Shutdown() error
- func (s *Server) Start(addr string) error
- func (s *Server) StartWithListener(listener net.Listener) error
- type Stream
- type Sync
- func (c *Sync) Close() error
- func (c *Sync) ConnectionState() (tls.ConnectionState, error)
- func (c *Sync) Context() (ctx context.Context)
- func (c *Sync) Error() error
- func (c *Sync) Handshake() error
- func (c *Sync) HandshakeContext(ctx context.Context) error
- func (c *Sync) LocalAddr() net.Addr
- func (c *Sync) Logger() *zerolog.Logger
- func (c *Sync) Raw() net.Conn
- func (c *Sync) ReadPacket() (*packet.Packet, error)
- func (c *Sync) RemoteAddr() net.Addr
- func (c *Sync) SetContext(ctx context.Context)
- func (c *Sync) SetDeadline(t time.Time) error
- func (c *Sync) SetReadDeadline(t time.Time) error
- func (c *Sync) SetWriteDeadline(t time.Time) error
- func (c *Sync) WritePacket(p *packet.Packet) error
Examples ¶
Constants ¶
const ( // NONE is used to do nothing (default) NONE = Action(iota) // CLOSE is used to close the frisbee connection CLOSE )
These are various frisbee actions, used to modify the state of the client or server from a Handler function:
const ( // PING is used to check if a client is still alive PING = uint16(iota) // PONG is used to respond to a PING packets PONG // STREAM is used to request that a new stream be created by the receiver to // receive packets with the same packet ID until a packet with a ContentLength of 0 is received STREAM RESERVED3 RESERVED4 RESERVED5 RESERVED6 RESERVED7 RESERVED8 RESERVED9 )
These are internal reserved packet types, and are the reason you cannot use 0-9 in Handler functions:
const DefaultBufferSize = 1 << 16
DefaultBufferSize is the size of the default buffer
const DefaultStreamBufferSize = 1 << 12
DefaultStreamBufferSize is the default size of the stream buffer.
Variables ¶
var ( DefaultDeadline = time.Second * 5 DefaultPingInterval = time.Millisecond * 500 )
var ( InvalidContentLength = errors.New("invalid content length") ConnectionClosed = errors.New("connection closed") StreamClosed = errors.New("stream closed") InvalidStreamPacket = errors.New("invalid stream packet") ConnectionNotInitialized = errors.New("connection not initialized") InvalidBufferLength = errors.New("invalid buffer length") InvalidHandlerTable = errors.New("invalid handler table configuration, a reserved value may have been used") InvalidOperation = errors.New("invalid operation in packet, a reserved value may have been used") )
These are various frisbee errors that can be returned by the client or server:
var ( // PINGPacket is a pre-allocated Frisbee Packet for PING Packets PINGPacket = &packet.Packet{ Metadata: &metadata.Metadata{ Operation: PING, }, Content: polyglot.NewBuffer(), } // PONGPacket is a pre-allocated Frisbee Packet for PONG Packets PONGPacket = &packet.Packet{ Metadata: &metadata.Metadata{ Operation: PONG, }, Content: polyglot.NewBuffer(), } )
var ( BaseContextNil = errors.New("BaseContext cannot be nil") OnClosedNil = errors.New("OnClosed cannot be nil") PreWriteNil = errors.New("PreWrite cannot be nil") StreamHandlerNil = errors.New("StreamHandler cannot be nil") ListenerNil = errors.New("Listener cannot be nil") )
var DefaultLogger = zerolog.New(io.Discard)
DefaultLogger is the default logger used within frisbee
var (
NotTLSConnectionError = errors.New("connection is not of type *tls.Conn")
)
Functions ¶
This section is empty.
Types ¶
type Action ¶
type Action int
Action is an ENUM used to modify the state of the client or server from a Handler function
NONE: used to do nothing (default) CLOSE: close the frisbee connection SHUTDOWN: shutdown the frisbee client or server
type Async ¶
Async is the underlying asynchronous frisbee connection which has extremely efficient read and write logic and can handle the specific frisbee requirements. This is not meant to be used on its own, and instead is meant to be used by frisbee client and server implementations
func ConnectAsync ¶
func ConnectAsync(addr string, keepAlive time.Duration, logger *zerolog.Logger, TLSConfig *tls.Config, streamHandler ...NewStreamHandler) (*Async, error)
ConnectAsync creates a new TCP connection (using net.Dial) and wraps it in a frisbee connection
func (*Async) CloseChannel ¶
func (c *Async) CloseChannel() <-chan struct{}
CloseChannel returns a channel that can be listened to for a close event on a frisbee connection
func (*Async) ConnectionState ¶
func (c *Async) ConnectionState() (tls.ConnectionState, error)
ConnectionState returns the tls.ConnectionState of a *tls.Conn if the connection is not *tls.Conn then the NotTLSConnectionError is returned
func (*Async) Flush ¶
Flush allows for synchronous messaging by flushing the write buffer and instantly sending packets
func (*Async) Handshake ¶
Handshake performs the tls.Handshake() of a *tls.Conn if the connection is not *tls.Conn then the NotTLSConnectionError is returned
func (*Async) HandshakeContext ¶
HandshakeContext performs the tls.HandshakeContext() of a *tls.Conn if the connection is not *tls.Conn then the NotTLSConnectionError is returned
func (*Async) NewStream ¶ added in v0.7.0
NewStream returns a new stream that can be used to send and receive packets
func (*Async) Raw ¶
Raw shuts off all of frisbee's underlying functionality and converts the frisbee connection into a normal TCP connection (net.Conn)
func (*Async) ReadPacket ¶
ReadPacket is a blocking function that will wait until a Frisbee packet is available and then return it (and its content). In the event that the connection is closed, ReadPacket will return an error.
func (*Async) RemoteAddr ¶
RemoteAddr returns the remote address of the underlying net.Conn
func (*Async) SetDeadline ¶
SetDeadline sets the read and write deadline on the underlying net.Conn
func (*Async) SetNewStreamHandler ¶ added in v0.7.0
func (c *Async) SetNewStreamHandler(handler NewStreamHandler)
SetNewStreamHandler sets the callback handler for new streams.
It's important to note that this handler is called for new streams and if it is not set then stream packets will be dropped.
It's also important to note that the handler itself is called in its own goroutine to avoid blocking the read lop. This means that the handler must be thread-safe.`
func (*Async) SetReadDeadline ¶
SetReadDeadline sets the read deadline on the underlying net.Conn
func (*Async) SetWriteDeadline ¶
SetWriteDeadline sets the write deadline on the underlying net.Conn
func (*Async) WriteBufferSize ¶
WriteBufferSize returns the size of the underlying write buffer (used for internal packet handling and for heartbeat logic)
func (*Async) WritePacket ¶
WritePacket takes a packet.Packet and queues it up to send asynchronously.
If packet.Metadata.ContentLength == 0, then the content array's length must be 0. Otherwise, it is required that packet.Metadata.ContentLength == len(content).
type Client ¶
type Client struct { // PacketContext is used to define packet-specific contexts based on the incoming packet // and is run whenever a new packet arrives PacketContext func(context.Context, *packet.Packet) context.Context // UpdateContext is used to update a handler-specific context whenever the returned // Action from a handler is UPDATE UpdateContext func(context.Context, *Async) context.Context // contains filtered or unexported fields }
Client connects to a frisbee Server and can send and receive frisbee packets
func NewClient ¶
NewClient returns an uninitialized frisbee Client with the registered ClientRouter. The ConnectAsync method must then be called to dial the server and initialize the connection.
Example ¶
handlerTable := make(frisbee.HandlerTable) handlerTable[10] = func(ctx context.Context, incoming *packet.Packet) (outgoing *packet.Packet, action frisbee.Action) { return } logger := zerolog.New(os.Stdout) _, _ = frisbee.NewClient(handlerTable, context.Background(), frisbee.WithLogger(&logger))
Output:
func (*Client) CloseChannel ¶
func (c *Client) CloseChannel() <-chan struct{}
CloseChannel returns a channel that can be listened to see if this client has been closed
func (*Client) Connect ¶
func (c *Client) Connect(addr string, streamHandler ...NewStreamHandler) error
Connect actually connects to the given frisbee server, and starts the reactor goroutines to receive and handle incoming packets. If this function is called, FromConn should not be called.
func (*Client) FromConn ¶
func (c *Client) FromConn(conn net.Conn, streamHandler ...NewStreamHandler) error
FromConn takes a pre-existing connection to a Frisbee server and starts the reactor goroutines to receive and handle incoming packets. If this function is called, Connect should not be called.
func (*Client) Raw ¶
Raw converts the frisbee client into a normal net.Conn object, and returns it. This is especially useful in proxying and streaming scenarios.
func (*Client) SetNewStreamHandler ¶ added in v0.7.0
func (c *Client) SetNewStreamHandler(handler NewStreamHandler)
SetNewStreamHandler sets the callback handler for new streams.
It's important to note that this handler is called for new streams and if it is not set then stream packets will be dropped.
It's also important to note that the handler itself is called in its own goroutine to avoid blocking the read lop. This means that the handler must be thread-safe.
type Conn ¶
type Conn interface { Close() error LocalAddr() net.Addr RemoteAddr() net.Addr ConnectionState() (tls.ConnectionState, error) Handshake() error HandshakeContext(context.Context) error SetDeadline(time.Time) error SetReadDeadline(time.Time) error SetWriteDeadline(time.Time) error WritePacket(*packet.Packet) error ReadPacket() (*packet.Packet, error) Logger() *zerolog.Logger Error() error Raw() net.Conn }
type Handler ¶
type Handler func(ctx context.Context, incoming *packet.Packet) (outgoing *packet.Packet, action Action)
Handler is the handler function called by frisbee for incoming packets of data, depending on the packet's Metadata.Operation field
type HandlerTable ¶
HandlerTable is the lookup table for Frisbee handler functions - based on the Metadata.Operation field of a packet, Frisbee will look up the correct handler for that packet.
type NewStreamHandler ¶ added in v0.7.0
type NewStreamHandler func(*Stream)
type Option ¶
type Option func(opts *Options)
Option is used to generate frisbee client and server options internally
func WithKeepAlive ¶
WithKeepAlive allows users to define TCP keepalive options for the frisbee client or server (use -1 to disable)
func WithLogger ¶
WithLogger sets the logger for the frisbee client or server
func WithOptions ¶
WithOptions allows users to pass in an Options struct to configure a frisbee client or server
type Options ¶
Options is used to provide the frisbee client and server with configuration options.
Default Values:
options := Options { KeepAlive: time.Minute * 3, Logger: &DefaultLogger, }
type Server ¶
type Server struct { // ConnContext is used to define a connection-specific context based on the incoming connection // and is run whenever a new connection is opened ConnContext func(context.Context, *Async) context.Context // PacketContext is used to define a handler-specific contexts based on the incoming packet // and is run whenever a new packet arrives PacketContext func(context.Context, *packet.Packet) context.Context // UpdateContext is used to update a handler-specific context whenever the returned // Action from a handler is UPDATE UpdateContext func(context.Context, *Async) context.Context // contains filtered or unexported fields }
Server accepts connections from frisbee Clients and can send and receive frisbee Packets
func NewServer ¶
func NewServer(handlerTable HandlerTable, opts ...Option) (*Server, error)
NewServer returns an uninitialized frisbee Server with the registered HandlerTable. The Start method must then be called to start the server and listen for connections.
Example ¶
handlerTable := make(frisbee.HandlerTable) handlerTable[10] = func(ctx context.Context, incoming *packet.Packet) (outgoing *packet.Packet, action frisbee.Action) { return } logger := zerolog.New(os.Stdout) _, _ = frisbee.NewServer(handlerTable, frisbee.WithLogger(&logger))
Output:
func (*Server) GetHandlerTable ¶ added in v0.5.3
func (s *Server) GetHandlerTable() HandlerTable
GetHandlerTable gets the handler table for the server.
This function should not be called once the server has started.
func (*Server) ServeConn ¶
ServeConn takes a net.Conn and starts a goroutine to handle it using the Server.
func (*Server) SetBaseContext ¶
SetBaseContext sets the baseContext function for the server. If f is nil, it returns an error.
func (*Server) SetConcurrency ¶ added in v0.6.0
SetConcurrency sets the maximum number of concurrent goroutines that will be created by the server to handle incoming packets.
An important caveat of this is that handlers must always thread-safe if they share resources between connections. If the concurrency is set to a value != 1, then the handlers must also be thread-safe if they share resources per connection.
This function should not be called once the server has started.
func (*Server) SetHandlerTable ¶ added in v0.5.2
func (s *Server) SetHandlerTable(handlerTable HandlerTable) error
SetHandlerTable sets the handler table for the server.
This function should not be called once the server has started.
func (*Server) SetOnClosed ¶
SetOnClosed sets the onClosed function for the server. If f is nil, it returns an error.
func (*Server) SetPreWrite ¶
SetPreWrite sets the preWrite function for the server. If f is nil, it returns an error.
func (*Server) SetStreamHandler ¶ added in v0.7.1
SetStreamHandler sets the streamHandler function for the server. If f is nil, it returns an error.
func (*Server) Shutdown ¶
Shutdown shuts down the frisbee server and kills all the goroutines and active connections
func (*Server) Start ¶
Start will start the frisbee server and its reactor goroutines to receive and handle incoming connections. If the baseContext, ConnContext, onClosed, OnShutdown, or preWrite functions have not been defined, it will use the default functions for these.
func (*Server) StartWithListener ¶ added in v0.7.2
StartWithListener will start the frisbee server and its reactor goroutines to receive and handle incoming connections with a given net.Listener. If the baseContext, ConnContext, onClosed, OnShutdown, or preWrite functions have not been defined, it will use the default functions for these.
type Stream ¶ added in v0.7.0
type Stream struct {
// contains filtered or unexported fields
}
func (*Stream) Close ¶ added in v0.7.0
Close will close the stream and prevent any further reads or writes.
func (*Stream) Conn ¶ added in v0.7.0
Conn returns the connection that the stream is associated with.
func (*Stream) ReadPacket ¶ added in v0.7.0
ReadPacket is a blocking function that will wait until a Frisbee packet is available and then return it (and its content). In the event that the connection is closed, ReadPacket will return an error.
func (*Stream) WritePacket ¶ added in v0.7.0
WritePacket will write the given packet to the stream but the ID and Operation will be overwritten with the stream's ID and the STREAM operation. Packets send to a stream must have a ContentLength greater than 0.
type Sync ¶
Sync is the underlying synchronous frisbee connection which has extremely efficient read and write logic and can handle the specific frisbee requirements. This is not meant to be used on its own, and instead is meant to be used by frisbee client and server implementations
func ConnectSync ¶
func ConnectSync(addr string, keepAlive time.Duration, logger *zerolog.Logger, TLSConfig *tls.Config) (*Sync, error)
ConnectSync creates a new TCP connection (using net.Dial) and wraps it in a frisbee connection
func (*Sync) ConnectionState ¶
func (c *Sync) ConnectionState() (tls.ConnectionState, error)
ConnectionState returns the tls.ConnectionState of a *tls.Conn if the connection is not *tls.Conn then the NotTLSConnectionError is returned
func (*Sync) Error ¶
Error returns the error that caused the frisbee.Sync to close or go into a paused state
func (*Sync) Handshake ¶
Handshake performs the tls.Handshake() of a *tls.Conn if the connection is not *tls.Conn then the NotTLSConnectionError is returned
func (*Sync) HandshakeContext ¶
HandshakeContext performs the tls.HandshakeContext() of a *tls.Conn if the connection is not *tls.Conn then the NotTLSConnectionError is returned
func (*Sync) Raw ¶
Raw shuts off all of frisbee's underlying functionality and converts the frisbee connection into a normal TCP connection (net.Conn)
func (*Sync) ReadPacket ¶
ReadPacket is a blocking function that will wait until a frisbee packet is available and then return it (and its content). In the event that the connection is closed, ReadPacket will return an error.
func (*Sync) RemoteAddr ¶
RemoteAddr returns the remote address of the underlying net.Conn
func (*Sync) SetContext ¶
SetContext allows users to save a context within a connection
func (*Sync) SetDeadline ¶
SetDeadline sets the read and write deadline on the underlying net.Conn
func (*Sync) SetReadDeadline ¶
SetReadDeadline sets the read deadline on the underlying net.Conn
func (*Sync) SetWriteDeadline ¶
SetWriteDeadline sets the write deadline on the underlying net.Conn