Documentation
¶
Overview ¶
Package velaros provides a lightweight, flexible WebSocket framework for Go.
Velaros enables building real-time WebSocket applications with pattern-based message routing, composable middleware, and bidirectional communication patterns.
Key Features ¶
- Pattern-based routing with parameters and wildcards
- Composable middleware for authentication, logging, and more
- Bidirectional communication with Send, Request, and Receive methods
- Context pooling for high performance
- Automatic text/binary message type detection
- Works with any HTTP router/framework via http.Handler interface
Quick Start ¶
Create a router, add middleware, and bind handlers to message paths:
router := velaros.NewRouter()
router.Use(json.Middleware())
router.Bind("/chat/message", func(ctx *velaros.Context) {
var msg ChatMessage
ctx.ReceiveInto(&msg)
ctx.Send(ChatResponse{Status: "received"})
})
http.ListenAndServe(":8080", router)
Message Format ¶
Message format is determined by middleware. The included JSON middleware expects messages with an ID (for request/reply) and path (for routing):
{
"id": "abc123",
"path": "/chat/message",
"username": "alice",
"text": "Hello!"
}
Routing ¶
Routes support exact paths, named parameters, and wildcards:
router.Bind("/users/list", handler) // Exact match
router.Bind("/users/:id", handler) // Named parameter
router.Bind("/files/**", handler) // Wildcard
Middleware ¶
Middleware executes before handlers and can modify context, perform authentication, or short-circuit the chain:
router.Use(func(ctx *velaros.Context) {
log.Printf("Message: %s", ctx.Path())
ctx.Next()
})
Context Storage ¶
Store values at the message level (per-message) or socket level (per-connection):
ctx.Set("requestTime", time.Now()) // Per-message
ctx.SetOnSocket("username", "alice") // Per-connection
For more examples and documentation, see https://github.com/RobertWHurst/velaros
Index ¶
- Variables
- func CtxFree(ctx *Context)
- func CtxMeta(ctx *Context) map[string]any
- type BindType
- type CloseHandler
- type CloseSource
- type ConnectionInfo
- type Context
- func NewContext(sender *Socket, message *InboundMessage, handlers ...any) *Context
- func NewContextWithNode(socket *Socket, message *InboundMessage, firstHandlerNode *HandlerNode) *Context
- func NewContextWithNodeAndMessageType(socket *Socket, message *InboundMessage, firstHandlerNode *HandlerNode, ...) *Context
- func NewSubContextWithNode(ctx *Context, firstHandlerNode *HandlerNode) *Context
- func (c *Context) Close()
- func (c *Context) CloseStatus() (Status, string, CloseSource)
- func (c *Context) CloseWithStatus(status Status, reason string)
- func (c *Context) Data() []byte
- func (c *Context) Deadline() (time.Time, bool)
- func (c *Context) Delete(key string)
- func (c *Context) DeleteFromSocket(key string)
- func (c *Context) Done() <-chan struct{}
- func (c *Context) Err() error
- func (c *Context) Get(key string) (any, bool)
- func (c *Context) GetFromSocket(key string) (any, bool)
- func (c *Context) Headers() http.Header
- func (c *Context) MessageID() string
- func (c *Context) MessageType() MessageType
- func (c *Context) Meta(key string) (any, bool)
- func (c *Context) MustGet(key string) any
- func (c *Context) MustGetFromSocket(key string) any
- func (c *Context) Next()
- func (c *Context) Params() MessageParams
- func (c *Context) Path() string
- func (c *Context) RawData() []byte
- func (c *Context) Receive() ([]byte, error)
- func (c *Context) ReceiveInto(into any) error
- func (c *Context) ReceiveIntoWithContext(ctx context.Context, into any) error
- func (c *Context) ReceiveIntoWithTimeout(into any, timeout time.Duration) error
- func (c *Context) ReceiveWithContext(ctx context.Context) ([]byte, error)
- func (c *Context) ReceiveWithTimeout(timeout time.Duration) ([]byte, error)
- func (c *Context) RemoteAddr() string
- func (c *Context) Request(data any) ([]byte, error)
- func (c *Context) RequestInto(data any, into any) error
- func (c *Context) RequestIntoWithContext(ctx context.Context, data any, into any) error
- func (c *Context) RequestIntoWithTimeout(data any, into any, timeout time.Duration) error
- func (c *Context) RequestWithContext(ctx context.Context, data any) ([]byte, error)
- func (c *Context) RequestWithTimeout(data any, timeout time.Duration) ([]byte, error)
- func (c *Context) Send(data any) error
- func (c *Context) Set(key string, value any)
- func (c *Context) SetMessageData(data []byte)
- func (c *Context) SetMessageID(id string)
- func (c *Context) SetMessageMarshaller(marshaller func(message *OutboundMessage) ([]byte, error))
- func (c *Context) SetMessageMeta(meta map[string]any)
- func (c *Context) SetMessagePath(path string)
- func (c *Context) SetMessageRawData(rawData []byte)
- func (c *Context) SetMessageUnmarshaler(unmarshaler func(message *InboundMessage, into any) error)
- func (c *Context) SetOnSocket(key string, value any)
- func (c *Context) SocketID() string
- func (c *Context) Value(v any) any
- type Handler
- type HandlerFunc
- type HandlerNode
- type InboundMessage
- type MessageParams
- type MessageType
- type OpenHandler
- type OutboundMessage
- type Pattern
- type RouteDescriptor
- type Router
- func (r *Router) Bind(path string, handlers ...any)
- func (r *Router) Handle(ctx *Context)
- func (r *Router) HandleClose(ctx *Context)
- func (r *Router) HandleConnection(info *ConnectionInfo, connection SocketConnection)
- func (r *Router) HandleOpen(ctx *Context)
- func (r *Router) Lookup(handlerOrTransformer any) (*Pattern, bool)
- func (r *Router) Middleware() navaros.HandlerFunc
- func (r *Router) PublicBind(path string, handlers ...any)
- func (r *Router) RouteDescriptors() []*RouteDescriptor
- func (r *Router) ServeHTTP(res http.ResponseWriter, req *http.Request)
- func (r *Router) SetOrigins(origins []string)
- func (r *Router) Use(handlers ...any)
- func (r *Router) UseClose(handlers ...any)
- func (r *Router) UseOpen(handlers ...any)
- type RouterHandler
- type Socket
- func (s *Socket) AddInterceptor(id string, interceptorChan chan *InboundMessage)
- func (s *Socket) Close(status Status, reason string, source CloseSource)
- func (s *Socket) Deadline() (time.Time, bool)
- func (s *Socket) Delete(key string)
- func (s *Socket) Done() <-chan struct{}
- func (s *Socket) Err() error
- func (s *Socket) Get(key string) (any, bool)
- func (s *Socket) GetInterceptor(id string) (chan *InboundMessage, bool)
- func (s *Socket) HandleClose(node *HandlerNode)
- func (s *Socket) HandleNextMessageWithNode(node *HandlerNode) bool
- func (s *Socket) HandleOpen(node *HandlerNode)
- func (s *Socket) Headers() http.Header
- func (s *Socket) ID() string
- func (s *Socket) IsClosed() bool
- func (s *Socket) MustGet(key string) any
- func (s *Socket) RemoteAddr() string
- func (s *Socket) RemoveInterceptor(id string)
- func (s *Socket) Send(messageType MessageType, data []byte) error
- func (s *Socket) Set(key string, value any)
- func (s *Socket) Value(key any) any
- type SocketConnection
- type SocketMessage
- type Status
- type WebSocketConnection
Constants ¶
This section is empty.
Variables ¶
var ErrContextFreed = errors.New("context cannot be used after handler returns - handlers must block until all operations complete")
ErrContextFreed is returned when attempting to use a Context after its handler has returned. Context objects are pooled and reused, so handlers must block until all operations using the context are complete.
Functions ¶
Types ¶
type BindType ¶ added in v1.2.0
type BindType int
BindType indicates the type of handler binding (regular message handler, open lifecycle handler, or close lifecycle handler).
const ( // NormalBindType indicates a regular message handler bound to a path pattern. NormalBindType BindType = iota // OpenBindType indicates a lifecycle handler that runs when a connection opens. OpenBindType // CloseBindType indicates a lifecycle handler that runs when a connection closes. CloseBindType )
type CloseHandler ¶ added in v1.9.0
type CloseHandler interface {
HandleClose(ctx *Context)
}
CloseHandler extends Handler to support connection close lifecycle hooks. When used as middleware (via Use), handlers implementing this interface will automatically have their HandleClose method registered as UseClose middleware.
type CloseSource ¶ added in v1.8.0
type CloseSource int
CloseSource indicates whether a connection close was initiated by the client or server. Use CloseStatus() in UseClose handlers to check who initiated the close and respond accordingly.
const ( ClientCloseSource CloseSource = iota ServerCloseSource )
type ConnectionInfo ¶ added in v1.13.0
ConnectionInfo contains information about a WebSocket connection, including the remote address and HTTP headers from the upgrade request. Used by HandleConnection for custom connection implementations.
type Context ¶
type Context struct {
// Error holds any error that occurred during handler execution, including
// errors from panics (automatically recovered). When Error is set, subsequent
// handlers in the chain are skipped. Error-handling middleware should call
// Next() first, then check this field after handlers execute.
Error error
// ErrorStack contains the stack trace if Error was set by a panic. This is
// useful for debugging and logging. The stack trace has the framework's
// internal frames removed for clarity.
ErrorStack string
// contains filtered or unexported fields
}
Context represents the context of a WebSocket message being processed. It provides access to the message data, connection state, and methods for sending responses. Context implements Go's context.Context interface for integration with standard context-aware code.
Context objects are pooled and reused for performance. Each incoming message gets its own Context instance from the pool, which is returned to the pool after the handler chain completes.
Key capabilities:
- Message access: Path(), Data(), MessageID(), Params()
- Storage: Set/Get for per-message storage, SetOnSocket/GetFromSocket for per-connection storage
- Communication: Send(), Receive()
- Control flow: Next() to continue the handler chain
- Lifecycle: Close(), CloseWithStatus() to terminate the connection
- Error handling: Error, ErrorStack fields for panic recovery
func NewContext ¶
func NewContext(sender *Socket, message *InboundMessage, handlers ...any) *Context
NewContext creates a new Context with the given socket, message, and handlers. The router creates contexts automatically when handling WebSocket connections, so most users should not call this directly.
func NewContextWithNode ¶
func NewContextWithNode(socket *Socket, message *InboundMessage, firstHandlerNode *HandlerNode) *Context
NewContextWithNode creates a Context with a specific handler node. Most users should use NewContext instead.
func NewContextWithNodeAndMessageType ¶
func NewContextWithNodeAndMessageType(socket *Socket, message *InboundMessage, firstHandlerNode *HandlerNode, messageType websocket.MessageType) *Context
NewContextWithNodeAndMessageType creates a Context with a specific handler node and WebSocket message type. This is for internal use by the router. Users should not call this directly.
func NewSubContextWithNode ¶
func NewSubContextWithNode(ctx *Context, firstHandlerNode *HandlerNode) *Context
NewSubContextWithNode creates a sub-context for nested handler chains. This is used internally by the router when mounting routers within routers. Users should not call this directly.
func (*Context) Close ¶ added in v1.2.0
func (c *Context) Close()
Close closes the WebSocket connection with a normal closure status. After calling Close(), the connection will be terminated and no more messages can be sent or received. UseClose handlers will still execute before the connection is fully closed.
func (*Context) CloseStatus ¶ added in v1.5.0
func (c *Context) CloseStatus() (Status, string, CloseSource)
CloseStatus returns the close status code, reason, and source (client or server) for the connection. This is useful in UseClose handlers to determine why and how the connection was closed.
Returns:
- Status: The WebSocket close status code
- string: The close reason (may be empty)
- CloseSource: Whether the close was initiated by the server or client
Example:
router.UseClose(func(ctx *velaros.Context) {
status, reason, source := ctx.CloseStatus()
if source == velaros.ClientCloseSource {
log.Printf("Client closed connection: %d %s", status, reason)
}
})
func (*Context) CloseWithStatus ¶ added in v1.5.0
CloseWithStatus closes the WebSocket connection with a specific status code and reason string. The status and reason are sent to the client as part of the WebSocket close frame. UseClose handlers will still execute before the connection is fully closed.
Common status codes:
- StatusNormalClosure (1000): Normal closure
- StatusGoingAway (1001): Server shutting down
- StatusPolicyViolation (1008): Client violated policy
Example:
ctx.CloseWithStatus(StatusPolicyViolation, "Rate limit exceeded")
func (*Context) Data ¶
Data returns the processed data of the current message after middleware extraction. Middleware may extract this from a nested 'data' field or transform RawData. For the original unmodified data, use RawData(). This method is safe for concurrent use.
func (*Context) Deadline ¶
Deadline returns the time when work done on behalf of this context should be canceled. Returns ok==false when no deadline is set. Part of the context.Context interface.
func (*Context) Delete ¶ added in v1.12.0
Delete removes a value stored at the message level with Set. Values are only accessible within the same message's handler chain. This method is safe for concurrent use.
func (*Context) DeleteFromSocket ¶ added in v1.12.0
DeleteFromSocket removes a value stored at the socket/connection level. This is thread-safe and can be called from handlers processing different messages concurrently on the same connection.
func (*Context) Done ¶
func (c *Context) Done() <-chan struct{}
Done returns a channel that's closed when work done on behalf of this context should be canceled. The channel closes when the WebSocket connection closes or Close() is called. Part of the context.Context interface.
func (*Context) Err ¶
Err returns a non-nil error value after Done is closed. Returns Canceled if the context was canceled or DeadlineExceeded if the deadline passed. Part of the context.Context interface.
func (*Context) Get ¶
Get retrieves a value stored at the message level. Returns the value and true if found, or nil and false if not found. Values are only accessible within the same message's handler chain. This method is safe for concurrent use.
func (*Context) GetFromSocket ¶
GetFromSocket retrieves a value stored at the socket/connection level. Returns the value and true if found, or nil and false if not found. This is thread-safe and can be called from handlers processing different messages concurrently on the same connection.
func (*Context) Headers ¶
Headers returns the HTTP headers from the initial WebSocket upgrade request. These headers persist for the lifetime of the connection and are useful for accessing things like authentication tokens, cookies, or custom headers sent during the handshake.
Example:
token := ctx.Headers().Get("Authorization")
cookie, _ := (&http.Request{Header: ctx.Headers()}).Cookie("session")
func (*Context) MessageID ¶
MessageID returns the ID of the current message. Message IDs are used for request/reply correlation. If the incoming message didn't have an ID, one is automatically generated. This method is safe for concurrent use.
func (*Context) MessageType ¶ added in v1.11.0
func (c *Context) MessageType() MessageType
MessageType returns the WebSocket message type of the current message (MessageText or MessageBinary). This is determined by the underlying WebSocket protocol, not the message content. This method is safe for concurrent use.
func (*Context) Meta ¶ added in v1.15.0
Meta retrieves a value from the message metadata by key. Returns the value and true if the key exists, or nil and false otherwise. Metadata can be used to pass authentication tokens, tracing IDs, or other contextual information alongside message data. This method is safe for concurrent use.
Example:
userId, ok := ctx.Meta("userId")
if ok {
log.Printf("Request from user: %v", userId)
}
func (*Context) MustGet ¶
MustGet retrieves a value stored at the message level. Panics if the key is not found. Use this when the value is expected to exist. This method is safe for concurrent use.
func (*Context) MustGetFromSocket ¶
MustGetFromSocket retrieves a value stored at the socket/connection level. Panics if the key is not found. Use this when the value is expected to exist.
func (*Context) Next ¶
func (c *Context) Next()
Next continues execution to the next handler in the chain. Middleware should call Next() to pass control to subsequent handlers, then perform any post-processing after Next() returns.
If an error is set on the context (via Error field or panic), subsequent handlers are skipped. Next() is safe to call multiple times.
func (*Context) Params ¶
func (c *Context) Params() MessageParams
Params returns a copy of the parameters extracted from the message path based on the matched route pattern. Parameters are defined in patterns using :name syntax. This method is safe for concurrent use.
Example pattern: "/users/:id" Example path: "/users/123" Result: Params().Get("id") returns "123"
func (*Context) Path ¶
Path returns the path of the current message. The path is used for routing and pattern matching. Middleware can modify the path via SetMessagePath(). This method is safe for concurrent use.
func (*Context) RawData ¶ added in v1.18.0
RawData returns the raw byte data of the current message before any middleware processing. This is the unmodified data as received from the WebSocket. This method is safe for concurrent use.
func (*Context) Receive ¶ added in v1.19.0
Receive waits for the next message from the client with the context's message ID and returns the raw data. Blocks until a message arrives, the connection closes, or the context is cancelled.
An interceptor is automatically set up the first time a message with an ID arrives. Subsequent messages with the same ID are intercepted and queued for the handler.
Returns the raw message data and an error if the connection closes or context is cancelled.
Example usage:
ctx.Send(greeting)
for {
data, err := ctx.Receive()
if err != nil {
return
}
ctx.Send(process(data))
}
func (*Context) ReceiveInto ¶ added in v1.19.0
ReceiveInto waits for the next message from the client with the context's message ID and unmarshals it into the provided value. Blocks until a message arrives, the connection closes, or the context is cancelled.
An interceptor is automatically set up the first time a message with an ID arrives. Subsequent messages with the same ID are intercepted and queued for the handler.
Returns an error if the connection closes, context is cancelled, or unmarshalling fails.
Example usage:
ctx.Send(greeting)
for {
var msg UserMessage
if err := ctx.ReceiveInto(&msg); err != nil {
return
}
ctx.Send(process(msg))
}
func (*Context) ReceiveIntoWithContext ¶ added in v1.19.0
ReceiveIntoWithContext is like ReceiveInto but accepts a custom context for cancellation.
func (*Context) ReceiveIntoWithTimeout ¶ added in v1.19.0
ReceiveIntoWithTimeout is like ReceiveInto but allows specifying a custom timeout duration. A timeout of 0 means no timeout (wait indefinitely).
func (*Context) ReceiveWithContext ¶ added in v1.19.0
ReceiveWithContext is like Receive but accepts a custom context for cancellation.
func (*Context) ReceiveWithTimeout ¶ added in v1.19.0
ReceiveWithTimeout is like Receive but allows specifying a custom timeout duration. A timeout of 0 means no timeout (wait indefinitely).
func (*Context) RemoteAddr ¶ added in v1.13.0
RemoteAddr returns the remote address of the client for the WebSocket connection.
func (*Context) Request ¶
Request sends a message to the client and waits for a response, returning the raw response data. This is a convenience method that combines Send() and Receive(). Blocks until a response arrives, the connection closes, or the context is cancelled.
Returns the raw response data and an error if sending fails, the connection closes, or the context is cancelled.
Example usage:
data, err := ctx.Request(query)
if err != nil {
return err
}
process(data)
func (*Context) RequestInto ¶
RequestInto sends a message to the client and waits for a response, unmarshalling the response into the provided value. This is a convenience method that combines Send() and ReceiveInto(). Blocks until a response arrives, the connection closes, or the context is cancelled.
Returns an error if sending fails, the connection closes, context is cancelled, or unmarshalling fails.
Example usage:
var response QueryResponse
if err := ctx.RequestInto(query, &response); err != nil {
return err
}
process(response)
func (*Context) RequestIntoWithContext ¶
RequestIntoWithContext is like RequestInto but accepts a custom context for cancellation.
func (*Context) RequestIntoWithTimeout ¶
RequestIntoWithTimeout is like RequestInto but allows specifying a custom timeout duration. A timeout of 0 means no timeout (wait indefinitely).
func (*Context) RequestWithContext ¶
RequestWithContext is like Request but accepts a custom context for cancellation.
func (*Context) RequestWithTimeout ¶
RequestWithTimeout is like Request but allows specifying a custom timeout duration. A timeout of 0 means no timeout (wait indefinitely).
func (*Context) Send ¶
Send sends a message to the client. If the current message has an ID, the response uses the same ID, enabling the client to correlate the response with their request. If there's no ID, the message is sent without one. This method is safe for concurrent use.
Example:
var req GetUserRequest
ctx.ReceiveInto(&req)
user := getUser(req.UserID)
ctx.Send(GetUserResponse{User: user})
func (*Context) Set ¶
Set stores a value at the message level. Values stored here only exist for the duration of processing this specific message. This is useful for passing data between middleware and handlers in the same message's handler chain. This method is safe for concurrent use.
Example:
ctx.Set("startTime", time.Now())
ctx.Set("requestID", uuid.New())
func (*Context) SetMessageData ¶
SetMessageData replaces the raw message data. This is useful for middleware that needs to transform or filter the message payload before it reaches handlers. This method is safe for concurrent use.
func (*Context) SetMessageID ¶
SetMessageID changes the ID of the current message. This affects routing behavior - the framework will attempt to deliver this message to any registered interceptor (from Request/RequestInto calls) matching this ID. This method is safe for concurrent use.
This is primarily used by middleware to implement custom request/reply patterns.
func (*Context) SetMessageMarshaller ¶
func (c *Context) SetMessageMarshaller(marshaller func(message *OutboundMessage) ([]byte, error))
SetMessageMarshaller sets the function used to marshal outgoing message data. This is typically called by middleware (like JSON middleware) to provide a serialization strategy for Send() and Request() methods. This method is safe for concurrent use.
func (*Context) SetMessageMeta ¶ added in v1.15.0
SetMessageMeta sets the metadata map for the current message. Metadata can be used to pass authentication tokens, tracing IDs, or other contextual information alongside message data. This method is safe for concurrent use.
func (*Context) SetMessagePath ¶
SetMessagePath changes the path of the current message. This affects routing behavior - after the path is changed, the framework will re-evaluate route patterns to find matching handlers. This method is safe for concurrent use.
This is useful for middleware that needs to rewrite or normalize paths before they reach handlers.
func (*Context) SetMessageRawData ¶ added in v1.18.0
SetMessageRawData replaces the raw message data. This is useful for middleware that needs to transform or filter the message payload before it reaches handlers. This method is safe for concurrent use.
func (*Context) SetMessageUnmarshaler ¶
func (c *Context) SetMessageUnmarshaler(unmarshaler func(message *InboundMessage, into any) error)
SetMessageUnmarshaler sets the function used to unmarshal incoming message data. This is typically called by middleware (like JSON middleware) to provide a deserialization strategy for the rest of the handler chain. This method is safe for concurrent use.
func (*Context) SetOnSocket ¶
SetOnSocket stores a value at the socket/connection level. Values stored here persist across all messages for the duration of the WebSocket connection. This is thread-safe and can be called from handlers processing different messages concurrently on the same connection.
Common uses: authentication state, user info, connection metadata.
Example:
ctx.SetOnSocket("userID", "12345")
ctx.SetOnSocket("authenticated", true)
type Handler ¶
type Handler interface {
Handle(ctx *Context)
}
Handler is a handler object interface. Any object that implements this interface can be used as a handler in a handler chain.
type HandlerFunc ¶
type HandlerFunc func(ctx *Context)
HandlerFunc is a function adapter that allows ordinary functions to be used as handlers. This is the most common way to define handlers. For stateful handlers or those needing HandleOpen/HandleClose, implement the Handler interface instead.
type HandlerNode ¶
type HandlerNode struct {
// BindType indicates whether this is a regular message handler, an open
// lifecycle handler, or a close lifecycle handler.
BindType BindType
// Pattern is the route pattern that messages must match for this handler
// to execute. Nil for lifecycle handlers (UseOpen/UseClose) and middleware
// that runs on all paths.
Pattern *Pattern
// Handlers is the list of handler functions to execute when this node matches.
// Multiple handlers can be attached to the same node and will execute in sequence.
Handlers []any
// Next is a pointer to the next handler node in the chain. Forms a linked list
// that the router traverses to find matching handlers.
Next *HandlerNode
}
HandlerNode represents a node in the internal handler chain. This is an internal implementation detail used by the router to organize handlers into a linked list for efficient traversal during message processing.
Each node represents either:
- A message handler bound to a specific path pattern (NormalBindType)
- A connection open lifecycle handler (OpenBindType)
- A connection close lifecycle handler (CloseBindType)
Nodes are created automatically by the router when handlers are registered via Bind(), Use(), UseOpen(), or UseClose().
type InboundMessage ¶
type InboundMessage struct {
// ID is the message identifier, used for request/reply correlation. If the
// incoming message doesn't include an ID, one is automatically generated.
ID string
// Path is the message path used for routing to handlers. Patterns are matched
// against this path to determine which handlers should execute.
Path string
// RawData contains the raw message payload as bytes before any middleware processing.
// This is the unmodified data as received from the WebSocket connection. Middleware
// can parse this and call ctx.SetMessageData() to populate the Data field.
RawData []byte
// Data contains the processed message payload as bytes after middleware extraction.
// Middleware may extract this from a nested 'data' field in the message envelope.
// Handlers can unmarshal this using ctx.ReceiveInto() if middleware has configured
// an unmarshaler.
Data []byte
// Meta contains optional metadata attached to the message. Applications can use
// this to pass authentication tokens, tracing IDs, or other contextual information.
Meta map[string]any
// contains filtered or unexported fields
}
InboundMessage represents a message received from a WebSocket client. The structure and interpretation of these fields depends on the middleware in use (typically JSON middleware which populates these fields from the incoming message format).
Middleware can modify these fields via Context methods (SetMessageID, SetMessagePath, SetMessageData) to transform messages before they reach handlers.
type MessageParams ¶
MessageParams represents the parameters extracted from the message path. Parameters are extracted from the path by matching the message path to the route pattern for the handler node. These may change each time Next is called on the context.
func (MessageParams) Get ¶
func (p MessageParams) Get(key string) string
Get returns the value of a parameter by key. The lookup is case-insensitive (e.g., 'ID' and 'id' match the same parameter). Returns an empty string if the key doesn't exist.
type MessageType ¶ added in v1.11.0
type MessageType = websocket.MessageType
MessageType represents the type of a WebSocket message (text or binary). This is a type alias for websocket.MessageType from the github.com/coder/websocket package.
const ( MessageText MessageType = websocket.MessageText MessageBinary MessageType = websocket.MessageBinary )
type OpenHandler ¶ added in v1.9.0
type OpenHandler interface {
HandleOpen(ctx *Context)
}
OpenHandler extends Handler to support connection open lifecycle hooks. When used as middleware (via Use), handlers implementing this interface will automatically have their HandleOpen method registered as UseOpen middleware.
type OutboundMessage ¶
type OutboundMessage struct {
// ID is the message identifier. When set, it enables the client to correlate
// this message with a previous request they sent. Used by Send() to echo back
// the client's message ID for request/reply correlation, and by Request() to
// track responses.
ID string
// Data is the message payload. This will be marshalled by middleware (typically
// to JSON) before being sent over the WebSocket connection.
Data any
}
OutboundMessage represents a message being sent to a WebSocket client. The marshalling of this structure into bytes is handled by middleware (typically JSON middleware).
type Pattern ¶
type Pattern struct {
// contains filtered or unexported fields
}
Pattern represents a compiled route pattern used for matching message paths. Patterns support static segments ('/users/list'), named parameters ('/users/:id'), wildcards ('/files/**'), and modifiers (:id?, :tags+, :path*). Use NewPattern to create patterns from strings.
func NewPattern ¶
NewPattern creates a pattern from a string. Supported syntax: static segments ('/users'), named parameters (':id'), wildcards ('*', '**'), and modifiers ('?' optional, '+' one or more, '*' zero or more). Examples: '/users/:id', '/files/**', '/api/:version?/users'. Returns an error if the pattern string is invalid.
func (*Pattern) Match ¶
func (p *Pattern) Match(path string) (MessageParams, bool)
Match compares a path to the pattern and returns a map of named parameters extracted from the path as per the pattern. If the path matches the pattern, the second return value will be true. If the path does not match the pattern, the second return value will be false.
func (*Pattern) MatchInto ¶
func (p *Pattern) MatchInto(path string, params *MessageParams) bool
MatchInto is like Match but reuses an existing MessageParams map instead of allocating a new one. The map is cleared before populating with new parameters. Returns true if the path matches the pattern. This is used internally for performance.
func (*Pattern) Path ¶ added in v1.7.0
func (p *Pattern) Path(params MessageParams, wildcards []string) (string, error)
Path creates a path string from the pattern by replacing dynamic segments with the provided parameters. If a required parameter is missing, an error is returned. Optional segments are only included if their parameters are provided. Wildcard segments are replaced with values from the wildcards slice in order. If there are more wildcard segments than values in the slice, an error is returned.
type RouteDescriptor ¶
type RouteDescriptor struct {
Pattern *Pattern
}
RouteDescriptor describes a public route registered with PublicBind. Route descriptors are used by API gateway frameworks for service discovery and routing. Access them via Router.RouteDescriptors().
func (*RouteDescriptor) MarshalJSON ¶
func (r *RouteDescriptor) MarshalJSON() ([]byte, error)
MarshalJSON returns the JSON representation of the route descriptor.
func (*RouteDescriptor) UnmarshalJSON ¶
func (r *RouteDescriptor) UnmarshalJSON(data []byte) error
UnmarshalJSON parses the JSON representation of the route descriptor.
type Router ¶
type Router struct {
// contains filtered or unexported fields
}
Router is the main WebSocket router that handles connection upgrades and message routing. It implements http.Handler for easy integration with Go's standard HTTP servers, and can also be used as middleware with Navaros.
Router supports pattern-based message routing, middleware, lifecycle hooks, and route descriptors for API gateway integration.
func (*Router) Bind ¶
Bind registers handlers for messages matching the specified path pattern. Handlers are executed in order when a message's path matches the pattern. The path pattern supports parameters (:name), wildcards (*), and modifiers.
Example patterns:
router.Bind("/users/list", listUsersHandler)
router.Bind("/users/:id", getUserHandler)
router.Bind("/files/**", fileHandler)
Handlers must be of type Handler, HandlerFunc, or func(*Context). Panics if no handlers are provided or if handlers are of an invalid type.
func (*Router) Handle ¶
Handle implements the Handler interface, allowing the router to be used as a handler in another router's middleware chain. This enables mounting one router inside another for modular routing organization.
func (*Router) HandleClose ¶ added in v1.9.0
HandleClose implements the CloseHandler interface, allowing the router to handle WebSocket connection close events when used as middleware in another router.
func (*Router) HandleConnection ¶ added in v1.11.0
func (r *Router) HandleConnection(info *ConnectionInfo, connection SocketConnection)
HandleConnection allows creating sockets with custom connections and driving the router with them. This is designed for frameworks that provide their own WebSocket implementations and need to integrate with Velaros. Most applications should use ServeHTTP or Middleware instead. The info parameter provides connection metadata, and connection is a SocketConnection implementation.
func (*Router) HandleOpen ¶ added in v1.9.0
HandleOpen implements the OpenHandler interface, allowing the router to handle WebSocket connection open events when used as middleware in another router.
func (*Router) Lookup ¶ added in v1.7.0
Lookup finds the pattern for a specific handler function. This is useful for generating paths from handlers (reverse routing). Returns the pattern as originally bound to the router (e.g., '/api/users/:id') and true if found. Recurses into nested routers. Example: if pattern, ok := router.Lookup(myHandler); ok { path, _ := pattern.Path(params, nil) }
func (*Router) Middleware ¶
func (r *Router) Middleware() navaros.HandlerFunc
Middleware returns a Navaros middleware function that handles WebSocket upgrade requests. This allows the router to be used as middleware in a Navaros HTTP router. If the request is a WebSocket upgrade, it handles the connection. Otherwise, it passes the request to the next handler in the Navaros chain.
func (*Router) PublicBind ¶
PublicBind is like Bind but marks the route as part of the public API. This creates a route descriptor that can be discovered by API gateway frameworks for service discovery and routing. Use this for routes that should be exposed externally, and use Bind for internal-only routes.
Example:
// Public API route - exposed through gateway
router.PublicBind("/api/users/:id", getUserHandler)
// Internal route - not exposed
router.Bind("/internal/health", healthHandler)
Route descriptors can be retrieved via RouteDescriptors() for gateway integration.
func (*Router) RouteDescriptors ¶ added in v1.5.0
func (r *Router) RouteDescriptors() []*RouteDescriptor
RouteDescriptors returns a list of all public route descriptors collected by this router. Route descriptors are generated when PublicBind is used, and they describe the routes that this router can handle. This is useful for API gateway frameworks that need to discover and route to WebSocket services.
func (*Router) ServeHTTP ¶
func (r *Router) ServeHTTP(res http.ResponseWriter, req *http.Request)
ServeHTTP implements the http.Handler interface, allowing the router to be used directly with Go's standard HTTP server. It handles WebSocket upgrade requests and manages the connection lifecycle. If the request is not a WebSocket upgrade request, it returns a 400 Bad Request error.
func (*Router) SetOrigins ¶ added in v1.5.0
SetOrigins configures the allowed origin patterns for WebSocket connections. This is used for CORS-style origin validation during the WebSocket handshake. If not set, all origins are allowed (equivalent to []string{"*"}).
Origin patterns support wildcards, for example:
- "https://example.com" - exact match
- "https://*.example.com" - subdomain wildcard
- "*" - allow all origins (default)
func (*Router) Use ¶
Use registers middleware handlers that execute for all messages. Middleware can optionally be scoped to a specific path pattern by providing a path as the first argument. Handlers are executed in the order they are registered.
Without a path, middleware runs for all messages:
router.Use(loggingMiddleware, authMiddleware)
With a path, middleware only runs for matching messages:
router.Use("/api/**", authMiddleware)
Routers can also be used as middleware to create modular sub-routers:
apiRouter := velaros.NewRouter()
router.Use("/api/**", apiRouter)
Handlers must be of type Handler, HandlerFunc, or func(*Context).
Handlers may also implement OpenHandler or CloseHandler to register connection lifecycle hooks when used as middleware.
func (*Router) UseClose ¶ added in v1.5.0
UseClose registers handlers that execute when a WebSocket connection is closing, after the message loop exits. This is useful for cleanup, logging, or notifying other systems about disconnections. UseClose handlers can still send messages to the client before the connection closes.
Example:
router.UseClose(func(ctx *velaros.Context) {
sessionID, _ := ctx.GetFromSocket("sessionID")
log.Printf("Connection closed: %s", sessionID)
})
UseClose handlers are executed in the order they are registered, for both server-initiated and client-initiated closures.
func (*Router) UseOpen ¶ added in v1.5.0
UseOpen registers handlers that execute when a new WebSocket connection is established, before any messages are processed. This is useful for connection initialization, authentication checks, or setting up connection-level state.
Example:
router.UseOpen(func(ctx *velaros.Context) {
ctx.SetOnSocket("connectedAt", time.Now())
ctx.SetOnSocket("sessionID", uuid.New())
})
UseOpen handlers are executed in the order they are registered.
type RouterHandler ¶
type RouterHandler interface {
RouteDescriptors() []*RouteDescriptor
Handle(ctx *Context)
Lookup(handlerOrTransformer any) (*Pattern, bool)
}
RouterHandler is implemented by routers to enable composition and nesting. When a RouterHandler is bound as middleware, the parent router collects its route descriptors for API discovery and can perform reverse routing through nested routers via Lookup. This allows building modular applications with sub-routers.
type Socket ¶ added in v1.5.0
type Socket struct {
// contains filtered or unexported fields
}
Socket represents a WebSocket connection and manages its lifecycle, message interception, and connection-level storage. It implements the context.Context interface to support cancellation and deadlines.
Socket is primarily used internally by the router but is exported to allow advanced use cases and custom integrations. Most users will interact with Socket indirectly through Context methods like SetOnSocket and GetFromSocket.
Key responsibilities:
- Connection lifecycle management (open, close, done signaling)
- Thread-safe connection-level value storage
- Message interception for request/reply correlation
- Access to original HTTP upgrade request headers
func CtxSocket ¶ added in v1.11.0
CtxSocket retrieves the Socket associated with the given Context. This function is for frameworks to be able to grab the underlying Socket and shouldn't be used in most cases.
func NewSocket ¶ added in v1.5.0
func NewSocket(info *ConnectionInfo, conn SocketConnection) *Socket
NewSocket creates a new Socket wrapping a WebSocket connection. This is primarily for internal use by the router. The socket ID is automatically generated and the done channel is initialized.
func (*Socket) AddInterceptor ¶ added in v1.11.0
func (s *Socket) AddInterceptor(id string, interceptorChan chan *InboundMessage)
AddInterceptor registers an interceptor channel for a given message ID. Messages arriving with this ID will be sent to the channel instead of following normal routing. This is an internal method used by the Request/Receive API.
func (*Socket) Close ¶ added in v1.11.0
func (s *Socket) Close(status Status, reason string, source CloseSource)
Close marks the socket as closed with the given status code, reason, and source (client or server). This is thread-safe and idempotent - subsequent calls have no effect. The actual connection close happens after UseClose handlers complete.
func (*Socket) Deadline ¶ added in v1.5.0
Deadline returns the time when work done on behalf of this socket's context should be canceled. Returns ok==false when no deadline is set. Part of the context.Context interface.
func (*Socket) Delete ¶ added in v1.12.0
Delete removes a value stored at the socket/connection level. This is thread-safe. Use Context.DeleteFromSocket instead of calling this directly.
func (*Socket) Done ¶ added in v1.5.0
func (s *Socket) Done() <-chan struct{}
Done returns a channel that's closed when the socket's context should be canceled. This closes when the connection closes. Part of the context.Context interface.
func (*Socket) Err ¶ added in v1.5.0
Err returns a non-nil error value after Done is closed. Returns Canceled if the context was canceled. Part of the context.Context interface.
func (*Socket) Get ¶ added in v1.11.0
Get retrieves a value stored at the socket/connection level. Returns the value and true if found, or nil and false otherwise. This is thread-safe. Use Context.GetFromSocket instead of calling this directly.
func (*Socket) GetInterceptor ¶ added in v1.11.0
func (s *Socket) GetInterceptor(id string) (chan *InboundMessage, bool)
GetInterceptor retrieves the interceptor channel for a given message ID. Interceptors are used for request/reply correlation and multi-message conversations. Returns the channel and true if found. This is an internal method.
func (*Socket) HandleClose ¶ added in v1.11.0
func (s *Socket) HandleClose(node *HandlerNode)
HandleClose executes the close lifecycle handlers starting at the given node. This is an internal method used by the router when a connection is closing.
func (*Socket) HandleNextMessageWithNode ¶ added in v1.11.0
func (s *Socket) HandleNextMessageWithNode(node *HandlerNode) bool
HandleNextMessageWithNode reads the next message from the connection and processes it through the handler chain starting at the given node. Returns false if the connection is closed or an error occurs. This is an internal method used by the router.
func (*Socket) HandleOpen ¶ added in v1.11.0
func (s *Socket) HandleOpen(node *HandlerNode)
HandleOpen executes the open lifecycle handlers starting at the given node. This is an internal method used by the router when a new connection is established.
func (*Socket) Headers ¶ added in v1.11.0
Headers returns the HTTP headers from the initial WebSocket upgrade request. These headers persist for the lifetime of the connection and are useful for accessing authentication tokens, cookies, or custom headers sent during the handshake.
func (*Socket) ID ¶ added in v1.11.0
ID returns the unique identifier for this socket. The ID is automatically generated when the socket is created and remains constant for the connection's lifetime.
func (*Socket) IsClosed ¶ added in v1.11.0
IsClosed returns true if the socket has been closed. This is thread-safe and can be called from any goroutine.
func (*Socket) MustGet ¶ added in v1.11.0
MustGet retrieves a value stored at the socket/connection level. Panics if the key is not found. This is thread-safe. Use Context.MustGetFromSocket instead of calling this directly.
func (*Socket) RemoteAddr ¶ added in v1.13.0
RemoteAddr returns the remote network address of the client. The format depends on the underlying connection but is typically 'IP:port'.
func (*Socket) RemoveInterceptor ¶ added in v1.11.0
RemoveInterceptor unregisters the interceptor channel for a given message ID. This is an internal method that cleans up interceptors when contexts are freed.
func (*Socket) Send ¶ added in v1.11.0
func (s *Socket) Send(messageType MessageType, data []byte) error
Send writes a message to the WebSocket connection with the specified message type (MessageText or MessageBinary). This is a low-level method - most users should use Context.Send instead.
type SocketConnection ¶
type SocketConnection interface {
Read(ctx context.Context) (*SocketMessage, error)
Write(ctx context.Context, msg *SocketMessage) error
Close(status Status, reason string) error
}
SocketConnection is an interface for WebSocket connection implementations. This allows Velaros to work with different WebSocket libraries or custom connection types. The framework provides WebSocketConnection for the standard github.com/coder/websocket library.
type SocketMessage ¶ added in v1.16.0
type SocketMessage struct {
Type MessageType
RawData []byte
Data []byte
Meta map[string]any
}
SocketMessage represents a WebSocket message at the transport layer, containing the message type, raw data, processed data, and metadata. This is used by SocketConnection implementations to pass messages between the WebSocket layer and the routing layer.
type Status ¶ added in v1.5.0
type Status = websocket.StatusCode
Status represents a WebSocket close status code as defined in RFC 6455. Use these codes when calling CloseWithStatus to indicate the reason for closing the connection.
const ( StatusNormalClosure Status = websocket.StatusNormalClosure // 1000 StatusGoingAway Status = websocket.StatusGoingAway // 1001 StatusProtocolError Status = websocket.StatusProtocolError // 1002 StatusUnsupportedData Status = websocket.StatusUnsupportedData // 1003 StatusNoStatusRcvd Status = websocket.StatusNoStatusRcvd // 1005 StatusAbnormalClosure Status = websocket.StatusAbnormalClosure // 1006 StatusInvalidFramePayloadData Status = websocket.StatusInvalidFramePayloadData // 1007 StatusPolicyViolation Status = websocket.StatusPolicyViolation // 1008 StatusMessageTooBig Status = websocket.StatusMessageTooBig // 1009 StatusMandatoryExtension Status = websocket.StatusMandatoryExtension // 1010 StatusInternalError Status = websocket.StatusInternalError // 1011 StatusServiceRestart Status = websocket.StatusServiceRestart // 1012 StatusTryAgainLater Status = websocket.StatusTryAgainLater // 1013 StatusBadGateway Status = websocket.StatusBadGateway // 1014 StatusTLSHandshake Status = websocket.StatusTLSHandshake // 1015 )
WebSocket close status codes
type WebSocketConnection ¶ added in v1.16.0
type WebSocketConnection struct {
// contains filtered or unexported fields
}
WebSocketConnection is a SocketConnection implementation that wraps github.com/coder/websocket.Conn. This is the default connection type used by the router when handling HTTP WebSocket upgrades.
func NewWebSocketConnection ¶ added in v1.16.0
func NewWebSocketConnection(websocketConnection *websocket.Conn) *WebSocketConnection
NewWebSocketConnection creates a WebSocketConnection from a github.com/coder/websocket.Conn. This is used internally by the router. Most applications don't need to call this directly unless implementing custom connection handling with Router.HandleConnection.
func (*WebSocketConnection) Close ¶ added in v1.16.0
func (c *WebSocketConnection) Close(status Status, reason string) error
Close closes the WebSocket connection with the given status code and reason. Implements SocketConnection.Close.
func (*WebSocketConnection) Read ¶ added in v1.16.0
func (c *WebSocketConnection) Read(ctx context.Context) (*SocketMessage, error)
Read reads the next message from the WebSocket connection. Blocks until a message arrives or an error occurs. Implements SocketConnection.Read.
func (*WebSocketConnection) Write ¶ added in v1.16.0
func (c *WebSocketConnection) Write(ctx context.Context, msg *SocketMessage) error
Write sends a message to the WebSocket connection. Implements SocketConnection.Write.