websocket

package module
v0.0.0-...-870b2e5 Latest Latest
Warning

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

Go to latest
Published: Nov 19, 2018 License: MIT Imports: 14 Imported by: 2

Documentation

Overview

Package websocket provides an easy way to setup a rich Websocket server and client side.

Index

Constants

View Source
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
)
View Source
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
)
View Source
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

View Source
const (
	// Version current version number
	Version = "0.0.2"
)

Variables

View Source
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

View Source
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

func Handler() http.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

func Random(n int) []byte

Random takes a parameter (int) and returns random slice of byte ex: var randomstrbytes []byte; randomstrbytes = utils.Random(32)

func RandomString

func RandomString(n int) string

RandomString accepts a number(10 for example) and returns a random string using simple but fairly safe random algorithm

func Serve

func Serve()

Serve starts the websocket server

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.

func (Config) Set

func (c Config) Set(main *Config)

Set is the func which makes the OptionSet an OptionSetter, this is used mostly

func (Config) Validate

func (c Config) Validate() Config

Validate validates the configuration

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

func BinaryMessages(val bool) OptionSet

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

func CheckOrigin(val func(req *http.Request) bool) OptionSet

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

func MaxMessageSize(val int64) OptionSet

MaxMessageSize max message size allowed from connection Default value is 1024

func PingPeriod

func PingPeriod(val time.Duration) OptionSet

PingPeriod send ping messages to the connection with this period. Must be less than PongTimeout Default value is (PongTimeout * 9) / 10

func PongTimeout

func PongTimeout(val time.Duration) OptionSet

PongTimeout allowed to read the next pong message from the connection Default value is 60 * time.Second

func ReadBufferSize

func ReadBufferSize(val int) OptionSet

ReadBufferSize is the buffer size for the underline reader

func WriteBufferSize

func WriteBufferSize(val int) OptionSet

WriteBufferSize is the buffer size for the underline writer

func WriteTimeout

func WriteTimeout(val time.Duration) OptionSet

WriteTimeout time allowed to write a message to the connection. Default value is 15 * time.Second

func (OptionSet) Set

func (o OptionSet) Set(c *Config)

Set is the func which makes the OptionSet an OptionSetter, this is used mostly

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 Rooms

type Rooms map[string][]string

Rooms is just a map with key a string and value slice of string

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

func New

func New(setters ...OptionSetter) Server

New creates a websocket server and returns it

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:

Directories

Path Synopsis
_examples

Jump to

Keyboard shortcuts

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