aws-lib
Version:
Extensible Node.js library for the Amazon Web Services API
169 lines (150 loc) • 5.6 kB
JavaScript
var http = require("http");
var https = require("https");
var qs = require("querystring");
var events = require("events");
var xml2js = require("xml2js");
var utils = require('./utils');
var _ = require('underscore');
// include specific API clients
var ec2 = require("./ec2");
var prodAdv = require("./prodAdv");
var simpledb = require("./simpledb");
var sqs = require("./sqs");
var sns = require("./sns");
var ses = require("./ses");
var elb = require("./elb");
var iam = require("./iam");
var sts = require("./sts");
var cw = require("./cw");
var as = require("./as");
var cfn = require("./cfn");
var emr = require("./emr");
var metadata = require("./metadata.js");
exports.createEC2Client = ec2.init(genericAWSClient);
exports.createProdAdvClient = prodAdv.init(genericAWSClient);
exports.createSimpleDBClient = simpledb.init(genericAWSClient);
exports.createSQSClient = sqs.init(genericAWSClient);
exports.createSNSClient = sns.init(genericAWSClient);
exports.createSESClient = ses.init(genericAWSClient);
exports.createELBClient = elb.init(genericAWSClient);
exports.createIAMClient = iam.init(genericAWSClient);
exports.createSTSClient = sts.init(genericAWSClient);
exports.createCWClient = cw.init(genericAWSClient);
exports.createASClient = as.init(genericAWSClient);
exports.createCFNClient = cfn.init(genericAWSClient);
exports.createEMRClient = emr.init(genericAWSClient);
exports.createMetaDataClient = metadata.init();
// a generic AWS API Client which handles the general parts
function genericAWSClient(obj) {
var securityToken = obj.token;
var signHeader = obj.signHeader;
var host = obj.host;
var accessKeyId = obj.accessKeyId;
var path = obj.path;
var agent = obj.agent;
var secretAccessKey = obj.secretAccessKey;
var secure = obj.secure == null ? true : false;
var connection = secure ? https : http;
return {call: call};
function call(action, query, callback) {
// Wrap the callback to prevent it from being called multiple times.
callback = (function(next) {
var isCalled = false;
return function() {
if (isCalled) return;
isCalled = true;
next.apply(null, arguments);
}
})(callback)
// Try to set credentials with metadata API if no credentials provided
metadata.readCredentials(obj, function(err) {
if (err) return callback(err);
var date = new Date();
query = addQueryProperties(query, securityToken, accessKeyId, date);
var body = qs.stringify(query);
var headers = createHeaders(host, body.length, date, securityToken, accessKeyId, secretAccessKey);
sendRequest();
return;
function sendRequest() {
var options = {
host: host,
path: path,
agent: agent,
method: 'POST',
headers: headers
};
var req = connection.request(options, function (res) {
var data = '';
//the listener that handles the response chunks
res.addListener('data', function (chunk) {
data += chunk.toString()
})
res.addListener('end', function() {
var parser = new xml2js.Parser();
parser.addListener('end', function(result) {
if (typeof result != "undefined") {
var err = result.Error || (result.Errors ? result.Errors.Error : null)
if (err) {
callback(new Error(err.Message), result)
} else {
callback(null, result)
}
} else {
callback(new Error('Unable to parse XML from AWS.'))
}
});
parser.parseString(data);
})
res.addListener('error', callback)
});
req.write(body)
req.addListener('error', callback)
req.end()
}
});
}
function addQueryProperties(query, securityToken, accessKeyId, date) {
var extendedQuery = _.clone(query);
if (securityToken) extendedQuery["SecurityToken"] = securityToken;
extendedQuery["Timestamp"] = date.toISOString();
extendedQuery["AWSAccessKeyId"] = accessKeyId;
extendedQuery["Signature"] = signQuery(extendedQuery);
return extendedQuery;
}
function createHeaders(host, bodyLength, date, securityToken, accessKeyId, secretAccessKey) {
var headers = {
"Host": host,
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
"Content-Length": bodyLength
};
if (signHeader) {
headers["Date"] = date.toUTCString();
if (securityToken !== undefined) headers["x-amz-security-token"] = securityToken;
headers["x-amzn-authorization"] =
"AWS3-HTTPS " +
"AWSAccessKeyId=" + accessKeyId + ", " +
"Algorithm=HmacSHA256, " +
"Signature=" + utils.hmacSha256(secretAccessKey, date.toUTCString());
}
return headers;
}
function signQuery(query) {
var keys = []
var sorted = {}
for(var key in query)
keys.push(key)
keys = keys.sort()
for(var n in keys) {
var key = keys[n]
sorted[key] = query[key]
}
var stringToSign = ["POST", host, path, qs.stringify(sorted)].join("\n");
// Amazon signature algorithm seems to require this
stringToSign = stringToSign.replace(/!/g,"%21");
stringToSign = stringToSign.replace(/'/g,"%27");
stringToSign = stringToSign.replace(/\*/g,"%2A");
stringToSign = stringToSign.replace(/\(/g,"%28");
stringToSign = stringToSign.replace(/\)/g,"%29");
return utils.hmacSha256(secretAccessKey, stringToSign);
}
}