Documentation
¶
Overview ¶
Package websocket provides an easy way to setup a rich Websocket server and client side.
Index ¶
- Constants
- Variables
- func Disconnect(connID string) error
- func HandleConnection(websocketConn UnderlineConnection)
- func Handler() http.Handler
- func IsConnected(connID string) bool
- func Join(roomName string, connID string)
- func Leave(roomName string, connID string)
- func LeaveAll(connID string)
- func OnConnection(cb ConnectionFunc)
- func Serve()
- func Set(setters ...OptionSetter)
- type Config
- type Connection
- type ConnectionFunc
- type DisconnectFunc
- type Emitter
- type ErrorFunc
- type MessageFunc
- type NativeMessageFunc
- type OptionSet
- func BinaryMessages(val bool) OptionSet
- func CheckOrigin(val func(req *http.Request) bool) OptionSet
- func Error(val func(res http.ResponseWriter, req *http.Request, status int, reason error)) OptionSet
- func IDGenerator(val func(*http.Request) string) OptionSet
- func MaxMessageSize(val int64) OptionSet
- func PingPeriod(val time.Duration) OptionSet
- func PongTimeout(val time.Duration) OptionSet
- func ReadBufferSize(val int) OptionSet
- func ReadTimeout(val time.Duration) OptionSet
- func WriteBufferSize(val int) OptionSet
- func WriteTimeout(val time.Duration) OptionSet
- type OptionSetter
- type Server
- type UnderlineConnection
Constants ¶
const ( // DefaultWebsocketWriteTimeout 15 * time.Second DefaultWebsocketWriteTimeout = 15 * time.Second // DefaultWebsocketReadTimeout 15 * time.Second DefaultWebsocketReadTimeout = 15 * time.Second // DefaultWebsocketPongTimeout 60 * time.Second DefaultWebsocketPongTimeout = 60 * time.Second // DefaultWebsocketPingPeriod (DefaultPongTimeout * 9) / 10 DefaultWebsocketPingPeriod = (DefaultWebsocketPongTimeout * 9) / 10 // DefaultWebsocketMaxMessageSize 1024 DefaultWebsocketMaxMessageSize = 1024 // DefaultWebsocketReadBufferSize 4096 DefaultWebsocketReadBufferSize = 4096 // DefaultWebsocketWriterBufferSize 4096 DefaultWebsocketWriterBufferSize = 4096 )
const ( // All is the string which the Emitter use to send a message to all All = "" // NotMe is the string which the Emitter use to send a message to all except this connection NotMe = ";gowebsocket;to;all;except;me;" // Broadcast is the string which the Emitter use to send a message to all except this connection, same as 'NotMe' Broadcast = NotMe )
const ClientSourcePath = "/go-websocket.js"
ClientSourcePath is never used inside this project but you can use it to serve the client source /go-websocket.js ex: http.Handle(websocket.ClientSourcePath, websocket.ClientSourceHandler) Note: this is totally optionally: if you use only connection.EmitMessage which sends native websocket messages then you can handle the connection by the native javascript websocket API
const (
// Version current version number
Version = "0.0.3"
)
Variables ¶
var ClientSource = []byte(`var websocketStringMessageType = 0;
var websocketIntMessageType = 1;
var websocketBoolMessageType = 2;
// bytes is missing here for reasons I will explain somewhen
var websocketJSONMessageType = 4;
var websocketMessagePrefix = "go-websocket-message:";
var websocketMessageSeparator = ";";
var websocketMessagePrefixLen = websocketMessagePrefix.length;
var websocketMessageSeparatorLen = websocketMessageSeparator.length;
var websocketMessagePrefixAndSepIdx = websocketMessagePrefixLen + websocketMessageSeparatorLen - 1;
var websocketMessagePrefixIdx = websocketMessagePrefixLen - 1;
var websocketMessageSeparatorIdx = websocketMessageSeparatorLen - 1;
var Ws = (function () {
//
function Ws(endpoint, protocols) {
var _this = this;
// events listeners
this.connectListeners = [];
this.disconnectListeners = [];
this.nativeMessageListeners = [];
this.messageListeners = {};
if (!window["WebSocket"]) {
return;
}
if (endpoint.indexOf("ws") == -1) {
endpoint = "ws://" + endpoint;
}
if (protocols != null && protocols.length > 0) {
this.conn = new WebSocket(endpoint, protocols);
}
else {
this.conn = new WebSocket(endpoint);
}
this.conn.onopen = (function (evt) {
_this.fireConnect();
_this.isReady = true;
return null;
});
this.conn.onclose = (function (evt) {
_this.fireDisconnect();
return null;
});
this.conn.onmessage = (function (evt) {
_this.messageReceivedFromConn(evt);
});
}
//utils
Ws.prototype.isNumber = function (obj) {
return !isNaN(obj - 0) && obj !== null && obj !== "" && obj !== false;
};
Ws.prototype.isString = function (obj) {
return Object.prototype.toString.call(obj) == "[object String]";
};
Ws.prototype.isBoolean = function (obj) {
return typeof obj === 'boolean' ||
(typeof obj === 'object' && typeof obj.valueOf() === 'boolean');
};
Ws.prototype.isJSON = function (obj) {
return typeof obj === 'object';
};
//
// messages
Ws.prototype._msg = function (event, websocketMessageType, dataMessage) {
return websocketMessagePrefix + event + websocketMessageSeparator + String(websocketMessageType) + websocketMessageSeparator + dataMessage;
};
Ws.prototype.encodeMessage = function (event, data) {
var m = "";
var t = 0;
if (this.isNumber(data)) {
t = websocketIntMessageType;
m = data.toString();
}
else if (this.isBoolean(data)) {
t = websocketBoolMessageType;
m = data.toString();
}
else if (this.isString(data)) {
t = websocketStringMessageType;
m = data.toString();
}
else if (this.isJSON(data)) {
//propably json-object
t = websocketJSONMessageType;
m = JSON.stringify(data);
}
else {
console.log("Invalid");
}
return this._msg(event, t, m);
};
Ws.prototype.decodeMessage = function (event, websocketMessage) {
//q-websocket-message;user;4;themarshaledstringfromajsonstruct
var skipLen = websocketMessagePrefixLen + websocketMessageSeparatorLen + event.length + 2;
if (websocketMessage.length < skipLen + 1) {
return null;
}
var websocketMessageType = parseInt(websocketMessage.charAt(skipLen - 2));
var theMessage = websocketMessage.substring(skipLen, websocketMessage.length);
if (websocketMessageType == websocketIntMessageType) {
return parseInt(theMessage);
}
else if (websocketMessageType == websocketBoolMessageType) {
return Boolean(theMessage);
}
else if (websocketMessageType == websocketStringMessageType) {
return theMessage;
}
else if (websocketMessageType == websocketJSONMessageType) {
return JSON.parse(theMessage);
}
else {
return null; // invalid
}
};
Ws.prototype.getWebsocketCustomEvent = function (websocketMessage) {
if (websocketMessage.length < websocketMessagePrefixAndSepIdx) {
return "";
}
var s = websocketMessage.substring(websocketMessagePrefixAndSepIdx, websocketMessage.length);
var evt = s.substring(0, s.indexOf(websocketMessageSeparator));
return evt;
};
Ws.prototype.getCustomMessage = function (event, websocketMessage) {
var eventIdx = websocketMessage.indexOf(event + websocketMessageSeparator);
var s = websocketMessage.substring(eventIdx + event.length + websocketMessageSeparator.length + 2, websocketMessage.length);
return s;
};
//
// Ws Events
// messageReceivedFromConn this is the func which decides
// if it's a native websocket message or a custom qws message
// if native message then calls the fireNativeMessage
// else calls the fireMessage
//
// remember q gives you the freedom of native websocket messages if you don't want to use this client side at all.
Ws.prototype.messageReceivedFromConn = function (evt) {
//check if qws message
var message = evt.data;
if (message.indexOf(websocketMessagePrefix) != -1) {
var event_1 = this.getWebsocketCustomEvent(message);
if (event_1 != "") {
// it's a custom message
this.fireMessage(event_1, this.getCustomMessage(event_1, message));
return;
}
}
// it's a native websocket message
this.fireNativeMessage(message);
};
Ws.prototype.OnConnect = function (fn) {
if (this.isReady) {
fn();
}
this.connectListeners.push(fn);
};
Ws.prototype.fireConnect = function () {
for (var i = 0; i < this.connectListeners.length; i++) {
this.connectListeners[i]();
}
};
Ws.prototype.OnDisconnect = function (fn) {
this.disconnectListeners.push(fn);
};
Ws.prototype.fireDisconnect = function () {
for (var i = 0; i < this.disconnectListeners.length; i++) {
this.disconnectListeners[i]();
}
};
Ws.prototype.OnMessage = function (cb) {
this.nativeMessageListeners.push(cb);
};
Ws.prototype.fireNativeMessage = function (websocketMessage) {
for (var i = 0; i < this.nativeMessageListeners.length; i++) {
this.nativeMessageListeners[i](websocketMessage);
}
};
Ws.prototype.On = function (event, cb) {
if (this.messageListeners[event] == null || this.messageListeners[event] == undefined) {
this.messageListeners[event] = [];
}
this.messageListeners[event].push(cb);
};
Ws.prototype.OnAll = function (cb) {
if (this.messageListeners['*'] == null || this.messageListeners['*'] == undefined) {
this.messageListeners['*'] = [];
}
this.messageListeners['*'].push(cb);
};
Ws.prototype.fireMessage = function (event, message) {
// Call messageListeners for this specific event
for (var key in this.messageListeners) {
if (this.messageListeners.hasOwnProperty(key)) {
if (key == event) {
for (var i = 0; i < this.messageListeners[key].length; i++) {
this.messageListeners[key][i](message);
}
}
}
}
// Call messageListeners for OnAll event
for (var i = 0; i < this.messageListeners['*'].length; i++) {
this.messageListeners['*'][i](message);
}
};
//
// Ws Actions
Ws.prototype.Disconnect = function () {
this.conn.close();
};
// EmitMessage sends a native websocket message
Ws.prototype.EmitMessage = function (websocketMessage) {
this.conn.send(websocketMessage);
};
// Emit sends an q-custom websocket message
Ws.prototype.Emit = function (event, data) {
var messageStr = this.encodeMessage(event, data);
this.EmitMessage(messageStr);
};
return Ws;
}());
`)
ClientSource the client-side javascript raw source code
var ClientSourceHandler = fs.StaticContentHandler(ClientSource, "application/javascript")
ClientSourceHandler is the handler which will serve the client side source code to your net/http server Note: this is totally optionally: if you use only connection.EmitMessage which sends native websocket messages then you can handle the connection by the native javascript websocket API
var ( // DefaultIDGenerator returns the result of 64 // random combined characters as the id of a new connection. // Used when config.IDGenerator is nil DefaultIDGenerator = func(*http.Request) string { return randomString(64) } )
Functions ¶
func Disconnect ¶ added in v0.0.3
Disconnect force-disconnects a websocket connection based on its connection.ID() What it does? 1. remove the connection from the list 2. leave from all joined rooms 3. fire the disconnect callbacks, if any 4. close the underline connection and return its error, if any.
You can use the connection.Disconnect() instead.
func HandleConnection ¶
func HandleConnection(websocketConn UnderlineConnection)
HandleConnection creates & starts to listening to a new connection
func Handler ¶
Handler returns the http.Handler which is setted to the 'Websocket Endpoint path', the client should target to this handler's developer's custom path ex: http.Handle("/myendpoint", mywebsocket.Handler()) Handler calls the HandleConnection, so Use Handler or HandleConnection manually, DO NOT USE both. Note: you can always create your own upgrader which returns an UnderlineConnection and call only the HandleConnection manually (as Iris web framework does)
func IsConnected ¶ added in v0.0.3
IsConnected returns true if the connection with that ID is connected to the server useful when you have defined a custom connection id generator (based on a database) and you want to check if that connection is already connected (on multiple tabs)
func Join ¶ added in v0.0.3
Join joins a websocket client to a room, first parameter is the room name and the second the connection.ID()
You can use connection.Join("room name") instead.
func Leave ¶ added in v0.0.3
Leave leaves a websocket client from a room, first parameter is the room name and the second the connection.ID()
You can use connection.Leave("room name") instead.
func LeaveAll ¶ added in v0.0.3
func LeaveAll(connID string)
LeaveAll kicks out a connection from ALL of its joined rooms
func OnConnection ¶
func OnConnection(cb ConnectionFunc)
OnConnection this is the main event you, as developer, will work with each of the websocket connections
func Serve ¶
func Serve()
Serve is here only for compatibility. Look here for more: https://github.com/kataras/iris/blob/5.0.0/websocket.go#L125
func Set ¶
func Set(setters ...OptionSetter)
Set sets an option aka configuration field to the default websocket server
Types ¶
type Config ¶
type Config struct {
Error func(res http.ResponseWriter, req *http.Request, status int, reason error)
CheckOrigin func(req *http.Request) bool
// WriteTimeout time allowed to write a message to the connection.
// Default value is 15 * time.Second
WriteTimeout time.Duration
// ReadTimeout time allowed to read a message from the connection.
// Default value is 15 * time.Second
ReadTimeout time.Duration
// PongTimeout allowed to read the next pong message from the connection
// Default value is 60 * time.Second
PongTimeout time.Duration
// PingPeriod send ping messages to the connection with this period. Must be less than PongTimeout
// Default value is (PongTimeout * 9) / 10
PingPeriod time.Duration
// MaxMessageSize max message size allowed from connection
// Default value is 1024
MaxMessageSize int64
// BinaryMessages set it to true in order to denotes binary data messages instead of utf-8 text
// compatible if you wanna use the Connection's EmitMessage to send a custom binary data to the client, like a native server-client communication.
// defaults to false
BinaryMessages bool
// ReadBufferSize is the buffer size for the underline reader
ReadBufferSize int
// WriteBufferSize is the buffer size for the underline writer
WriteBufferSize int
// IDGenerator used to create (and later on, set)
// an ID for each incoming websocket connections (clients).
// The request is an argument which you can use to generate the ID (from headers for example).
// If empty then the ID is generated by DefaultIDGenerator: randomString(64)
IDGenerator func(*http.Request) string
}
Config the websocket server configuration all of these are optional.
type Connection ¶
type Connection interface {
// Emitter implements EmitMessage & Emit
Emitter
// ID returns the connection's identifier
ID() string
// OnDisconnect registers a callback which fires when this connection is closed by an error or manual
OnDisconnect(DisconnectFunc)
// OnError registers a callback which fires when this connection occurs an error
OnError(ErrorFunc)
// EmitError can be used to send a custom error message to the connection
//
// It does nothing more than firing the OnError listeners. It doesn't sends anything to the client.
EmitError(errorMessage string)
// To defines where server should send a message
// returns an emitter to send messages
To(string) Emitter
// OnMessage registers a callback which fires when native websocket message received
OnMessage(NativeMessageFunc)
// On registers a callback to a particular event which fires when a message to this event received
On(string, MessageFunc)
// Join join a connection to a room, it doesn't check if connection is already there, so care
Join(string)
// Leave removes a connection from a room
Leave(string)
// Disconnect disconnects the client, close the underline websocket conn and removes it from the conn list
// returns the error, if any, from the underline connection
Disconnect() error
}
Connection is the front-end API that you will use to communicate with the client side
type ConnectionFunc ¶
type ConnectionFunc func(Connection)
ConnectionFunc is the callback which fires when a client/connection is connected to the server. Receives one parameter which is the Connection
type DisconnectFunc ¶
type DisconnectFunc func()
DisconnectFunc is the callback which fires when a client/connection closed
type Emitter ¶ added in v0.0.3
type Emitter interface {
// EmitMessage sends a native websocket message
EmitMessage([]byte) error
// Emit sends a message on a particular event
Emit(string, interface{}) error
}
Emitter is the message/or/event manager
type ErrorFunc ¶
type ErrorFunc (func(string))
ErrorFunc is the callback which fires when an error happens
type MessageFunc ¶
type MessageFunc interface{}
MessageFunc is the second argument to the Emitter's Emit functions. A callback which should receives one parameter of type string, int, bool or any valid JSON/Go struct
type NativeMessageFunc ¶
type NativeMessageFunc func([]byte)
NativeMessageFunc is the callback for native websocket messages, receives one []byte parameter which is the raw client's message
type OptionSet ¶
type OptionSet func(c *Config)
OptionSet implements the OptionSetter
func BinaryMessages ¶
BinaryMessages set it to true in order to denotes binary data messages instead of utf-8 text compatible if you wanna use the Connection's EmitMessage to send a custom binary data to the client, like a native server-client communication. defaults to false
func CheckOrigin ¶
CheckOrigin sets a handler which will check if different origin(domains) are allowed to contact with the websocket server
func Error ¶
func Error(val func(res http.ResponseWriter, req *http.Request, status int, reason error)) OptionSet
Error sets the error handler
func IDGenerator ¶ added in v0.0.3
IDGenerator used to create (and later on, set) an ID for each incoming websocket connections (clients). The request is an argument which you can use to generate the ID (from headers for example). If empty then the ID is generated by func: randomString(64)
func MaxMessageSize ¶
MaxMessageSize max message size allowed from connection Default value is 1024
func PingPeriod ¶
PingPeriod send ping messages to the connection with this period. Must be less than PongTimeout Default value is (PongTimeout * 9) / 10
func PongTimeout ¶
PongTimeout allowed to read the next pong message from the connection Default value is 60 * time.Second
func ReadBufferSize ¶
ReadBufferSize is the buffer size for the underline reader
func ReadTimeout ¶ added in v0.0.3
ReadTimeout time allowed to read a message from the connection. Default value is 15 * time.Second
func WriteBufferSize ¶
WriteBufferSize is the buffer size for the underline writer
func WriteTimeout ¶
WriteTimeout time allowed to write a message to the connection. Default value is 15 * time.Second
type OptionSetter ¶
type OptionSetter interface {
// Set receives a pointer to the global Config type and does the job of filling it
Set(c *Config)
}
OptionSetter sets a configuration field to the websocket config used to help developers to write less and configure only what they really want and nothing else
type Server ¶
type Server interface {
// Set sets an option aka configuration field to the websocket server
Set(...OptionSetter)
// Handler returns the http.Handler which is setted to the 'Websocket Endpoint path', the client should target to this handler's developer's custom path
// ex: http.Handle("/myendpoint", mywebsocket.Handler())
// Handler calls the HandleConnection, so
// Use Handler or HandleConnection manually, DO NOT USE both.
// Note: you can always create your own upgrader which returns an UnderlineConnection and call only the HandleConnection manually (as Iris web framework does)
Handler() http.Handler
// HandleConnection creates & starts to listening to a new connection
// DO NOT USE Handler() and HandleConnection at the sametime, see Handler for more
// NOTE: You don't need this, this is needed only when we want to 'hijack' the upgrader
// (used for Iris and fasthttp before Iris v6)
HandleConnection(UnderlineConnection)
// OnConnection this is the main event you, as developer, will work with each of the websocket connections
OnConnection(cb ConnectionFunc)
// IsConnected returns true if the connection with that ID is connected to the server
// useful when you have defined a custom connection id generator (based on a database)
// and you want to check if that connection is already connected (on multiple tabs)
IsConnected(connID string) bool
// Join joins a websocket client to a room,
// first parameter is the room name and the second the connection.ID()
//
// You can use connection.Join("room name") instead.
Join(roomName string, connID string)
// LeaveAll kicks out a connection from ALL of its joined rooms
LeaveAll(connID string)
// Leave leaves a websocket client from a room,
// first parameter is the room name and the second the connection.ID()
//
// You can use connection.Leave("room name") instead.
Leave(roomName string, connID string)
// Disconnect force-disconnects a websocket connection
// based on its connection.ID()
// What it does?
// 1. remove the connection from the list
// 2. leave from all joined rooms
// 3. fire the disconnect callbacks, if any
// 4. close the underline connection and return its error, if any.
//
// You can use the connection.Disconnect() instead.
Disconnect(connID string) error
// Serve is here only for compatibility.
// Look here for more: https://github.com/kataras/iris/blob/5.0.0/websocket.go#L125
Serve()
}
Server is the websocket server, listens on the config's port, the critical part is the event OnConnection
type UnderlineConnection ¶
type UnderlineConnection interface {
// SetWriteDeadline sets the write deadline on the underlying network
// connection. After a write has timed out, the websocket state is corrupt and
// all future writes will return an error. A zero value for t means writes will
// not time out.
SetWriteDeadline(t time.Time) error
// SetReadDeadline sets the read deadline on the underlying network connection.
// After a read has timed out, the websocket connection state is corrupt and
// all future reads will return an error. A zero value for t means reads will
// not time out.
SetReadDeadline(t time.Time) error
// SetReadLimit sets the maximum size for a message read from the peer. If a
// message exceeds the limit, the connection sends a close frame to the peer
// and returns ErrReadLimit to the application.
SetReadLimit(limit int64)
// SetPongHandler sets the handler for pong messages received from the peer.
// The appData argument to h is the PONG frame application data. The default
// pong handler does nothing.
SetPongHandler(h func(appData string) error)
// SetPingHandler sets the handler for ping messages received from the peer.
// The appData argument to h is the PING frame application data. The default
// ping handler sends a pong to the peer.
SetPingHandler(h func(appData string) error)
// WriteControl writes a control message with the given deadline. The allowed
// message types are CloseMessage, PingMessage and PongMessage.
WriteControl(messageType int, data []byte, deadline time.Time) error
// WriteMessage is a helper method for getting a writer using NextWriter,
// writing the message and closing the writer.
WriteMessage(messageType int, data []byte) error
// ReadMessage is a helper method for getting a reader using NextReader and
// reading from that reader to a buffer.
ReadMessage() (messageType int, p []byte, err error)
// NextWriter returns a writer for the next message to send. The writer's Close
// method flushes the complete message to the network.
//
// There can be at most one open writer on a connection. NextWriter closes the
// previous writer if the application has not already done so.
NextWriter(messageType int) (io.WriteCloser, error)
// Close closes the underlying network connection without sending or waiting for a close frame.
Close() error
}
UnderlineConnection is used for compatible with fasthttp and net/http underline websocket libraries we only need ~8 funcs from websocket.Conn so:
