Documentation
¶
Overview ¶
Package neffos is a cross-platform real-time framework with an expressive, elegant API, written in Go.
Overview ¶
A neffos application is a server and one or more clients exchanging Messages over a websocket connection. Messages are grouped into namespaces, and optionally into rooms within a namespace. The server and the client share the same handler types, so one set of event handlers can run on either side.
Server ¶
New builds a Server from an Upgrader and a ConnHandler:
upgrader := gorilla.DefaultUpgrader
server := neffos.New(upgrader, events)
http.Handle("/", server)
Server implements http.Handler, so it can be mounted at a specific path, wrapped by middleware, or served next to an ordinary HTTP mux. Once it is running, Server.Broadcast, Server.Do, Server.Ask, Server.GetConnections and Server.GetConnectionsByNamespace are all safe to call from any goroutine, including from inside an event handler. One limit applies to Server.Ask there: an Ask aimed at the connection whose handler is running cannot be answered until that handler returns, so give its context a deadline.
Client ¶
Dial connects to a neffos server and returns a Client:
client, err := neffos.Dial(ctx, gorilla.DefaultDialer, "ws://localhost:8080", events)
Client.Connect joins a namespace and returns an NSConn, the value used to send and receive on that namespace. Client.Conn returns the underlying Conn, and Client.NotifyClose, a channel, reports when the connection ends.
Handlers ¶
A ConnHandler is one of Events (event name to callback, for a single unnamed namespace), Namespaces (namespace to Events), WithTimeout (either of those plus timeouts and limits), or a Struct built with NewStruct (a Go struct whose methods become event handlers). JoinConnHandlers combines several into one, which helps when a client and a server share most of their handlers but each add a few of their own. The same ConnHandler value, or an equivalent one, is normally passed to both New and Dial.
Messages ¶
A Message carries a Namespace, an optional Room, an Event name and a Body. NSConn.Emit and Room.Emit send one without waiting for a reply. NSConn.Ask and Conn.Ask send one and block for a reply, a CloseError, or until ctx is done. Returning Reply(body) from an event handler answers an Ask; returning a plain error sends that error back instead. NSConn.Send and Room.Send send an event and report whether the write itself succeeded, rather than whether a handler should keep looping.
Backends ¶
Three backend packages adapt a specific websocket library to the Socket interface neffos needs: gorilla (gorilla/websocket), gobwas (gobwas/ws) and coder (coder/websocket). Each exports a DefaultUpgrader and a DefaultDialer, plus a constructor, Upgrader or Dialer, for a customised instance. The three are interchangeable on the wire: a client built on one backend talks to a server built on another, because every one of them speaks plain websocket text and binary frames.
Timeouts, heartbeat and limits ¶
WithTimeout.ReadTimeout and WriteTimeout bound a read and a write. WithTimeout.PingInterval, above zero, starts a heartbeat: the connection pings the remote side on that interval and closes if no pong arrives within it. WithTimeout.MaxMessageSize, above zero, caps the size of one incoming message; a bigger one closes the connection with CloseMessageTooBig, and Conn.Err then reports ErrMessageTooBig. The heartbeat and the size cap both need a Socket that implements SocketPinger or SocketReadLimiter; the three backend packages do. Struct.SetPingInterval and Struct.SetMaxMessageSize set the same two options for a Struct-based handler.
Closing ¶
Conn.Terminate closes one connection with a close code and a reason; Conn.Close is Terminate(CloseNormalClosure, ""). Conn.Err reports the close error once a connection is closed, or nil while it stays open. Server.Close closes every connection at once and returns. Server.Shutdown does the same, then waits for every connection's reader goroutine, including any event callback still running, to return, or until its ctx is done. Use Shutdown, not Close, when the process itself is exiting.
Scaling out ¶
A StackExchange shares broadcasts and Ask replies across more than one neffos server, through a message broker. The stackexchange/redis and stackexchange/nats subpackages each implement one, on redis publish and subscribe and on nats publish and subscribe. A StackExchange that also implements StackExchangeCloser gets its Close called as part of Server.Close, which releases its broker connections and goroutines. Within one server, NSConn.Broadcast and NSConn.BroadcastOthers send to every local connection directly, without going through a StackExchange.
Index ¶
- Constants
- Variables
- func CloseStatus(err error) int
- func Debugf(format string, args ...any)
- func EnableDebug(printer Printer)
- func IsCloseError(err error) bool
- func IsDisconnectError(err error) bool
- func IsSystemEvent(event string) bool
- func IsTimeoutError(err error) bool
- func IsTryingToReconnect(err error) (ok bool)
- func Marshal(v any) ([]byte, error)
- func RegisterKnownError(err error)
- func Reply(body []byte) error
- func ReplyObject(v any) error
- type Client
- type CloseError
- type Conn
- func (c *Conn) Ask(ctx context.Context, msg Message) (Message, error)
- func (c *Conn) Close()
- func (c *Conn) Connect(ctx context.Context, namespace string) (*NSConn, error)
- func (c *Conn) Decrement(key string) int
- func (c *Conn) DeserializeMessage(msgTyp MessageType, payload []byte) Message
- func (c *Conn) DisconnectAll(ctx context.Context) error
- func (c *Conn) Err() error
- func (c *Conn) HandlePayload(msgTyp MessageType, payload []byte) error
- func (c *Conn) ID() string
- func (c *Conn) Increment(key string) int
- func (c *Conn) Is(connID string) bool
- func (c *Conn) IsClient() bool
- func (c *Conn) IsClosed() bool
- func (c *Conn) Namespace(namespace string) *NSConn
- func (c *Conn) Send(msg Message) error
- func (c *Conn) Server() *Server
- func (c *Conn) Set(key string, value any)
- func (c *Conn) Socket() Socket
- func (c *Conn) String() string
- func (c *Conn) Terminate(code int, reason string)
- func (c *Conn) Value[T any](key string) (T, bool)
- func (c *Conn) WaitConnect(ctx context.Context, namespace string) (*NSConn, error)
- func (c *Conn) WasReconnected() bool
- func (c *Conn) Write(msg Message) bool
- type ConnHandler
- type Dialer
- type EventMatcherFunc
- type Events
- type IDGenerator
- type Marshaler
- type Message
- type MessageHandlerFunc
- type MessageObjectMarshaler
- type MessageObjectUnmarshaler
- type MessageType
- type NSConn
- func (ns *NSConn) Ask(ctx context.Context, event string, body []byte) (Message, error)
- func (ns *NSConn) AskObject[Reply any](ctx context.Context, event string, v any) (Reply, error)
- func (ns *NSConn) Broadcast(msgs ...Message)
- func (ns *NSConn) BroadcastOthers(msgs ...Message)
- func (ns *NSConn) Disconnect(ctx context.Context) error
- func (ns *NSConn) Emit(event string, body []byte) bool
- func (ns *NSConn) EmitBinary(event string, body []byte) bool
- func (ns *NSConn) Instance[T any]() (*T, bool)
- func (ns *NSConn) JoinRoom(ctx context.Context, roomName string) (*Room, error)
- func (ns *NSConn) LeaveAll(ctx context.Context) error
- func (ns *NSConn) Room(roomName string) *Room
- func (ns *NSConn) Rooms() []*Room
- func (ns *NSConn) Send(event string, body []byte) error
- func (ns *NSConn) SendObject(event string, v any) error
- func (ns *NSConn) String() string
- type Namespaces
- type Printer
- type PrinterFunc
- type Room
- type Sender
- type Server
- func (s *Server) Ask(ctx context.Context, msg Message) (Message, error)
- func (s *Server) Broadcast(exceptSender Sender, msgs ...Message)
- func (s *Server) Close()
- func (s *Server) Do(fn func(*Conn), async bool)
- func (s *Server) GetConnections() map[string]*Conn
- func (s *Server) GetConnectionsByNamespace(namespace string) map[string]*NSConn
- func (s *Server) GetTotalConnections() uint64
- func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)
- func (s *Server) Shutdown(ctx context.Context) error
- func (s *Server) Upgrade(w http.ResponseWriter, r *http.Request, socketWrapper func(Socket) Socket, ...) (*Conn, error)
- func (s *Server) UseStackExchange(exc StackExchange) error
- type Socket
- type SocketCloser
- type SocketPinger
- type SocketReadLimiter
- type StackExchange
- type StackExchangeCloser
- type StackExchangeInitializer
- type Struct
- func (s *Struct[T]) Events() Events
- func (s *Struct[T]) GetNamespaces() Namespaces
- func (s *Struct[T]) SetEventMatcher(matcher EventMatcherFunc) *Struct[T]
- func (s *Struct[T]) SetInjector(fn func(nsConn *NSConn) *T) *Struct[T]
- func (s *Struct[T]) SetMaxMessageSize(n int64) *Struct[T]
- func (s *Struct[T]) SetNamespace(namespace string) *Struct[T]
- func (s *Struct[T]) SetPingInterval(d time.Duration) *Struct[T]
- func (s *Struct[T]) SetTimeouts(read, write time.Duration) *Struct[T]
- type StructValue
- func (s *StructValue) Events() Events
- func (s *StructValue) GetNamespaces() Namespaces
- func (s *StructValue) SetEventMatcher(matcher EventMatcherFunc) *StructValue
- func (s *StructValue) SetInjector(fn func(nsConn *NSConn) reflect.Value) *StructValue
- func (s *StructValue) SetMaxMessageSize(n int64) *StructValue
- func (s *StructValue) SetNamespace(namespace string) *StructValue
- func (s *StructValue) SetPingInterval(d time.Duration) *StructValue
- func (s *StructValue) SetTimeouts(read, write time.Duration) *StructValue
- type Unmarshaler
- type Upgrader
- type WithTimeout
Examples ¶
Constants ¶
const ( // OnNamespaceConnect is the event name which its callback is fired right before namespace connect, // if non-nil error then the remote connection's `Conn.Connect` will fail and send that error text. // Connection is not ready to emit data to the namespace. OnNamespaceConnect = "_OnNamespaceConnect" // OnNamespaceConnected is the event name which its callback is fired after namespace successfully connected. // Connection is ready to emit data back to the namespace. OnNamespaceConnected = "_OnNamespaceConnected" // OnNamespaceDisconnect is the event name which its callback is fired when // remote namespace disconnection or local namespace disconnection is happening. // For server-side connections the reply matters, so if error returned then the client-side cannot disconnect yet, // for client-side the return value does not matter. OnNamespaceDisconnect = "_OnNamespaceDisconnect" // if allowed to connect then it's allowed to disconnect as well. // OnRoomJoin is the event name which its callback is fired right before room join. OnRoomJoin = "_OnRoomJoin" // able to check if allowed to join. // OnRoomJoined is the event name which its callback is fired after the connection has successfully joined to a room. OnRoomJoined = "_OnRoomJoined" // able to broadcast messages to room. // OnRoomLeave is the event name which its callback is fired right before room leave. OnRoomLeave = "_OnRoomLeave" // able to broadcast bye-bye messages to room. // OnRoomLeft is the event name which its callback is fired after the connection has successfully left from a room. OnRoomLeft = "_OnRoomLeft" // if allowed to join to a room, then its allowed to leave from it. // OnAnyEvent is the event name which its callback is fired when incoming message's event is not declared to the ConnHandler(`Events` or `Namespaces`). OnAnyEvent = "_OnAnyEvent" // when event no match. // OnNativeMessage is fired on incoming native/raw websocket messages. // If this event defined then an incoming message can pass the check (it's an invalid message format) // with just the Message's Body filled, the Event is "OnNativeMessage" and IsNative always true. // This event should be defined under an empty namespace in order this to work. OnNativeMessage = "_OnNativeMessage" )
const ( CloseNormalClosure = 1000 CloseGoingAway = 1001 CloseProtocolError = 1002 CloseUnsupportedData = 1003 CloseNoStatusReceived = 1005 CloseAbnormalClosure = 1006 CloseInvalidFramePayloadData = 1007 ClosePolicyViolation = 1008 CloseMessageTooBig = 1009 CloseMandatoryExtension = 1010 CloseInternalServerErr = 1011 CloseServiceRestart = 1012 CloseTryAgainLater = 1013 CloseTLSHandshake = 1015 )
Close status codes from RFC 6455 section 7.4 and the IANA registry. Pass them to Conn.Terminate or return them in a CloseError, and compare them with the result of CloseStatus.
const URLParamAsHeaderPrefix = "X-Websocket-Header-"
URLParamAsHeaderPrefix is the prefix that server parses the url parameters as request headers. The client's `URLParamAsHeaderPrefix` must match. Note that this is mostly useful for javascript browser-side clients, nodejs and go client support custom headers by default. No action required from end-developer, exported only for chance to a custom parsing.
Variables ¶
var ( // JSONMarshalOptions are the encoding/json/v2 options the `DefaultMarshaler` uses: // map keys sorted (byte-stable bodies), invalid UTF-8 replaced by U+FFFD instead of // failing the message, and time.Duration as int64 nanoseconds. Everything else is // the v2 default: a nil slice encodes as [] and a nil map as {}, `omitempty` omits // empty JSON values only (use `omitzero` for Go zero values), and <, > and & are // not HTML-escaped. Reassign to change the policy, for example // `json.JoinOptions(neffos.JSONMarshalOptions, json.FormatNilSliceAsNull(true))`. JSONMarshalOptions = json.JoinOptions( json.Deterministic(true), jsontext.AllowInvalidUTF8(true), marshalDurationAsNanos, ) // JSONUnmarshalOptions are the encoding/json/v2 options the `DefaultUnmarshaler` uses: // case-insensitive member names as in encoding/json v1 (with '_' and '-' kept // significant) and time.Duration from int64 nanoseconds. Duplicate object names // and invalid UTF-8 in the input are rejected, which is the v2 default. JSONUnmarshalOptions = json.JoinOptions( unmarshalDurationFromNanos, json.MatchCaseInsensitiveNames(true), jsonv1.MatchCaseSensitiveDelimiter(true), ) )
var ( // DefaultMarshaler encodes message bodies for `Marshal`, `SendObject`, `AskObject` // and `ReplyObject` when the value is not a `MessageObjectMarshaler`. // It is encoding/json/v2 with `JSONMarshalOptions`. Reassign it for another format. DefaultMarshaler Marshaler = func(v any) ([]byte, error) { return json.Marshal(v, JSONMarshalOptions) } // DefaultUnmarshaler decodes message bodies for `Message.Unmarshal` and `Message.As` // when the target is not a `MessageObjectUnmarshaler`. // It is encoding/json/v2 with `JSONUnmarshalOptions`. Reassign it for another format. DefaultUnmarshaler Unmarshaler = func(body []byte, outPtr any) error { return json.Unmarshal(body, outPtr, JSONUnmarshalOptions) } )
var ( // ErrBadNamespace may return from a `Conn#Connect` method when the remote side does not declare the given namespace. ErrBadNamespace = errors.New("bad namespace") // ErrBadRoom may return from a `Room#Leave` method when trying to leave from a not joined room. ErrBadRoom = errors.New("bad room") // ErrWrite may return from any connection's method when the underline connection is closed (unexpectedly). ErrWrite = errors.New("write closed") )
var ErrInvalidPayload = errors.New("invalid payload")
ErrInvalidPayload can be returned by the internal `handleMessage`. In the future it may be exposed by an error listener.
var ErrMessageTooBig = errors.New("message too big")
ErrMessageTooBig is the error Conn.Err reports after the remote side sent a message bigger than WithTimeout.MaxMessageSize.
var ErrServerClosed = errors.New("server closed")
ErrServerClosed is returned by `Server#Upgrade` and `Server#Ask` after `Server#Close` was called.
Functions ¶
func CloseStatus ¶ added in v0.0.25
CloseStatus returns the close code of err when err is or wraps a CloseError, and -1 otherwise.
func Debugf ¶ added in v0.0.6
Debugf prints debug messages to the printer defined on `EnableDebug`. Runs only on debug mode.
func EnableDebug ¶ added in v0.0.4
func EnableDebug(printer Printer)
EnableDebug enables debug mode and sets the printer that receives the debug messages. A nil "printer" prints through a new `log.Logger` on stderr prefixed with "| neffos |".
Note that neffos, currently, uses debug mode only on the build state of the events. Therefore enabling the debugger has zero performance cost on up-and-running servers and clients.
There is no way to disable the debug mode on serve-time.
func IsCloseError ¶
IsCloseError reports whether the "err" is a "closed by the remote host" network connection error.
func IsDisconnectError ¶
IsDisconnectError reports whether the "err" is a timeout or a closed connection error.
func IsSystemEvent ¶
IsSystemEvent reports whether the "event" is a system event, OnNamespaceConnect, OnNamespaceConnected, OnNamespaceDisconnect, OnRoomJoin, OnRoomJoined, OnRoomLeave and OnRoomLeft.
func IsTimeoutError ¶
IsTimeoutError reports whether the "err" is caused by a defined timeout: a net.Error whose Timeout method reports true, os.ErrDeadlineExceeded or context.DeadlineExceeded, wrapped or not.
func IsTryingToReconnect ¶ added in v0.0.2
IsTryingToReconnect reports whether the returning "err" from the `Server#Upgrade` is from a client that was trying to reconnect to the websocket server.
Look the `Conn#WasReconnected` and `Conn#ReconnectTries` too.
func Marshal ¶
Marshal encodes "v" into a Message's Body. A `MessageObjectMarshaler` encodes itself through its `Marshal` method, anything else goes through the `DefaultMarshaler`. A nil "v" gives a nil body and no error.
`NSConn.SendObject`, `Room.SendObject`, `NSConn.AskObject` and `ReplyObject` call Marshal for you; use it directly when a Message is built by hand:
body, err := neffos.Marshal(user)
if err != nil {
return err
}
return c.Send("user", body)
func RegisterKnownError ¶ added in v0.0.2
func RegisterKnownError(err error)
RegisterKnownError registers an error that is "known" to both server and client sides. On the receiving side an incoming error text is matched against every registered known error: if any registered error's text matches (or a registered error implements `ResolveError(errorText string) bool` and returns true), the `Message.Err` field is set to that error value instead of a fresh `errors.New`. This lets event callbacks compare errors with `==` or `errors.Is` across the wire.
RegisterKnownError mutates a package-level slice without synchronization. Call it during program initialization (e.g. in an `init` function), before any `New` or `Dial`. Calling it concurrently with active connections is unsafe.
func Reply ¶
Reply is a special type of custom error which sends a message back to the other side with the exact same incoming Message's Namespace (and Room if specified) except its body which would be the given "body".
func ReplyObject ¶ added in v0.0.25
ReplyObject is `Reply` for a value: it encodes "v" with `Marshal` and replies with the result. When encoding fails the error itself is returned, so the caller's event callback reports it instead of sending a broken body.
Types ¶
type Client ¶
type Client struct {
// ID comes from server, local changes are not reflected,
// use the `Server#IDGenerator` if you want to set a custom logic for ID set.
ID string
// NotifyClose can be optionally registered to notify about the client's disconnect.
// This callback is for the entire client side connection,
// the channel is notified after namespace disconnected and any room left events.
// Don't confuse it with the `OnNamespaceDisconnect` event.
// Usage:
// <- client.NotifyClose // blocks until local `Close` or remote close of connection.
NotifyClose <-chan struct{}
// contains filtered or unexported fields
}
Client is the neffos client. Contains the neffos client-side connection and the ID came from server on acknowledgement process of the `Dial` function. Use its `Connect` to connect to a namespace or `WaitServerConnect` to wait for server to force-connect this client to a namespace.
func Dial ¶
Dial establishes a new neffos client connection. Context "ctx" is used for handshake timeout. Dialer "dial" can be either `gobwas.Dialer/DefaultDialer` or `gorilla.Dialer/DefaultDialer`, custom dialers can be used as well when complete the `Socket` and `Dialer` interfaces for valid client. URL "url" is the endpoint of the neffos server, i.e "ws://localhost:8080/echo". The last parameter, and the most important one is the "connHandler", it can be filled as `Namespaces`, `Events` or `WithTimeout`, same namespaces and events can be used on the server-side as well.
See examples for more.
func (*Client) Close ¶
func (c *Client) Close()
Close method terminates the client-side connection. Forces the client to disconnect from all connected namespaces and leave from all joined rooms, server gets notified.
func (*Client) Conn ¶ added in v0.0.25
Conn returns the client-side connection. Use it to read `Conn.Err` after NotifyClose fires, or to call `Conn.Terminate` and `Conn.Send`.
func (*Client) Connect ¶
Connect method returns a new connected to the specific "namespace" `NSConn` value. The "namespace" should be declared in the `connHandler` of both server and client sides. Returns error if server-side's `OnNamespaceConnect` event callback returns an error.
See `Conn#Connect` for more details.
type CloseError ¶
CloseError can be used to send and close a remote connection in the event callback's return statement.
Code is the close code. Reason is a human-readable explanation, used as the error text when there is no underlying error. A literal such as CloseError{Code: 1008} is valid.
func (CloseError) Error ¶
func (err CloseError) Error() string
Error returns "[Code] text", where text is the underlying error's message or, without one, the Reason.
func (CloseError) Unwrap ¶ added in v0.0.25
func (err CloseError) Unwrap() error
Unwrap returns the underlying error, if any, so errors.Is and errors.As can see through a CloseError.
type Conn ¶
type Conn struct {
// ReconnectTries, if > 0 then this connection is a result of a client-side reconnection,
// see `WasReconnected() bool`.
ReconnectTries int
// contains filtered or unexported fields
}
Conn contains the websocket connection and the neffos communication functionality. Its `Connection` will return a new `NSConn` instance. Each connection can connect to one or more declared namespaces. Each `NSConn` can join to multiple rooms.
func (*Conn) Ask ¶
Ask sends msg to the remote side and blocks until the remote replies, ctx is done or the connection closes.
Always call Ask with a context that has a deadline (context.WithTimeout). A remote that never replies would otherwise block this goroutine until the connection closes.
A context that is already done returns its error without writing anything. On a closed connection, or when the connection closes while waiting, Ask returns a CloseError for which both errors.Is(err, ErrWrite) and IsCloseError(err) are true.
Ask is safe to call from inside an event callback. The reader goroutine is busy running that callback, so Ask reads the socket on its behalf: it dispatches unrelated messages to their events as they arrive and returns once the reply to its own message lands. At most one goroutine reads the socket at any time.
Known limitation: the inside-a-callback state is per connection, not per goroutine. An Ask made from another goroutine while the reader goroutine is running a callback takes the same path, so it can run event callbacks on its own goroutine, concurrently with the reader goroutine and out of order with the frames the reader loop reads next.
func (*Conn) Close ¶
func (c *Conn) Close()
Close force-disconnects the connection from every joined namespace and room and terminates the underlying socket. It is Terminate(CloseNormalClosure, "").
Close is idempotent: subsequent calls are no-ops. Disconnect events (OnNamespaceDisconnect, OnRoomLeave, OnRoomLeft) fire synchronously before the socket is closed. After Close returns, the Conn is no longer usable. A new Dial / Upgrade is required to reconnect.
During server shutdown Close still completes promptly: the goroutine that notifies the dispatch loop selects against s.done.
func (*Conn) Connect ¶
Connect method returns a new connected to the specific "namespace" `NSConn` value. The "namespace" should be declared in the `connHandler` of both server and client sides. If this is a client-side connection then the server-side namespace's `OnNamespaceConnect` event callback MUST return null in order to allow this client-side connection to connect, otherwise a non-nil error is returned instead.
func (*Conn) Decrement ¶ added in v0.0.8
Decrement works like `Set` method. It's just a helper for decrementing integer values. If value does exist, and it's an integer then it decrements it by 1, otherwise the value is overridden to value -1. If value does not exist, then it assumes the default value is 0 and it decrements it by one, the result will be -1.
Calling it twice for example it will set the value to -2, even if doesn't exist before.
Returns the decremented value.
func (*Conn) DeserializeMessage ¶ added in v0.0.6
func (c *Conn) DeserializeMessage(msgTyp MessageType, payload []byte) Message
DeserializeMessage returns a Message from the "payload".
On a client-side connection a stack-exchange marked wait token is kept as it arrived, so the reply echoes it back and the server can route it through the exchange. Server-side connections strip the marker and set Message.FromStackExchange, like the package-level DeserializeMessage.
func (*Conn) DisconnectAll ¶
DisconnectAll method disconnects from all namespaces, `OnNamespaceDisconnect` even will be fired and its `Message.IsLocal` will be true. The remote side gets notified.
func (*Conn) Err ¶ added in v0.0.25
Err returns the error that closed the connection, or nil while it is open.
After Close it is a CloseError with CloseNormalClosure, and after Terminate a CloseError with the given code and reason. When the remote side or the network closed the connection it is the error the socket reported, for example a CloseError carrying the remote close code, ErrMessageTooBig, or a timeout error from the heartbeat. Use CloseStatus to read the code.
Err is set before NotifyClose fires, so it is safe to call right after.
func (*Conn) HandlePayload ¶ added in v0.0.6
func (c *Conn) HandlePayload(msgTyp MessageType, payload []byte) error
HandlePayload fires manually a local event based on the "payload".
func (*Conn) ID ¶
ID method returns the unique identifier of the connection. If this is a server-side connection then this value is the generated one by the `Server#IDGenerator`. If this is a client-side connection then this value is filled on the acknowledgment process which is done on the `Client#Dial`.
func (*Conn) Increment ¶ added in v0.0.8
Increment works like `Set` method. It's just a helper for incrementing integer values. If value does exist, and it's an integer then it increments it by 1, otherwise the value is overridden to value 1. If value does not exist, then it assumes the default value is 0 and it increments it by one, the result will be 1.
Returns the incremented value.
func (*Conn) Is ¶ added in v0.0.6
Is reports whether the "connID" is part of this server's connections and their IDs are equal.
func (*Conn) IsClient ¶
IsClient method reports whether this connections is a client-side connetion.
func (*Conn) IsClosed ¶
IsClosed method reports whether this connection is remotely or manually terminated.
func (*Conn) Namespace ¶
Namespace method returns an already-connected `NSConn` value based on the given "namespace".
func (*Conn) Send ¶ added in v0.0.25
Send sends msg to the remote side and returns nil once the message was handed off to the underlying socket. Otherwise it says why:
- the closed-connection error (see Ask) when the connection is closed,
- ErrBadNamespace when msg.Namespace is not connected,
- ErrBadRoom when msg.Room is set but not joined,
- the socket's own error when the write fails.
A broadcast message that must skip this connection also returns a non-nil error.
Send is safe for concurrent use; the underlying socket implementation serializes writes internally.
func (*Conn) Server ¶
Server returns the backend server for server-side connections, or nil on client-side connections.
func (*Conn) Set ¶ added in v0.0.7
Set stores value under key in this connection's context-scope store. Set is safe for concurrent use, but the stored value is not: callers must synchronize their own access if the value is mutable and shared.
func (*Conn) String ¶
String method simply returns the ID(). Useful for fmt usage and to a connection to be passed on `Server#Broadcast` method to exclude itself from the broadcasted message's receivers.
func (*Conn) Terminate ¶ added in v0.0.25
Terminate closes the connection with the close status code and reason, for example Terminate(ClosePolicyViolation, "banned"). It works like Close, and Close is Terminate(CloseNormalClosure, "").
When the socket is a SocketCloser, the remote side receives a close frame with code and reason, the reason cut to 123 bytes. Otherwise the network connection is closed directly. Err then returns a CloseError with code and reason.
func (*Conn) Value ¶ added in v0.0.25
Value returns the value stored under key as a T. The second result is false when the key is absent or the stored value is not a T. `Value[any](key)` returns whatever was stored.
user, ok := c.Conn.Value[User]("user")
Value is safe for concurrent use.
func (*Conn) WaitConnect ¶
WaitConnect method can be used instead of the `Connect` if the other side force-calls `Connect` to this connection and this side wants to "waits" for that signal.
Nil context means try without timeout, wait until it connects to the specific namespace. Note that, this function will not return an `ErrBadNamespace` if namespace does not exist in the server-side or it's not defined in the client-side, it waits until deadline (if any, or loop forever, so a context with deadline is highly recommended).
WaitConnect returns the closed-connection error (see Ask) as soon as the connection closes.
func (*Conn) WasReconnected ¶ added in v0.0.2
WasReconnected reports whether the current connection is a result of a client-side reconnection. To get the numbers of total retries see the `ReconnectTries` field.
func (*Conn) Write ¶
Write sends msg to the remote side. It returns true if the message was handed off to the underlying socket, false otherwise. It is `Send(msg) == nil`; use Send to learn why a message was not sent.
Write is safe for concurrent use; the underlying socket implementation serializes writes internally.
type ConnHandler ¶
type ConnHandler interface {
GetNamespaces() Namespaces
}
ConnHandler is the interface which namespaces and events can be retrieved through. Built-in ConnHandlers are the `Events`, `Namespaces`, `WithTimeout` and `Struct` (see `NewStruct`). Users of this are the `Dial`(client) and `New` (server) functions.
func JoinConnHandlers ¶
func JoinConnHandlers(connHandlers ...ConnHandler) ConnHandler
JoinConnHandlers combines two or more "connHandlers" and returns a result of a single `ConnHandler` that can be passed on the `New` and `Dial` functions.
Later handlers override earlier ones on event-name collisions within the same namespace.
The settings of `WithTimeout` and `Struct` inputs (timeouts, PingInterval, MaxMessageSize) are kept: the result is then a `WithTimeout`, and a later non-zero setting overrides an earlier one. Without any setting the result is a `Namespaces`.
type Dialer ¶
Dialer is the definition type of a dialer, gorilla or gobwas or custom. It is the second parameter of the `Dial` function.
type EventMatcherFunc ¶ added in v0.0.3
EventMatcherFunc is a type of which a Struct matches the methods with neffos events.
func EventPrefixMatcher ¶ added in v0.0.3
func EventPrefixMatcher(prefix string) EventMatcherFunc
EventPrefixMatcher matches methods to events based on the "prefix".
func EventTrimPrefixMatcher ¶ added in v0.0.3
func EventTrimPrefixMatcher(prefixToTrim string) EventMatcherFunc
EventTrimPrefixMatcher matches methods based on the "prefixToTrim" and events are registered without this prefix.
type Events ¶
type Events map[string]MessageHandlerFunc
Events completes the `ConnHandler` interface. It is a map which its key is the event name and its value the event's callback.
Events type completes the `ConnHandler` itself therefore, can be used as standalone value on the `New` and `Dial` functions to register events on empty namespace as well.
See `Namespaces`, `New` and `Dial` too.
func (Events) GetNamespaces ¶ added in v0.0.5
func (e Events) GetNamespaces() Namespaces
GetNamespaces returns an empty namespace with the "e" Events.
func (Events) On ¶ added in v0.0.8
func (e Events) On(eventName string, msgHandler MessageHandlerFunc)
On is a shortcut of Events { eventName: msgHandler }. It registers a callback "msgHandler" for an event "eventName".
Example ¶
events := make(Events)
events.On(OnNamespaceConnected, nil)
events.On("chat", nil)
fmt.Println(len(events)) // we can't loop them and expect the same order ofc.
Output: 2
type IDGenerator ¶
type IDGenerator func(w http.ResponseWriter, r *http.Request) string
IDGenerator is the type of function that it is used to generate unique identifiers for new connections.
See `Server.IDGenerator`.
var DefaultIDGenerator IDGenerator = func(http.ResponseWriter, *http.Request) string { return uuid.NewV4().String() }
DefaultIDGenerator returns a universal unique identifier for a new connection. It's the default `IDGenerator` for `Server`.
type Marshaler ¶ added in v0.0.25
Marshaler is the type of `DefaultMarshaler`: it encodes a value to a message body.
type Message ¶
type Message struct {
// The Namespace that this message sent to/received from.
Namespace string
// The Room that this message sent to/received from.
Room string
// The Event that this message sent to/received from.
Event string
// The actual body of the incoming/outcoming data.
Body []byte
// The Err contains any message's error, if any.
// Note that server-side and client-side connections can return an error instead of a message from each event callbacks,
// except the clients's force Disconnect which its local event doesn't matter when disconnected manually.
Err error
// When sent by the same connection of the current running server instance.
// This field is serialized/deserialized but it's clean on sending or receiving from a client
// and it's only used on StackExchange feature.
// It's serialized as the first parameter, instead of wait signal, if incoming starts with 0x.
FromExplicit string // the exact Conn's pointer in this server instance.
// Reports whether this message is coming from a stackexchange.
// This field is not exposed and it's not serialized at all, ~local-use only~.
//
// The "wait" field can determinate if this message is coming from a stackexchange using its second char,
// This value set based on "wait" on deserialization when coming from remote side.
// Only server-side can actually set it.
FromStackExchange bool
// To is the connection ID of the receiver, used only when `Server#Broadcast` is called, indeed when we only need to send a message to a single connection.
// The Namespace, Room are still respected at all.
//
// However, sending messages to a group of connections is done by the `Room` field for groups inside a namespace or just `Namespace` field as usual.
// This field is not filled on sending/receiving.
To string
// True when event came from local (i.e client if running client) on force disconnection,
// i.e OnNamespaceDisconnect and OnRoomLeave when closing a conn.
// This field is not filled on sending/receiving.
// Err does not matter and never sent to the other side.
IsForced bool
// True when asking the other side and fire the respond's event (which matches the sent for connect/disconnect/join/leave),
// i.e if a client (or server) onnection want to connect
// to a namespace or join to a room.
// Should be used rarely, state can be checked by `Conn#IsClient() bool`.
// This field is not filled on sending/receiving.
IsLocal bool
// True when user define it for writing, only its body is written as raw native websocket message, namespace, event and all other fields are empty.
// The receiver should accept it on the `OnNativeMessage` event.
// This field is not filled on sending/receiving.
IsNative bool
// if server or client should write using Binary message or if the incoming message was readen as binary.
SetBinary bool
// contains filtered or unexported fields
}
The Message is the structure which describes the incoming and outcoming data. Emitter's "body" argument is the `Message.Body` field. Emitter's return non-nil error is the `Message.Err` field. If native message sent then the `Message.Body` is filled with the body and when incoming native message then the `Message.Event` is the `OnNativeMessage`, native messages are allowed only when an empty namespace("") and its `OnNativeMessage` callback are present.
The the raw data received/sent structured following this order: <wait()>; <namespace>; <room>; <event>; <isError(0-1)>; <isNoOp(0-1)>; <body||error_message>
Internal `serializeMessage` and exported `DeserializeMessage` functions do the job on `Conn#Write`, `NSConn#Emit` and `Room#Emit` calls.
func DeserializeMessage ¶ added in v0.0.7
func DeserializeMessage(msgTyp MessageType, b []byte, allowNativeMessages, shouldHandleOnlyNativeMessages bool) Message
DeserializeMessage accepts a serialized message []byte and returns a neffos Message. When allowNativeMessages only Body is filled and check about message format is skipped.
func (Message) As ¶ added in v0.0.25
As decodes this Message's body into a new value of type T and returns it. A *T that implements `MessageObjectUnmarshaler` decodes through its `Unmarshal` method, anything else through the `DefaultUnmarshaler`:
req, err := msg.As[LoginRequest]()
if err != nil {
return err
}
func (*Message) IsWait ¶ added in v0.0.7
IsWait reports whether this message waits for a response back.
func (*Message) Unmarshal ¶
Unmarshal unmarshals this Message's body to the "outPtr". The "outPtr" must be a pointer to a value that can customize its decoded value by implementing the `MessageObjectUnmarshaler`, otherwise the `DefaultUnmarshaler` will be used instead. A nil "outPtr" returns an error.
type MessageHandlerFunc ¶
MessageHandlerFunc is the definition type of the events' callback. Its error can be written to the other side on specific events, i.e on `OnNamespaceConnect` it will abort a remote namespace connection. See examples for more.
type MessageObjectMarshaler ¶
MessageObjectMarshaler is an optional interface that "objects" can implement to customize their byte representation, see `Marshal` package-level function.
type MessageObjectUnmarshaler ¶
MessageObjectUnmarshaler is an optional interface that "objects" can implement to customize their structure, see `Message.Unmarshal` and `Message.As`.
type MessageType ¶ added in v0.0.11
type MessageType uint8
MessageType is a type for readen and to-send data, helpful to set `msg.SetBinary` to the rest of the clients through a Broadcast, as SetBinary is not part of the deserialization.
const ( // TextMessage is a websocket text message. TextMessage MessageType = iota + 1 // BinaryMessage is a websocket binary message. BinaryMessage )
See `MessageType` definition for details.
func (MessageType) String ¶ added in v0.0.25
func (t MessageType) String() string
String returns "text" or "binary", and "unknown" for any other value.
type NSConn ¶
type NSConn struct {
Conn *Conn
// contains filtered or unexported fields
}
NSConn describes a connection connected to a specific namespace, it emits with the `Message.Namespace` filled and it can join to multiple rooms. A single `Conn` can be connected to one or more namespaces, each connected namespace is described by this structure.
func (*NSConn) Ask ¶
Ask method writes a message to the remote side and blocks until a response or an error received.
func (*NSConn) AskObject ¶ added in v0.0.25
AskObject is `Ask` for values: it encodes "v" with `Marshal`, waits for the reply and decodes the reply's body into a Reply with `Message.As`.
user, err := c.AskObject[User](ctx, "login", credentials)
func (*NSConn) Broadcast ¶ added in v0.0.24
Broadcast sends msgs to every connection, including the sender. Equivalent to `ns.Conn.Server().Broadcast(nil, msgs...)` but reads more clearly inside an event callback. Delivery is at-most-once; see Server.Broadcast for details.
This is a server-side helper: on a client-side NSConn it does nothing.
func (*NSConn) BroadcastOthers ¶ added in v0.0.24
BroadcastOthers sends msgs to every connection except this one. Equivalent to `ns.Conn.Server().Broadcast(ns.Conn, msgs...)`.
This is a server-side helper: on a client-side NSConn it does nothing.
func (*NSConn) Disconnect ¶
Disconnect method sends a disconnect signal to the remote side and fires the local `OnNamespaceDisconnect` event.
func (*NSConn) Emit ¶
Emit method sends a message to the remote side with its `Message.Namespace` filled to this specific namespace. It is `Send(event, body) == nil`.
func (*NSConn) EmitBinary ¶ added in v0.0.12
EmitBinary acts like `Emit` but it sets the `Message.SetBinary` to true and sends the data as binary, the receiver's Message in javascript-side is Uint8Array.
func (*NSConn) Instance ¶ added in v0.0.25
Instance returns the per-connection instance of a dynamic `Struct[T]` handler (one whose T has a `*NSConn` field), created when this connection connected to the namespace. It reports false before that, for a static struct, and when the handler's type is not T.
chat, ok := c.Instance[Chat]()
func (*NSConn) JoinRoom ¶
JoinRoom method can be used to join a connection to a specific room, rooms are dynamic. Returns the joined `Room`.
func (*NSConn) LeaveAll ¶
LeaveAll method sends a remote and local leave room signal `OnRoomLeave` to and for all rooms and fires the `OnRoomLeft` event if succeed.
func (*NSConn) Send ¶ added in v0.0.25
Send sends a message to the remote side with its `Message.Namespace` filled to this specific namespace, and returns nil once it was handed off to the socket. A nil NSConn returns ErrBadNamespace; see `Conn.Send` for the other errors.
func (*NSConn) SendObject ¶ added in v0.0.25
SendObject is `Send` for a value: it encodes "v" with `Marshal` and sends the result as the body. An encoding error is returned and nothing is sent.
return c.SendObject("chat", chatMessage{From: "makis", Text: "hi"})
type Namespaces ¶
Namespaces completes the `ConnHandler` interface. Can be used to register one or more namespaces on the `New` and `Dial` functions. The key is the namespace literal and the value is the `Events`, a map with event names and their callbacks.
See `WithTimeout`, `New` and `Dial` too.
func (Namespaces) GetNamespaces ¶ added in v0.0.5
func (nss Namespaces) GetNamespaces() Namespaces
GetNamespaces just returns the "nss" namespaces.
func (Namespaces) On ¶ added in v0.0.8
func (nss Namespaces) On(namespace, eventName string, msgHandler MessageHandlerFunc) Events
On is a shortcut of Namespaces { namespace: Events: { eventName: msgHandler } }. It registers a callback "msgHandler" for an event "eventName" of the particular "namespace".
Example ¶
nss := make(Namespaces)
nss.On("default", OnNamespaceConnected, nil).
On("chat", nil) // registers on "default"
nss.On("other", "chat", nil)
nss.On("other", "event", nil)
fmt.Println(len(nss))
Output: 2
type Printer ¶ added in v0.0.25
Printer is what `EnableDebug` writes neffos debug messages to. `*log.Logger` and kataras/golog satisfy it; wrap anything else in a `PrinterFunc`.
type PrinterFunc ¶ added in v0.0.25
PrinterFunc adapts a function to the `Printer` interface:
neffos.EnableDebug(neffos.PrinterFunc(func(format string, args ...any) {
slog.Debug(fmt.Sprintf(format, args...))
}))
func (PrinterFunc) Printf ¶ added in v0.0.25
func (f PrinterFunc) Printf(format string, args ...any)
Printf calls f.
type Room ¶
Room describes a connected connection to a room, emits messages with the `Message.Room` filled to the specific room and `Message.Namespace` to the underline `NSConn`'s namespace.
func (*Room) Emit ¶
Emit method sends a message to the remote side with its `Message.Room` filled to this specific room and `Message.Namespace` to the underline `NSConn`'s namespace. It is `Send(event, body) == nil`.
func (*Room) Leave ¶
Leave method sends a remote and local leave room signal `OnRoomLeave` to this specific room and fires the `OnRoomLeft` event if succeed.
func (*Room) Send ¶ added in v0.0.25
Send sends a message to the remote side with its `Message.Room` filled to this specific room and `Message.Namespace` to the underline `NSConn`'s namespace, and returns nil once it was handed off to the socket. A nil Room returns ErrBadRoom; see `Conn.Send` for the other errors.
func (*Room) SendObject ¶ added in v0.0.25
SendObject is `Send` for a value: it encodes "v" with `Marshal` and sends the result as the body to this room. An encoding error is returned and nothing is sent.
type Sender ¶ added in v0.0.25
type Sender interface {
// contains filtered or unexported methods
}
Sender identifies the connection a broadcast must not be sent back to. `*Conn`, `*NSConn`, `*Room` (the connection that joined it) and the value returned by `Exclude` are Senders; the interface is sealed.
func Exclude ¶
Exclude can be passed on `Server.Broadcast` when the caller does not have access to the `Conn`, `NSConn` or `Room` value but has the connection's ID.
Example Code:
nsConn.Conn.Server().Broadcast(
neffos.Exclude("connection_id_here"),
neffos.Message{Namespace: "default", Room: "roomName or empty", Event: "chat", Body: [...]})
When the id belongs to a connection of this server instance the exclusion travels through a `StackExchange` like a `*Conn` does. An id this server does not know is excluded locally only.
type Server ¶
type Server struct {
IDGenerator IDGenerator
StackExchange StackExchange
// If `StackExchange` is set then this field is ignored.
//
// It overrides the default behavior(when no StackExchange is not used)
// which publishes a message independently.
// In short the default behavior doesn't wait for a message to be published to all clients
// before any next broadcast call.
//
// Therefore, if set to true,
// each broadcast call will publish its own message(s) by order.
SyncBroadcaster bool
// FireDisconnectAlways will allow firing the `OnDisconnect` server's
// event even if the connection wasimmediately closed from the `OnConnect` server's event
// through `Close()` or non-nil error.
// See https://github.com/kataras/neffos/issues/41
//
// Defaults to false.
FireDisconnectAlways bool
// OnUpgradeError can be optionally registered to catch upgrade errors.
OnUpgradeError func(err error)
// OnConnect can be optionally registered to be notified for any new neffos client connection,
// it can be used to force-connect a client to a specific namespace(s) or to send data immediately or
// even to cancel a client connection and dissalow its connection when its return error value is not nil.
// Don't confuse it with the `OnNamespaceConnect`, this callback is for the entire client side connection.
OnConnect func(c *Conn) error
// OnDisconnect can be optionally registered to notify about a connection's disconnect.
// Don't confuse it with the `OnNamespaceDisconnect`, this callback is for the entire client side connection.
// During Close and Shutdown, OnDisconnect may run on the closing goroutine
// concurrently with the dispatch loop; protect shared state in the callback.
OnDisconnect func(c *Conn)
// contains filtered or unexported fields
}
Server is the neffos server. Keeps the `IDGenerator` which can be customized, by default it's the `DefaultIDGenerator` which generates connections unique identifiers using the uuid/v4.
Callers can optionally register callbacks for connection, disconnection and errored. Its most important methods are `ServeHTTP` which is used to register the server on a specific endpoint and `Broadcast` and `Close`. Use the `New` function to create a new server, server starts automatically, no further action is required.
func New ¶
func New(upgrader Upgrader, connHandler ConnHandler) *Server
New constructs and returns a new neffos server. Listens to incoming connections automatically, no further action is required from the caller. The second parameter is the "connHandler", it can be filled as `Namespaces`, `Events` or `WithTimeout`, same namespaces and events can be used on the client-side as well, Use the `Conn#IsClient` on any event callback to determinate if it's a client-side connection or a server-side one.
See examples for more.
func (*Server) Ask ¶ added in v0.0.7
Ask broadcasts msg and blocks until a matching reply arrives or ctx is canceled.
If msg.To is set, the reply comes from that specific connection. Otherwise Ask returns the first reply received from any connection that handles the event.
Always call Ask with a context that has a deadline (context.WithTimeout). A connection that goes silent will otherwise pin this goroutine forever.
When `StackExchange` is configured, the wait is routed through it so the reply can come from a different neffos instance.
func (*Server) Broadcast ¶
Broadcast publishes msgs to every connection that is allowed to receive them. It does not block: the message is queued for the connection's writer, so this method is safe to call as frequently as needed.
At-most-once semantics: when a connection's outbox is saturated or the connection is mid-close, that connection silently drops the message. There is no per-message delivery acknowledgement; if delivery confirmation is required, use Ask instead.
If exceptSender is non-nil, msgs are not sent back to that connection. The argument may be a *Conn, *NSConn, *Room (its connection), or the result of Exclude(connID).
When `StackExchange` is configured, msgs are published through it. When `SyncBroadcaster` is true, the call enqueues to the dispatch loop so broadcasts preserve a strict order at the cost of throughput.
func (*Server) Close ¶
func (s *Server) Close()
Close terminates the server and all of its connections, client connections are getting notified. Close is idempotent: subsequent calls are no-ops.
Close closes every connection with CloseGoingAway and the reason "server closed", all of them at the same time, and waits for them: each connection's OnNamespaceDisconnect / OnRoomLeave callbacks and the server's OnDisconnect run once per connection before this method returns. The OnNamespaceDisconnect / OnRoomLeave callbacks of different connections may run concurrently, as they already can in normal operation. OnDisconnect may run on the closing goroutine concurrently with the dispatch loop; protect shared state in the callback.
It then signals the internal dispatch loop to exit and (when the configured StackExchange implements StackExchangeCloser) releases its broker connections. Close does not wait for event callbacks that are still running; use Shutdown for that. After Close, Upgrade and Ask return ErrServerClosed, and Do and Broadcast return without doing anything.
func (*Server) Do ¶
Do loops through all connected connections and fires "fn" once per connection. Callers can use this to manipulate connections outside an event callback. fn may call `GetConnections` and `GetConnectionsByNamespace`.
Do not perform long work inside fn: it runs on the server's dispatch goroutine and delays other Do calls, disconnect notifications and SyncBroadcaster broadcasts until it returns.
If async is true Do returns immediately; otherwise it blocks until every connection has been processed. After `Close`, Do returns without calling fn.
func (*Server) GetConnections ¶
GetConnections can be used as an alternative way to retrieve all connected connections to the server on a specific time point. Do not use this function frequently, it is not designed to be fast or cheap, use it for debugging or logging every 'x' time.
It is safe for concurrent use, including from inside a `Do` callback.
func (*Server) GetConnectionsByNamespace ¶
GetConnectionsByNamespace can be used as an alternative way to retrieve all connected connections to a specific "namespace" on a specific time point. Do not use this function frequently, it is not designed to be fast or cheap, use it for debugging or logging every 'x' time.
It is safe for concurrent use, including from inside a `Do` callback.
func (*Server) GetTotalConnections ¶
GetTotalConnections returns the total amount of the connected connections to the server, it's fast and can be used as frequently as needed.
func (*Server) ServeHTTP ¶
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP completes the `http.Handler` interface, it should be passed on a http server's router to serve this neffos server on a specific endpoint.
func (*Server) Shutdown ¶ added in v0.0.25
Shutdown closes the server like Close, with CloseGoingAway and the reason "server shutting down", and then waits until every connection's reader goroutine has returned, which includes any event callback still running. It gives up when ctx is done and returns ctx.Err(); otherwise it returns nil. The StackExchange is closed after the wait, or when ctx gives up.
Do not call Shutdown from inside an event callback or OnConnect / OnDisconnect: it would wait for the callback that called it, until ctx is done.
func (*Server) Upgrade ¶
func (s *Server) Upgrade( w http.ResponseWriter, r *http.Request, socketWrapper func(Socket) Socket, customIDGen IDGenerator, ) (*Conn, error)
Upgrade handles the connection, same as `ServeHTTP` but it can accept a socket wrapper and a "customIDGen" that overrides the server's IDGenerator and it does return the connection or any errors.
func (*Server) UseStackExchange ¶ added in v0.0.7
func (s *Server) UseStackExchange(exc StackExchange) error
UseStackExchange can be used to add one or more StackExchange to the server. Returns a non-nil error when "exc" completes the `StackExchangeInitializer` interface and its `Init` failed.
Read more at the `StackExchange` type's docs.
type Socket ¶
type Socket interface {
// NetConn returns the underline net connection.
NetConn() net.Conn
// Request returns the http request value.
Request() *http.Request
// ReadData reads binary or text messages from the remote connection.
ReadData(timeout time.Duration) (body []byte, typ MessageType, err error)
// WriteBinary sends a binary message to the remote connection.
WriteBinary(body []byte, timeout time.Duration) error
// WriteText sends a text message to the remote connection.
WriteText(body []byte, timeout time.Duration) error
}
Socket is the interface that an underline protocol implementation should implement.
type SocketCloser ¶ added in v0.0.25
type SocketCloser interface {
// Close sends a close frame carrying code and reason, then closes the
// underlying connection. timeout bounds the write of the close frame.
//
// Conn calls Close while other goroutines may be inside ReadData,
// WriteText or WriteBinary, so the close frame write must be serialised
// with the socket's other writes.
Close(code int, reason string, timeout time.Duration) error
}
SocketCloser is implemented by a Socket that can close the websocket connection with a status code and a reason.
type SocketPinger ¶ added in v0.0.25
type SocketPinger interface {
// Ping sends a ping frame. A timeout above zero also waits up to that
// long for the pong and returns a timeout error when none arrives. A zero
// timeout only sends the ping.
//
// Conn calls Ping from its heartbeat goroutine while other goroutines may
// be inside ReadData, WriteText or WriteBinary, so the ping write must be
// serialised with the socket's other writes. The pong arrives through the
// goroutine inside ReadData.
Ping(timeout time.Duration) error
}
SocketPinger is implemented by a Socket that can send websocket pings.
type SocketReadLimiter ¶ added in v0.0.25
type SocketReadLimiter interface {
// SetReadLimit sets the largest message, in bytes, the socket accepts.
// A bigger message makes ReadData return ErrMessageTooBig after the
// socket sends a close frame with CloseMessageTooBig. n <= 0 removes
// the cap.
SetReadLimit(n int64)
}
SocketReadLimiter is implemented by a Socket that can cap the size of incoming messages.
type StackExchange ¶ added in v0.0.6
type StackExchange interface {
// OnConnect should prepare the connection's subscriber.
// It's called automatically after the neffos server's OnConnect (if any)
// on incoming client connections.
OnConnect(c *Conn) error
// OnDisconnect should close the connection's subscriber that
// created on the `OnConnect` method.
// It's called automatically when a connection goes offline,
// manually by server or client or by network failure.
OnDisconnect(c *Conn)
// Publish should publish messages through a stackexchange.
// It's called automatically on neffos broadcasting.
Publish(msgs []Message) bool
// Subscribe should subscribe to a specific namespace,
// it's called automatically on neffos namespace connected.
Subscribe(c *Conn, namespace string)
// Unsubscribe should unsubscribe from a specific namespace,
// it's called automatically on neffos namespace disconnect.
Unsubscribe(c *Conn, namespace string) // should close the subscriber.
// Ask should be able to perform a server Ask to a specific client or to all clients
// It blocks until response from a specific client if msg.To is filled,
// otherwise will return on the first responder's reply.
Ask(ctx context.Context, msg Message, token string) (Message, error)
// NotifyAsk should notify and unblock a subscribed connection for this
// specific message, "token" is the neffos wait signal for this message.
NotifyAsk(msg Message, token string) error
}
StackExchange is an optional interface that can be used to change the way neffos sends messages to its clients, i.e communication between multiple neffos servers.
See the "kataras/neffos/stackexchange" subpackage for more details. Real-World example and usage documentation can be found at: "kataras/neffos/_examples/redis".
type StackExchangeCloser ¶ added in v0.0.25
type StackExchangeCloser interface {
// Close releases resources held by the stack exchange (worker goroutines,
// broker connections, etc.). Implementations should be idempotent.
Close() error
}
StackExchangeCloser is an optional interface for a `StackExchange`. If implemented, its Close method is called as part of `Server.Close` so background goroutines and broker connections can be released cleanly.
type StackExchangeInitializer ¶ added in v0.0.7
type StackExchangeInitializer interface {
// Init should initialize a stackexchange, it's optional.
Init(Namespaces) error
}
StackExchangeInitializer is an optional interface for a `StackExchange`. It contains a single `Init` method which accepts the registered server namespaces and returns error. It does not called on manual `Server.StackExchange` field set, use the `Server.UseStackExchange` to make sure that this implementation is respected.
type Struct ¶ added in v0.0.3
type Struct[T any] struct { // contains filtered or unexported fields }
Struct is a ConnHandler built from the exported methods of a struct of type T. All fields are unexported, use `NewStruct` instead.
A method named `OnChat` with the signature of a `MessageHandlerFunc`, `func(c *neffos.NSConn, msg neffos.Message) error`, handles the "OnChat" event (see `SetEventMatcher` to change the mapping). System events are matched by name without the leading underscore: `OnNamespaceConnected` handles `_OnNamespaceConnected`.
When T has a field of type `*neffos.NSConn` the struct is dynamic: a new T is created for every connection to the namespace (see `SetInjector`), the field is set to that connection, and the methods take the shorter `func(msg neffos.Message) error` form. Exported fields that are non-zero on the prototype passed to `NewStruct` are copied into each instance; unexported fields are not. Without such a field the struct is static and its methods are registered as plain events with no per-connection cost.
func NewStruct ¶ added in v0.0.3
NewStruct returns a ConnHandler built from the exported methods of *T. "prototype" is the value whose exported, non-zero fields are copied into every per-connection instance of a dynamic struct; for a static struct it is the receiver of every event. A nil prototype, a non-struct T, or a T without exported methods panics, as a programming error at registration time.
type chat struct {
Conn *neffos.NSConn
Users *userStore
}
func (c *chat) OnChat(msg neffos.Message) error { ... }
server := neffos.New(upgrader, neffos.NewStruct(&chat{Users: store}).SetNamespace("default"))
Users of this handler are `New` and `Dial`.
func (*Struct[T]) Events ¶ added in v0.0.4
Events builds and returns the Events. Callers of this method is users that want to add Structs to different namespaces in the same application. When a single namespace is used then this call is unnecessary, the `Struct` is already a fully featured `ConnHandler` by itself.
func (*Struct[T]) GetNamespaces ¶ added in v0.0.5
func (s *Struct[T]) GetNamespaces() Namespaces
GetNamespaces creates and returns Namespaces based on the pointer to struct value provided by the "s".
func (*Struct[T]) SetEventMatcher ¶ added in v0.0.3
func (s *Struct[T]) SetEventMatcher(matcher EventMatcherFunc) *Struct[T]
SetEventMatcher sets an event method matcher which applies to every event except the system events (OnNamespaceConnected, and so on). See `EventPrefixMatcher` and `EventTrimPrefixMatcher`.
func (*Struct[T]) SetInjector ¶ added in v0.0.5
SetInjector sets the function that builds the per-connection instance of a dynamic struct (one with a `*NSConn` field). It is called once per connection to the namespace; neffos sets the `*NSConn` field on the returned value, so the function only fills the application's own dependencies:
neffos.NewStruct(&chat{}).SetInjector(func(c *neffos.NSConn) *chat {
return &chat{Users: store, Log: logger.With("conn", c.Conn.ID())}
})
With an injector set, the prototype's fields are not copied. Static structs never call it.
func (*Struct[T]) SetMaxMessageSize ¶ added in v0.0.25
SetMaxMessageSize caps the size in bytes of one incoming message. Above zero, a bigger message closes the connection with CloseMessageTooBig. It needs a Socket that implements SocketReadLimiter. See `WithTimeout.MaxMessageSize`.
Defaults to 0, no cap except the one of the `Upgrader` or `Dialer`.
func (*Struct[T]) SetNamespace ¶ added in v0.0.3
SetNamespace sets a namespace that this Struct is responsible for, Alterinatively create a method on the controller named `Namespace() string` to retrieve this namespace at build time.
func (*Struct[T]) SetPingInterval ¶ added in v0.0.25
SetPingInterval sets the heartbeat interval. Above zero, the connection pings the remote side every d and closes if no pong arrives within d. It needs a Socket that implements SocketPinger. See `WithTimeout.PingInterval`.
Defaults to 0, no heartbeat.
func (*Struct[T]) SetTimeouts ¶ added in v0.0.4
SetTimeouts sets read and write deadlines on the underlying network connection. After a read or write have timed out, the websocket connection is closed.
Defaults to 0, no timeout except an `Upgrader` or `Dialer` specifies its own values.
type StructValue ¶ added in v0.0.25
type StructValue struct {
// contains filtered or unexported fields
}
StructValue is the reflection-driven ConnHandler behind `Struct`. It is for frameworks that only have the controller as a `reflect.Value` (iris mvc builds its controllers through a dependency injection container). Application code uses `NewStruct`, which is typed. All fields are unexported, use `NewStructValue`.
func NewStructValue ¶ added in v0.0.25
func NewStructValue(v reflect.Value) *StructValue
NewStructValue is `NewStruct` for a controller held as a `reflect.Value`. The value must be a non-nil pointer to a struct with at least one exported method; anything else panics, as a programming error at registration time.
func (*StructValue) Events ¶ added in v0.0.25
func (s *StructValue) Events() Events
Events builds and returns the Events. See `Struct.Events`.
func (*StructValue) GetNamespaces ¶ added in v0.0.25
func (s *StructValue) GetNamespaces() Namespaces
GetNamespaces completes the `ConnHandler` interface. See `Struct.GetNamespaces`.
func (*StructValue) SetEventMatcher ¶ added in v0.0.25
func (s *StructValue) SetEventMatcher(matcher EventMatcherFunc) *StructValue
SetEventMatcher sets the method-to-event matcher. See `Struct.SetEventMatcher`.
func (*StructValue) SetInjector ¶ added in v0.0.25
func (s *StructValue) SetInjector(fn func(nsConn *NSConn) reflect.Value) *StructValue
SetInjector sets the function that builds the per-connection controller instance when the struct has a `*NSConn` field. The returned value must be a pointer to the same struct type; neffos sets its `*NSConn` field afterwards. Static fields are not copied when an injector is set. See `Struct.SetInjector`.
func (*StructValue) SetMaxMessageSize ¶ added in v0.0.25
func (s *StructValue) SetMaxMessageSize(n int64) *StructValue
SetMaxMessageSize caps the size of one incoming message. See `Struct.SetMaxMessageSize`.
func (*StructValue) SetNamespace ¶ added in v0.0.25
func (s *StructValue) SetNamespace(namespace string) *StructValue
SetNamespace sets the namespace this handler is responsible for. See `Struct.SetNamespace`.
func (*StructValue) SetPingInterval ¶ added in v0.0.25
func (s *StructValue) SetPingInterval(d time.Duration) *StructValue
SetPingInterval sets the heartbeat interval. See `Struct.SetPingInterval`.
func (*StructValue) SetTimeouts ¶ added in v0.0.25
func (s *StructValue) SetTimeouts(read, write time.Duration) *StructValue
SetTimeouts sets the read and write deadlines. See `Struct.SetTimeouts`.
type Unmarshaler ¶ added in v0.0.25
Unmarshaler is the type of `DefaultUnmarshaler`: it decodes a message body into "outPtr".
type Upgrader ¶
Upgrader is the definition type of a protocol upgrader, gorilla or gobwas or custom. It is the first parameter of the `New` function which constructs a neffos server.
type WithTimeout ¶
type WithTimeout struct {
ReadTimeout time.Duration
WriteTimeout time.Duration
// PingInterval, when above zero, makes the connection ping the remote
// side every PingInterval and close if no pong arrives within the same
// interval. It needs a Socket that implements SocketPinger; with any
// other Socket it does nothing. Pongs are handled on the reader
// goroutine, so an event callback that runs longer than PingInterval
// delays them and the heartbeat then closes the connection with a timeout.
PingInterval time.Duration
// MaxMessageSize, when above zero, caps the size in bytes of one
// incoming message. A bigger message closes the connection with
// CloseMessageTooBig, and Conn.Err reports ErrMessageTooBig. It needs a
// Socket that implements SocketReadLimiter; with any other Socket it
// does nothing.
MaxMessageSize int64
Namespaces Namespaces
Events Events
}
WithTimeout completes the `ConnHandler` interface. Can be used to register namespaces and events or just events on an empty namespace with Read and Write timeouts, a heartbeat and a message size cap.
See `New` and `Dial`.
func (WithTimeout) GetNamespaces ¶ added in v0.0.5
func (t WithTimeout) GetNamespaces() Namespaces
GetNamespaces returns combined namespaces from "Namespaces" and "Events" fields with read and write timeouts from "ReadTimeout" and "WriteTimeout" fields of "t".
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package coder is the neffos backend for github.com/coder/websocket.
|
Package coder is the neffos backend for github.com/coder/websocket. |
|
stackexchange
|
|
|
nats
Package nats provides a neffos StackExchange that scales neffos servers out through nats publish and subscribe.
|
Package nats provides a neffos StackExchange that scales neffos servers out through nats publish and subscribe. |
|
redis
Package redis provides a neffos StackExchange that scales neffos servers out through redis publish and subscribe.
|
Package redis provides a neffos StackExchange that scales neffos servers out through redis publish and subscribe. |
