Fork me on GitHub Knox

Knox

Light-weight Amazon S3 client for NodeJS.

auth

lib/knox/auth.js

Module dependencies.

var crypto = require('crypto');

Return an "Authorization" header value with the given options in the form of "AWS <key>:<signature>"

  • param: Object options

  • return: String

  • api: private

exports.authorization = function(options){
  return 'AWS ' + options.key + ':' + exports.sign(options);
};

Create a base64 sha1 HMAC for options.

  • param: Object options

  • return: String

  • api: private

exports.sign = function(options){
  var str = exports.stringToSign(options);
  return crypto.createHmac('sha1', options.secret).update(str).digest('base64');
};

Return a string for sign() with the given options.

Spec

<verb>\n <md5>\n <content-type>\n <date>\n [headers\n] <resource>

  • param: Object options

  • return: String

  • api: private

exports.stringToSign = function(options){
  var headers = options.amazonHeaders || '';
  if (headers) headers += '\n';
  return [
      options.verb
    , options.md5
    , options.contentType
    , options.date.toUTCString()
    , headers + options.resource
  ].join('\n');
};

Perform the following:

  • ignore non-amazon headers
  • lowercase fields
  • sort lexicographically
  • trim whitespace between ":"
  • join with newline

  • param: Object headers

  • return: String

  • api: private

exports.canonicalizeHeaders = function(headers){
  var buf = []
    , fields = Object.keys(headers);
  for (var i = 0, len = fields.length; i &lt; len; ++i) {
    var field = fields[i]
      , val = headers[field]
      , field = field.toLowerCase();
    if (0 !== field.indexOf('x-amz')) continue;
    buf.push(field + ':' + val);
  }
  return buf.sort().join('\n');
};

client

lib/knox/client.js

Module dependencies.

var utils = require('./utils')
  , auth = require('./auth')
  , http = require('http')
  , join = require('path').join;

Initialize a Client with the given options.

Required

  • key amazon api key
  • secret amazon secret
  • bucket bucket name string, ex: "learnboost"

  • param: Object options

  • api: public

var Client = module.exports = exports = function Client(options) {
  this.host = 's3.amazonaws.com';
  utils.merge(this, options);
  if (!this.key) throw new Error('aws "key" required');
  if (!this.secret) throw new Error('aws "secret" required');
  if (!this.bucket) throw new Error('aws "bucket" required');
};

Request with filename the given method, and optional headers.

  • param: String method

  • param: String filename

  • param: Object headers

  • return: ClientRequest

  • api: private

Client.prototype.request = function(method, filename, headers){
  var client = http.createClient(80, this.host)
    , path = join('/', this.bucket, filename)
    , date = new Date
    , headers = headers || {};

  // Default headers
  utils.merge(headers, {
      Date: date.toUTCString()
    , Host: this.host
  });

  // Authorization header
  headers.Authorization = auth.authorization({
      key: this.key
    , secret: this.secret
    , verb: method
    , date: date
    , resource: path
    , contentType: headers['Content-Type']
    , amazonHeaders: auth.canonicalizeHeaders(headers)
  });

  var req = client.request(method, path, headers);
  req.url = this.url(filename);
  return req;
};

PUT data to filename with optional headers.

Example

// Fetch the size
fs.stat('Readme.md', function(err, stat){
 // Create our request
 var req = client.put('/test/Readme.md', {
     'Content-Length': stat.size
   , 'Content-Type': 'text/plain'
 });
 fs.readFile('Readme.md', function(err, buf){
   // Output response
   req.on('response', function(res){
     console.log(res.statusCode);
     console.log(res.headers);
     res.on('data', function(chunk){
       console.log(chunk.toString());
     });
   }); 
   // Send the request with the file's Buffer obj
   req.end(buf);
 });
});

  • param: String filename

  • param: Object headers

  • return: ClientRequest

  • api: public

Client.prototype.put = function(filename, headers){
  headers = utils.merge({
      Expect: '100-continue'
    , 'x-amz-acl': 'public-read'
  }, headers || {});
  return this.request('PUT', filename, headers);
};

GET filename with optional headers.

  • param: String filename

  • param: Object headers

  • return: ClientRequest

  • api: public

Client.prototype.get = function(filename, headers){
  return this.request('GET', filename, headers);
};

Issue a HEAD request on filename with optional `headers.

  • param: String filename

  • param: Object headers

  • return: ClientRequest

  • api: public

Client.prototype.head = function(filename, headers){
  return this.request('HEAD', filename, headers);
};

DELETE filename with optional `headers.

  • param: String filename

  • param: Object headers

  • return: ClientRequest

  • api: public

Client.prototype.del = function(filename, headers){
  return this.request('DELETE', filename, headers);
};

Return a url to the given filename.

  • param: String filename

  • return: String

  • api: public

Client.prototype.url = function(filename){
  return 'http://' + this.bucket + '.' + this.host + join('/', filename);
};

Shortcut for new Client().

  • param: Object options

  • see: Client ()

  • api: public

exports.createClient = function(options){
  return new Client(options);
};

index

lib/knox/index.js

Client is the main export.

exports = module.exports = require('./client');

Library version.

  • type: String

exports.version = '0.0.1';

Expose utilities.

  • type: Object

exports.utils = require('./utils');

Expose auth utils.

  • type: Object

exports.auth = require('./auth');

utils

lib/knox/utils.js

Merge object b with object a.

  • param: Object a

  • param: Object b

  • return: Object a

  • api: private

exports.merge = function(a, b){
  var keys = Object.keys(b);
  for (var i = 0, len = keys.length; i &lt; len; ++i) {
    var key = keys[i];
    a[key] = b[key]
  }
  return a;
};

Base64.

exports.base64 = {

Base64 encode the given str.

  • param: String str

  • return: String

  • api: private

encode: function(str){
    return new Buffer(str).toString('base64');
  },

Base64 decode the given str.

  • param: String str

  • return: String

  • api: private

decode: function(str){
    return new Buffer(str, 'base64').toString();
  }
};