eventhubs-js
Version:
Easy to use client for Azure event hubs
164 lines (132 loc) • 4.32 kB
JavaScript
var Q = require("q"),
http = require('https'),
https = require('https'),
crypto = require('crypto'),
moment = require('moment');
var namespace,
hubName,
keyName,
key,
sasToken,
sasTokens = {}; //key=uri, value=SAS token
function getSasToken(uri) {
if(sasToken) {
return sasToken;
}
var expiration,
toSign,
hmac,
encodedHmac,
token;
//Cache tokens to avoid recalculations
token = sasTokens[uri];
// remove expired tokens from the cache
if(token && moment().unix() < token.expiration) {
return token.token;
}
expiration = moment().add(15, 'days').unix();
toSign = encodeURIComponent(uri) + '\n' + expiration;
hmac = crypto.createHmac('sha256', key);
hmac.update(toSign);
encodedHmac = hmac.digest('base64');
token = 'SharedAccessSignature sr=' + encodeURIComponent(uri) + '&sig=' + encodeURIComponent(encodedHmac) + '&se=' + expiration + '&skn=' + keyName;
sasTokens[uri] = {
token: token,
expiration: expiration
};
console.log('Generated SAS token "' + token + '"');
return token;
}
//https://msdn.microsoft.com/en-us/library/azure/dn790664.aspx
function getDeviceUri(deviceId) {
var uri = null;
if (!deviceId) {
uri = 'https://' + namespace
+ '.servicebus.windows.net' + '/' + hubName + '/messages';
} else {
uri = 'https://' + namespace
+ '.servicebus.windows.net' + '/' + hubName
+ '/publishers/' + encodeURIComponent(deviceId) + '/messages';
}
return uri;
}
//hubNamespace, hubName, keyName, key
function init(options) {
namespace = options.hubNamespace;
hubName = options.hubName;
keyName = options.keyName;
key = options.key;
sasToken = options.sasToken;
//Allow the connection pool to grow larger. This improves performance
//by a factor of 10x in my testing
http.globalAgent.maxSockets = 50;
}
function sendMessage(messageObject) {
var message = messageObject.message,
deviceId = messageObject.deviceId,
payload;
if (typeof message === 'string') {
payload = message;
} else {
//This allows us to send in a POJSO
payload = JSON.stringify(message);
}
return send(payload, deviceId);
}
function sendMessages(messageObject) {
var messages = messageObject.messages,
deviceId = messageObject.deviceId,
payload;
if (messages.length === 0)
throw new Error("Given argument contains no messages!");
payload = JSON.stringify(messages);
return send(payload, deviceId);
}
function send(payload, deviceId) {
var deferral,
requestOptions,
deviceUri,
token,
req,
responseData = '';
deferral = Q.defer();
deviceUri = getDeviceUri(deviceId);
token = getSasToken(deviceUri);
requestOptions = {
hostname: namespace + '.servicebus.windows.net',
port: 443,
path: deviceId ? '/' + hubName + '/publishers/' + deviceId + '/messages' : '/' + hubName + '/messages',
method: 'POST',
headers: {
'Authorization': token,
'Content-Length': Buffer.byteLength(payload),
'Content-Type': 'application/atom+xml;type=entry;charset=utf-8'
}
}
req = https.request(requestOptions, function (res) {
res.on('data', function (data) {
responseData += data;
}).on('end', function () {
if (res.statusCode !== 201) {
deferral.reject(new Error("Invalid server status: " + res.statusCode + "Message: " + responseData));
return;
}
deferral.resolve({
statusCode: res.statusCode,
responseData: responseData
});
}).on('error', function (e) {
deferral.reject(e);
});
});
req.on('error', function(e){
deferral.reject(e);
});
req.setSocketKeepAlive(true);
req.write(payload);
req.end();
return deferral.promise;
}
exports.init = init;
exports.sendMessage = sendMessage;
exports.sendMessages = sendMessages;