Skip to content

Instantly share code, notes, and snippets.

@jeffsrepoaccount
Last active February 24, 2016 12:54
Show Gist options
  • Save jeffsrepoaccount/cf40125a624676c39e9e to your computer and use it in GitHub Desktop.
Save jeffsrepoaccount/cf40125a624676c39e9e to your computer and use it in GitHub Desktop.
AngularJS WebSocket Service
(function() {
'use strict';
var app = angular.module('jrl.websockets', [
'jrl.utils'
]);
app.service('websocket', [
'$q', '$timeout', 'common',
function($q, $timeout, common) {
var connectType = 'connect',
logError = common.getLogFn('websocket', 'error'),
logWarn = common.getLogFn('websocket', 'warn'),
logSuccess = common.getLogFn('websocket', 'success'),
logInfo = common.getLogFn('websocket', 'info'),
connected = false,
handlers = {}, // Message Handlers
onConnectQ = [], // Q'd messages to send on connection
connection = null, // Socket Connection Handle
timeoutTimer
;
addMessageHandler('connected', function(){});
return {
addMessageHandler: addMessageHandler,
closeConnection: closeConnection,
connect: connect,
send: send,
queueMessage: queueMessage,
clientId: null
};
// {{{ addMessageHandler()
/**
* Adds a callback handler for a given message type
*/
function addMessageHandler(type, handler) {
// TODO: Somehow make this a list of message handlers?
//if(typeof handlers[type] === 'undefined') {
// handlers[type] = [];
//}
handlers[type] = handler;
return this;
}
// }}}
// {{{ connect()
/**
* Connect to web socket server. Supports connection timeout with
* either the default delay or one specified elsewhere. Connection
* is resolved once open, so on message events should be handled
* elsewhere.
*
* @param user
* @param string server
* @param string port
* @param int timeout
* @return promise
*/
function connect(user, server, port, timeout) {
logInfo('Connecting to websocket server..', user);
var defer = $q.defer();
if(user.name === 'undefined') {
logError('Invalid user, no name. Cannot connect', user);
defer.reject();
return defer.promise;
}
// Open connection, and resolve promise when it's open
if(connection !== undefined && connection !== null) {
defer.resolve(connection);
return defer.promise;
}
connection = new WebSocket('ws://' + server + ':' + port);
connection.onopen = function() {
logSuccess('Connection to socket server established');
connected = true;
$timeout.cancel(timeoutTimer);
// Send initial connection message
send({
type: connectType,
name: user.name,
data: {}
});
defer.resolve(connection);
if(onConnectQ.length) {
logInfo('Sending ' + onConnectQ.length + ' queued message(s)');
onConnectQ.forEach(function(message) {
send(message.message);
message.promise.resolve();
});
onConnectQ = [];
}
};
connection.onclose = function() {
logInfo('Connection to socket server closed');
connection = undefined;
connected = false;
if(handlers.hasOwnProperty('connection.closed')) {
handlers['connection.closed']()
}
};
connection.onmessage = function(message) {
message = JSON.parse(message.data);
if(typeof message.type !== 'undefined' &&
handlers.hasOwnProperty(message.type)
) {
logInfo('Message Received', {type: message.type, data: message.data});
handlers[message.type](message.data);
} else {
logWarn('Unhandled message', message);
}
};
// Set timeout and reject the promise if reached
timeoutTimer = $timeout(function() {
logError('Connection to socket server timed out');
connection = undefined;
defer.reject();
// Reject any queued promises
if(onConnectQ.length) {
onConnectQ.forEach(function(message) {
message.promise.reject();
});
onConnectQ = [];
}
}, timeout);
return defer.promise;
}
// }}}
// {{{ closeConnection()
/**
* Closes connection with the web socket server
*
* @return boolean
*/
function closeConnection( ) {
if(connection !== undefined) {
connection.close();
connection = undefined;
}
return true;
}
// }}}
// {{{ send()
/**
* Sends a message to the web socket server. Converts the object to
* JSON prior to transmission.
*
* @param Object msg
* @return boolean
*/
function send( msg ) {
logInfo('Sending message', msg);
if(!validateMessage(msg)) {
logError('Invalid message, no name', msg);
return false;
}
if(connection === undefined) {
logError('No connection established');
return false;
}
connection.send(JSON.stringify(msg));
return true;
}
// }}}
// {{{ queueMessage()
/**
* Queued messages will be sent upon connection. A promise is returned
* to allow calling contexts to be notified once a message is sent or
* if connection failed.
* @param Object msg
* @return promise
*/
function queueMessage(msg) {
var defer = $q.defer();
if(!validateMessage(msg)) {
defer.reject();
return defer.promise;
}
if(connected) {
// Connection is already established, go ahead and send message
send(msg);
defer.resolve();
return defer.promise;
}
logInfo('Queuing message', msg);
onConnectQ.push({ message: msg, promise: defer});
return defer.promise;
}
// }}}
// {{{ validateMessage()
/**
* Returns false if a message is not valid, true otherwise.
*/
function validateMessage(msg) {
if(!msg.name) {
return false;
}
return true;
}
// }}}
}
]);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment