Documentation ¶
Overview ¶
Package websocket provides an easy way to setup a rich Websocket server and client side.
Index ¶
- Constants
- Variables
- func HandleConnection(websocketConn UnderlineConnection)
- func Handler() http.Handler
- func OnConnection(cb ConnectionFunc)
- func Random(n int) []byte
- func RandomString(n int) string
- func Serve()
- func Set(setters ...OptionSetter)
- type Config
- type Connection
- type ConnectionFunc
- type DisconnectFunc
- type Emmiter
- 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 MaxMessageSize(val int64) OptionSet
- func PingPeriod(val time.Duration) OptionSet
- func PongTimeout(val time.Duration) OptionSet
- func ReadBufferSize(val int) OptionSet
- func WriteBufferSize(val int) OptionSet
- func WriteTimeout(val time.Duration) OptionSet
- type OptionSetter
- type Rooms
- type Server
- type UnderlineConnection
Constants ¶
const ( // DefaultWebsocketWriteTimeout 15 * time.Second DefaultWebsocketWriteTimeout = 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 Emmiter use to send a message to all All = "" // NotMe is the string which the Emmiter use to send a message to all except this connection NotMe = ";gowebsocket;to;all;except;me;" // Broadcast is the string which the Emmiter 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.2"
)
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.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 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
Functions ¶
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 OnConnection ¶
func OnConnection(cb ConnectionFunc)
OnConnection this is the main event you, as developer, will work with each of the websocket connections
func Random ¶
Random takes a parameter (int) and returns random slice of byte ex: var randomstrbytes []byte; randomstrbytes = utils.Random(32)
func RandomString ¶
RandomString accepts a number(10 for example) and returns a random string using simple but fairly safe random algorithm
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 // 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 }
Config the websocket server configuration all of these are optional.
type Connection ¶
type Connection interface { // Emmiter implements EmitMessage & Emit Emmiter // 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 emmiter to send messages To(string) Emmiter // 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 Emmiter ¶
type Emmiter interface { // EmitMessage sends a native websocket message EmitMessage([]byte) error // Emit sends a message on a particular event Emit(string, interface{}) error }
Emmiter 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 Emmiter'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 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 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: to see examples on how to manually use the HandleConnection, see one of my repositories: // look at https://github.com/iris-contrib/websocket, which is an edited version from gorilla/websocket to work with iris // and https://github.com/kataras/iris/blob/master/websocket.go // from fasthttp look at the https://github.com/fasthttp-contrib/websocket, which is an edited version from gorilla/websocket to work with fasthttp HandleConnection(UnderlineConnection) // OnConnection this is the main event you, as developer, will work with each of the websocket connections OnConnection(cb ConnectionFunc) // Serve starts the websocket server, it's a non-blocking function (runs from a new goroutine) 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) // 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 Iris(fasthttp web framework) we only need ~4 funcs from websocket.Conn so: