websocket

package
v6.0.0-...-03bcada Latest Latest
Warning

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

Go to latest
Published: Jun 3, 2017 License: MIT, BSD-2-Clause, MIT Imports: 14 Imported by: 25

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

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

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 = "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

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

func (Config) Validate

func (c Config) Validate() Config

Validate validates the configuration

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) Reset

func (r *ConnectionValues) Reset()

Reset clears the values

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

func New

func New(cfg Config) Server

New returns a new websocket server policy adaptor.

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:

Jump to

Keyboard shortcuts

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