Documentation
¶
Overview ¶
Package websocket provides an easy way to setup server and client side rich websocket experience for Iris As originally written by me at https://github.com/kataras/go-websocket based on v0.1.1
Index ¶
Constants ¶
const ( // DefaultWebsocketWriteTimeout 0, no timeout DefaultWebsocketWriteTimeout = 0 // DefaultWebsocketReadTimeout 0, no timeout DefaultWebsocketReadTimeout = 0 // 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 // DefaultClientSourcePath "/iris-ws.js" DefaultClientSourcePath = "/iris-ws.js" )
const ( // All is the string which the Emitter use to send a message to all All = "" // Broadcast is the string which the Emitter use to send a message to all except this connection Broadcast = ";gowebsocket;to;all;except;me;" )
const ( // WriteWait is 1 second at the internal implementation, // same as here but this can be changed at the future* WriteWait = 1 * time.Second )
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 = "iris-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, javascript-side should contains an empty second parameter.");
}
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.fireMessage = function (event, message) {
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);
}
}
}
}
};
//
// 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 ( // DefaultIDGenerator returns the result of 64 // random combined characters as the id of a new connection. // Used when config.IDGenerator is nil DefaultIDGenerator = func(*iris.Context) string { return randomString(64) } )
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct { // Endpoint is the path which the websocket server will listen for clients/connections // Default value is empty string, if you don't set it the Websocket server is disabled. Endpoint string // ClientSourcePath is is the path which the client-side // will be auto-served when the server adapted to an Iris station. // Default value is "/iris-ws.js" ClientSourcePath string Error func(w http.ResponseWriter, r *http.Request, status int, reason error) CheckOrigin func(r *http.Request) bool // WriteTimeout time allowed to write a message to the connection. // 0 means no timeout. // Default value is 0 WriteTimeout time.Duration // ReadTimeout time allowed to read a message from the connection. // 0 means no timeout. // Default value is 0 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 60 *time.Second 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 // Default value is 4096 ReadBufferSize int // WriteBufferSize is the buffer size for the underline writer // Default value is 4096 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(ctx *iris.Context) string // Subprotocols specifies the server's supported protocols in order of // preference. If this field is set, then the Upgrade method negotiates a // subprotocol by selecting the first match in this list with a protocol // requested by the client. Subprotocols []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 // Context returns the (upgraded) *iris.Context of this connection // avoid using it, you normally don't need it, // websocket has everything you need to authenticate the user BUT if it's necessary // then you use it to receive user information, for example: from headers Context() *iris.Context // 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 // Returns true if the connection has actually left from the particular room. Leave(string) bool // OnLeave registeres a callback which fires when this connection left from any joined room. // This callback is called automatically on Disconnected client, because websocket server automatically // deletes the disconnected connection from any joined rooms. // // Note: the callback(s) called right before the server deletes the connection from the room // so the connection theoritical can still send messages to its room right before it is being disconnected. OnLeave(roomLeaveCb LeaveRoomFunc) // 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 // SetValue sets a key-value pair on the connection's mem store. SetValue(key string, value interface{}) // GetValue gets a value by its key from the connection's mem store. GetValue(key string) interface{} // GetValueArrString gets a value as []string by its key from the connection's mem store. GetValueArrString(key string) []string // GetValueString gets a value as string by its key from the connection's mem store. GetValueString(key string) string // GetValueInt gets a value as integer by its key from the connection's mem store. GetValueInt(key string) int }
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 ConnectionValues ¶
type ConnectionValues []connectionValue
ConnectionValues is the temporary connection's memory store
func (*ConnectionValues) Get ¶
func (r *ConnectionValues) Get(key string) interface{}
Get returns a value based on its key
func (*ConnectionValues) Set ¶
func (r *ConnectionValues) Set(key string, value interface{})
Set sets a value based on the key
type DisconnectFunc ¶
type DisconnectFunc func()
DisconnectFunc is the callback which fires when a client/connection closed
type Emitter ¶
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 LeaveRoomFunc ¶
type LeaveRoomFunc func(roomName string)
LeaveRoomFunc is the callback which fires when a client/connection leaves from any room. This is called automatically when client/connection disconnected (because websocket server automatically leaves from all joined rooms)
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 Server ¶
type Server interface { // Adapt implements the iris' adaptor, it adapts the websocket server to an Iris station. // see websocket.go Adapt(frame *iris.Policies) // Handler returns the iris.HandlerFunc // which is setted to the 'Websocket Endpoint path', // the client should target to this handler's developer's custom path // ex: iris.Default.Any("/myendpoint", mywebsocket.Handler()) Handler() iris.HandlerFunc // 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. // Returns true if the connection has actually left from the particular room. Leave(roomName string, connID string) bool // GetConnectionsByRoom returns a list of Connection // are joined to this room. GetConnectionsByRoom(roomName string) []Connection // 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 }
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: