gows

package module
v1.1.8 Latest Latest
Warning

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

Go to latest
Published: May 10, 2025 License: CC-BY-4.0 Imports: 14 Imported by: 0

README

Go-WS: A High-Level WebSocket Library for Go

Go-WS is wrapper built around Gorilla WebSocket, designed to simplify and accelerate WebSocket communication for both server and client implementations in Go. It introduces high-level event emitters, request-response abstractions, and message parsing logic, making it ideal for building scalable real-time applications.


Features

  • Easy WebSocket server and client setup
  • Event-driven architecture (connection, disconnection, errors, messages)
  • Request-response pattern built-in
  • Custom message parsing and typed handler registration
  • Graceful connection handling with ID assignment
  • Thread-safe message sending

Installation

go get github.com/GTedZ/Go-WS

Getting Started

1. Setting Up a WebSocket Server
server := gows.NewWebSocketServer(nil)

Use server.Listen(port) to start listening.

go server.Listen(3000)
2. Configuring Connection Origin Check (can be left as nil) [OPTIONAL]

This allows the server to set rules on who to accept based on the return value of the function provided

Use Cases:
  • Set rules on who to accept and who to reject via the *http.Request variable
  • Can be used to set complex rules based on application-logic, metadata checks
server.SetCheckOriginFunc(func(r *http.Request) bool {
    return true // Allow all origins
})

OR

server.SetCheckOriginFunc(nil)  // nil means accept-all (similar to the above example)
3. Handling Events
server.OnConnect.Subscribe(func(conn *gows.WebsocketConnection) {
    // Handle new connection
})

server.OnRequest.Subscribe(func(req gows.ServerIncomingRequest) {
    // Handle incoming structured requests
})
4. Adding connection-level metadata:

You can use OnConnect's connection to check the *http.Request that was used to connect, alongside the CustomData property of the connection to set any metadata you would like into the structure.

Example:
server.SetCheckOriginFunc(func(r *http.Request) bool {
    username := r.Header.Get("username")
    if username == "" {
        return false
    }

    return true;
})

server.OnConnect.Subscribe(func(conn *gows.WebsocketConnection) {
		wc.CustomData.Mu.Lock()
		wc.CustomData.Mu.Unlock()
		wc.CustomData.Map["username"] = conn.OrigRequest.Header.Get("username")
})
5. Sending Text and JSON Messages
conn.SendText("Hello")
conn.SendJSON(struct { Message string }{Message: "Hello"})

WebSocket Client

1. Creating and Connecting
client := gows.NewWebsocketClient("ws://localhost:3000")
err := client.Connect()
2. Receiving Messages
client.OnMessage.Subscribe(func(msg gows.SocketMessage) {
    fmt.Println(string(msg.Message))
})

Request-Response Pattern

The library implements a Request/Response pattern that can be used to quickly and synchronously send and receive messages.

It uses a discrete requestId_propertyName (by default "Id") silently embedded alongside your request struct, that will be used to automatically match with the other party's Reply message

NOTE:
  • The request/response schema is bidirectional, both the server and client can send, receive and handle both requests and responses
  • Both parties MUST have the same requestId_propertyName registered, you can register your own custom propertyName via server.SetRequestIdProperty() or client.SetRequestIdProperty
Client Sending Request
var response MyResponse
client.SendRequest(MyStruct{...}, &response, nil)
Server Handling Request
server.OnRequest.Subscribe(func(req gows.ServerIncomingRequest) {
    var parsedData MyRequest
    json.Unmarshal(req.Data, &parsedData)
    req.Reply(MyResponse{...})
})

Message Parsers

Message parsers setup the retrieval of expected JSON structures, with the ability to provide a custom Parser and a handler to receive it

gows.RegisterMessageParserCallback(
    &server.MessageParserRegistry,
    <nil> OR func(b []byte) (bool, *MyStruct),
    func(s *YourType),
)
Server-Side Parser Registration:

Register a typed parser for incoming JSON structures:

gows.RegisterMessageParserCallback(
    &server.MessageParserRegistry,
    func(b []byte) (bool, *MyStruct) {
        var s MyStruct
        err := json.Unmarshal(b, &s)
        return err == nil && s.Field != "", &s
    },
    func(s *MyStruct) {
        fmt.Println("Parsed struct:", s)
    },
)
Client-Side Parser Registration:

Register a typed parser for incoming JSON structures:

gows.RegisterMessageParserCallback(
    &client.MessageParserRegistry,
    func(b []byte) (bool, *MyStruct) {
        var s MyStruct
        err := json.Unmarshal(b, &s)
        return err == nil && s.Field != "", &s
    },
    func(s *MyStruct) {
        fmt.Println("Parsed struct:", s)
    },
)
Parser Function: func(b []byte) (isValid bool, result *YourType) OR nil

The parser function (passed as the second argument in the gows.RegisterMessageParserCallback function) is the function called on every message in order to check if it is a valid message, here's a brief explanation of the variables:

  • b []byte: This is the raw message received in the websocket stream
  • isValid bool: When the parser function returns true, the message is accepted as a parsed message, and forwards the returned result value to the callback function
  • result *YourType: Once the message is accepted, this value will be forwarded as-is to the registered callback function.
IMPORTANT: Message parsers aren't limited to structs only, but when providing a nil parser, it is expected to be a struct, and any non-error JSON unmarshall of the struct will be accepted as a valid parsed message.

Events Summary

  • OnConnect, OnClose, OnError, OnAnyMessage, OnMessage, OnParsedMessage
  • OnRequest and OnRequestResponse
  • All events are EventEmitter[T] allowing Subscribe() and Unsubscribe()

Contributions

Feel free to submit issues and pull requests. Feedback and improvements are welcome.

Support

If you would like to support my projects, you can donate to the following addresses:

BTC: 19XC9b9gkbNVTB9eTPzjByijmx8gNd2qVR

ETH (ERC20): 0x7e5db6e135da1f4d5de26f7f7d4ed10dada20478

USDT (TRC20): TLHo8zxBpFSTdyuvvCTG4DzXFfDfv49EMu

SOL: B3ZKvA3yCm5gKLb7vucU7nu8RTnp6MEAPpfeySMSMoDc

Thank you for your support!


License

MIT License

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DeregisterMessageParserCallback

func DeregisterMessageParserCallback[T any](r *messageParsers_Registry, handler *typedHandler[*T])

Generic function to register parser and callback for type T

if `parser` is nil, the callback will be called upon any non-error unmarshall of messages (not recommended unless it is the only message structure that is received)

func RegisterMessageParserCallback

func RegisterMessageParserCallback[T any](r *messageParsers_Registry, parser ParserFunc[*T], callback CallbackFunc[*T]) *typedHandler[*T]

Generic function to register parser and callback for type T

if `parser` is nil, the callback will be called upon any non-error unmarshall of messages (not recommended unless it is the only message structure that is received)

Types

type CallbackFunc

type CallbackFunc[T any] func(T)

type CloseEvent

type CloseEvent struct {
	Connection *WebsocketConnection
	Code       int
	Text       string
}

type Error

type Error struct {
	Connection *WebsocketConnection
	Err        error
}

type EventEmitter

type EventEmitter[T any] struct {
	// contains filtered or unexported fields
}

func (*EventEmitter[T]) Emit

func (e *EventEmitter[T]) Emit(arg T)

Not sure if this needs to be public

func (*EventEmitter[T]) Subscribe

func (e *EventEmitter[T]) Subscribe(handler VoidHandler[T])

func (*EventEmitter[T]) Unsubscribe

func (e *EventEmitter[T]) Unsubscribe(handler VoidHandler[T])

type IncomingMessage

type IncomingMessage struct {
	Connection *WebsocketConnection
	Message    []byte
}

type IncomingRequest

type IncomingRequest struct {
	Data []byte
	// contains filtered or unexported fields
}

func (*IncomingRequest) Reply

func (request *IncomingRequest) Reply(data interface{}) error

type ParserFunc

type ParserFunc[T any] func([]byte) (bool, T)

Parser and Callback types

type SendRequestParams

type SendRequestParams struct {
	Timeout time.Duration
}

type ServerIncomingRequest

type ServerIncomingRequest struct {
	WebsocketConnection *WebsocketConnection
	Data                []byte
	// contains filtered or unexported fields
}

func (*ServerIncomingRequest) Reply

func (request *ServerIncomingRequest) Reply(data interface{}) error

type SocketCloseEvent

type SocketCloseEvent struct {
	Code int
	Text string
}

type SocketError

type SocketError struct {
	Err error
}

type SocketMessage

type SocketMessage struct {
	Message []byte
}

type UpgradeError

type UpgradeError struct {
	Err     error
	Request *http.Request
}

type VoidHandler

type VoidHandler[T any] func(T)

For events with no return value

type WebsocketClient

type WebsocketClient struct {
	URL string

	Conn *websocket.Conn

	OnError EventEmitter[SocketError]
	OnClose EventEmitter[SocketCloseEvent]

	OnAnyMessage EventEmitter[SocketMessage]

	// This is an eventEmitter for incoming requests from the server socket
	OnRequest EventEmitter[IncomingRequest]

	// This is an eventEmitter for incoming responses from the server to a request sent from the client
	OnRequestResponse EventEmitter[SocketMessage]

	// In order to register the parsed messages
	MessageParserRegistry messageParsers_Registry
	OnParsedMessage       EventEmitter[SocketMessage]
	OnMessage             EventEmitter[SocketMessage]
	// contains filtered or unexported fields
}

func NewWebsocketClient

func NewWebsocketClient(url string) *WebsocketClient

func (*WebsocketClient) Close

func (ws_client *WebsocketClient) Close() error

func (*WebsocketClient) Connect

func (ws_client *WebsocketClient) Connect() error

func (*WebsocketClient) GetRequestIdProperty

func (ws_client *WebsocketClient) GetRequestIdProperty() string

func (*WebsocketClient) IsClosed

func (ws_client *WebsocketClient) IsClosed() bool

func (*WebsocketClient) SendJSON

func (ws_client *WebsocketClient) SendJSON(msg interface{}) error

func (*WebsocketClient) SendRequest

func (ws_client *WebsocketClient) SendRequest(request any, response any, opt_params *SendRequestParams) (hasTimedOut bool, err error)

SendRequest sends msg to the server and waits for a response or timeout (if set).

The response is unmarshalled into the provided variable v.

If the message failed to send, or the response is not received within the timeout, an error is returned along with the respective hasTimedOut value.

func (*WebsocketClient) SendText

func (ws_client *WebsocketClient) SendText(msg string) error

func (*WebsocketClient) SetRequestIdProperty

func (ws_client *WebsocketClient) SetRequestIdProperty(propertyName string) error

type WebsocketConnection

type WebsocketConnection struct {
	Id int64 // Read-only unique ID for the connection (set automatically by the server)

	Conn        *websocket.Conn
	OrigRequest *http.Request

	OnError EventEmitter[SocketError]
	OnClose EventEmitter[SocketCloseEvent]

	OnAnyMessage EventEmitter[SocketMessage]

	// This is an eventEmitter for incoming requests from the client socket
	OnRequest EventEmitter[IncomingRequest]

	// This is an eventEmitter for incoming response from the client socket of a request sent from the server
	OnRequestResponse EventEmitter[SocketMessage]

	OnParsedMessage EventEmitter[SocketMessage]
	OnMessage       EventEmitter[SocketMessage]

	// # Not used by library
	//
	// This is simply left to the developer to use as they please
	//
	// In the case of complex AllowOrigin configurations, this can help store metadata to match a connection to a user or any application-logic data
	CustomData struct {
		Mu  sync.Mutex
		Map map[string]string
	}
	// contains filtered or unexported fields
}

func (*WebsocketConnection) Close

func (ws_connection *WebsocketConnection) Close() error

func (*WebsocketConnection) SendJSON

func (ws_connection *WebsocketConnection) SendJSON(msg interface{}) error

func (*WebsocketConnection) SendPreparedMessage added in v1.1.2

func (ws_connection *WebsocketConnection) SendPreparedMessage(pm *websocket.PreparedMessage) error

func (*WebsocketConnection) SendRequest

func (ws_connection *WebsocketConnection) SendRequest(request any, response any, opt_params *SendRequestParams) (hasTimedOut bool, err error)

SendRequest sends msg to the server and waits for a response or timeout (if set).

The response is unmarshalled into the provided variable v.

If the message failed to send, or the response is not received within the timeout, an error is returned along with the respective hasTimedOut value.

func (*WebsocketConnection) SendText

func (ws_connection *WebsocketConnection) SendText(msg string) error

type WebsocketServer

type WebsocketServer struct {
	Clients struct {
		Mu  sync.Mutex
		Map map[int64]*WebsocketConnection
	}

	OnUpgradeError EventEmitter[UpgradeError]
	OnConnect      EventEmitter[*WebsocketConnection]

	OnError EventEmitter[Error]
	OnClose EventEmitter[CloseEvent]

	OnAnyMessage EventEmitter[IncomingMessage]

	// This is an eventEmitter for incoming requests from the client socket
	OnRequest EventEmitter[ServerIncomingRequest]
	// This is an eventEmitter for incoming response from the client socket of a request sent from the server
	OnRequestResponse EventEmitter[IncomingMessage]

	// In order to register the parsed messages
	MessageParserRegistry messageParsers_Registry
	OnParsedMessage       EventEmitter[IncomingMessage]

	OnMessage EventEmitter[IncomingMessage]
	// contains filtered or unexported fields
}

func NewWebSocketServer

func NewWebSocketServer(checkOriginFunc func(r *http.Request) bool) *WebsocketServer

Create a function to create a new websocket server, with a given address and port

func (*WebsocketServer) GetRequestIdProperty

func (ws_server *WebsocketServer) GetRequestIdProperty() string

func (*WebsocketServer) Listen

func (ws_server *WebsocketServer) Listen(port int, paths ...string) error

'Listen' starts the WebSocket server and listens for incoming connections on the specified paths.

This function is blocking and will not return until the server is stopped.

If no paths are provided, it will listen on the root path ("/").

func (*WebsocketServer) SetCheckOriginFunc

func (ws_server *WebsocketServer) SetCheckOriginFunc(checkOriginFunc func(r *http.Request) bool)

func (*WebsocketServer) SetRequestIdProperty

func (ws_server *WebsocketServer) SetRequestIdProperty(propertyName string) error

Jump to

Keyboard shortcuts

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