Last active
February 24, 2016 12:54
-
-
Save jeffsrepoaccount/cf40125a624676c39e9e to your computer and use it in GitHub Desktop.
AngularJS WebSocket Service
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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