Comparing version 0.3.1 to 0.4.0
@@ -19,3 +19,5 @@ /*! | ||
, fs = require('fs') | ||
, crypto = require('crypto'); | ||
, crypto = require('crypto') | ||
, xml2js = require('xml2js') | ||
, qs = require('querystring'); | ||
@@ -44,2 +46,11 @@ // The max for multi-object delete, bucket listings, etc. | ||
function getContentLength(headers) { | ||
for (var header in headers) { | ||
if (header.toLowerCase() === 'content-length') { | ||
return headers[header]; | ||
} | ||
} | ||
return null; | ||
} | ||
/** | ||
@@ -69,3 +80,12 @@ * Initialize a `Client` with the given `options`. | ||
this.endpoint = options.bucket + '.s3.amazonaws.com'; | ||
var domain = 's3.amazonaws.com'; | ||
if (options.region) { | ||
if (options.region === 'us-standard') { | ||
// Pesky inconsistency | ||
domain = 's3.amazonaws.com'; | ||
} else { | ||
domain = 's3-' + options.region + '.amazonaws.com'; | ||
} | ||
} | ||
this.endpoint = options.bucket + '.' + domain; | ||
this.secure = 'undefined' == typeof options.port; | ||
@@ -88,3 +108,3 @@ utils.merge(this, options); | ||
Client.prototype.request = function(method, filename, headers){ | ||
var options = { host: this.endpoint } | ||
var options = { host: this.endpoint, agent: this.agent } | ||
, date = new Date | ||
@@ -234,3 +254,4 @@ , headers = headers || {}; | ||
Client.prototype.putStream = function(stream, filename, headers, fn){ | ||
if (!('Content-Length' in headers)) { | ||
var contentLength = getContentLength(headers); | ||
if (contentLength === null) { | ||
process.nextTick(function () { | ||
@@ -249,9 +270,8 @@ fn(new Error('You must specify a Content-Length header.')); | ||
var written = 0; | ||
var total = headers['Content-Length']; | ||
stream.on('data', function(chunk){ | ||
written += chunk.length; | ||
req.emit('progress', { | ||
percent: written / total * 100 | 0 | ||
percent: written / contentLength * 100 | 0 | ||
, written: written | ||
, total: total | ||
, total: contentLength | ||
}); | ||
@@ -487,2 +507,101 @@ }); | ||
/** | ||
* Possible params for Client#list. | ||
* | ||
* @type {Object} | ||
*/ | ||
var LIST_PARAMS = { | ||
delimiter: true | ||
, marker: true | ||
,'max-keys': true | ||
, prefix: true | ||
}; | ||
/** | ||
* Normalization map for Client#list. | ||
* | ||
* @type {Object} | ||
*/ | ||
var RESPONSE_NORMALIZATION = { | ||
MaxKeys: Number, | ||
IsTruncated: Boolean, | ||
LastModified: Date, | ||
Size: Number | ||
}; | ||
/** | ||
* Convert data we get from S3 xml in Client#list, since every primitive | ||
* value there is a string. | ||
* | ||
* @type {Object} | ||
*/ | ||
function normalizeResponse(data) { | ||
for (var key in data) { | ||
var Constr = RESPONSE_NORMALIZATION[key]; | ||
if (Constr) { | ||
if (Constr === Date) { | ||
data[key] = new Date(data[key]); | ||
} else { | ||
data[key] = Constr(data[key]); | ||
} | ||
} else if (Array.isArray(data[key])) { | ||
data[key].forEach(normalizeResponse); | ||
} | ||
} | ||
} | ||
/** | ||
* List up to 1000 objects at a time, with optional `headers`, `params` | ||
* and callback `fn` with a possible exception and the response. | ||
* | ||
* @param {Object|Function} params | ||
* @param {Object|Function} headers | ||
* @param {Function} fn | ||
* @api public | ||
*/ | ||
Client.prototype.list = function(params, headers, fn){ | ||
if ('function' == typeof headers) { | ||
fn = headers; | ||
headers = {}; | ||
} | ||
if ('function' == typeof params) { | ||
fn = params; | ||
params = null; | ||
} | ||
if (params && !LIST_PARAMS[Object.keys(params)[0]]) { | ||
headers = params; | ||
params = null; | ||
} | ||
var url = params ? '?' + qs.stringify(params) : ''; | ||
this.getFile(url, headers, function(err, res){ | ||
if (err) return fn(err); | ||
var xmlStr = ''; | ||
res.on('data', function(chunk){ | ||
xmlStr += chunk; | ||
}); | ||
res.on('end', function(){ | ||
new xml2js.Parser({explicitArray: false, explicitRoot: false}) | ||
.parseString(xmlStr, function(err, data){ | ||
if (err) return fn(err); | ||
delete data.$; | ||
normalizeResponse(data); | ||
fn(null, data); | ||
}); | ||
}).on('error', fn); | ||
}); | ||
}; | ||
/** | ||
* Return a url to the given `filename`. | ||
@@ -489,0 +608,0 @@ * |
@@ -5,7 +5,8 @@ { | ||
"keywords": ["aws", "amazon", "s3"], | ||
"version": "0.3.1", | ||
"version": "0.4.0", | ||
"author": "TJ Holowaychuk <tj@learnboost.com>", | ||
"contributors": [ | ||
"TJ Holowaychuk <tj@learnboost.com>", | ||
"Domenic Denicola <domenic@domenicdenicola.com>" | ||
"Domenic Denicola <domenic@domenicdenicola.com>", | ||
"Oleg Slobodksoi <oleg008@gmail.com>" | ||
], | ||
@@ -22,3 +23,4 @@ "license": "MIT", | ||
"dependencies": { | ||
"mime": "*" | ||
"mime": "*", | ||
"xml2js": "0.2.x" | ||
}, | ||
@@ -29,3 +31,3 @@ "devDependencies": { | ||
"scripts": { | ||
"test": "mocha --slow 500ms --reporter spec" | ||
"test": "mocha" | ||
}, | ||
@@ -32,0 +34,0 @@ "directories": { |
111
Readme.md
@@ -26,6 +26,3 @@ # knox | ||
By default knox will send all requests to the global endpoint | ||
(bucket.s3.amazonaws.com). This works regardless of the region where the bucket | ||
is. But if you want to manually set the endpoint (for performance reasons) you | ||
can do it with the `endpoint` option. | ||
More options are documented below for features like other endpoints or regions. | ||
@@ -55,7 +52,6 @@ ### PUT | ||
By default the _x-amz-acl_ header is _public-read_, meaning anyone can __GET__ | ||
the file. To alter this simply pass this header to the client request method. | ||
By default the _x-amz-acl_ header is _private_. To alter this simply pass this header to the client request method. | ||
```js | ||
client.put('/test/obj.json', { 'x-amz-acl': 'private' }); | ||
client.put('/test/obj.json', { 'x-amz-acl': 'public-read' }); | ||
``` | ||
@@ -181,2 +177,29 @@ | ||
and [listing all the files in your bucket][list]: | ||
```js | ||
client.list({ prefix: 'my-prefix' }, function(err, data){ | ||
/* `data` will look roughly like: | ||
{ | ||
Prefix: 'my-prefix', | ||
IsTruncated: true, | ||
MaxKeys: 1000, | ||
Contents: [ | ||
{ | ||
Key: 'whatever' | ||
LastModified: new Date(2012, 11, 25, 0, 0, 0), | ||
ETag: 'whatever', | ||
Size: 123, | ||
Owner: 'you', | ||
StorageClass: 'whatever' | ||
}, | ||
⋮ | ||
] | ||
} | ||
*/ | ||
}); | ||
``` | ||
And you can always issue ad-hoc requests, e.g. the following to | ||
@@ -192,14 +215,72 @@ [get an object's ACL][acl]: | ||
[list]: http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGET.html | ||
[acl]: http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectGETacl.html | ||
## Client Creation Options | ||
Besides the required `key`, `secret`, and `bucket` options, you can supply any | ||
of the following: | ||
### `endpoint` | ||
By default knox will send all requests to the global endpoint | ||
(bucket.s3.amazonaws.com). This works regardless of the region where the bucket | ||
is. But if you want to manually set the endpoint (for performance reasons) you | ||
can do it with the `endpoint` option. | ||
### `region` | ||
For your convenience when using buckets not in the US Standard region, you can | ||
specify the `region` option. When you do so, the `endpoint` hostname is | ||
automatically assembled. | ||
As of this writing, valid values for the `region` option are: | ||
* US Standard (default): `us-standard` | ||
* US West (Oregon): `us-west-2` | ||
* US West (Northern California): `us-west-1` | ||
* EU (Ireland): `eu-west-1` | ||
* Asia Pacific (Singapore): `ap-southeast-1` | ||
* Asia Pacific (Tokyo): `ap-northeast-1` | ||
* South America (Sao Paulo): `sa-east-1` | ||
If new regions are added later, their subdomain names will also work when passed | ||
as the `region` option. See the [AWS endpoint documentation][endpoint-docs] for | ||
the latest list. | ||
**Convenience APIs such as `putFile` and `putStream` currently do not work as | ||
expected with buckets in regions other than US Standard without explicitly | ||
specify the region option.** This will eventually be addressed by resolving | ||
[issue #66][]; however, for performance reasons, it is always best to specify | ||
the region option anyway. | ||
[endpoint-docs]: http://docs.amazonwebservices.com/general/latest/gr/rande.html#s3_region | ||
[issue #66]: https://github.com/LearnBoost/knox/issues/66 | ||
### `secure` and `port` | ||
By default, knox uses HTTPS to connect to S3 on port 443. You can override | ||
either of these with the `secure` and `port` options. Note that if you specify a | ||
custom `port` option, the default for `secure` switches to `false`, although | ||
you can override it manually if you want to run HTTPS against a specific port. | ||
### `agent` | ||
If you want to use a custom [HTTP agent][], you can specify this with the | ||
`agent` option. | ||
[HTTP agent]: http://nodejs.org/docs/latest/api/http.html#http_class_http_agent | ||
## Running Tests | ||
To run the test suite you must first have an S3 account, and create | ||
a file named _./auth_, which contains your credentials as json, for example: | ||
To run the test suite you must first have an S3 account. Then create a file named | ||
_./test/auth.json_, which contains your credentials as JSON, for example: | ||
```json | ||
{ | ||
"key":"<api-key-here>", | ||
"secret":"<secret-here>", | ||
"bucket":"<your-bucket-name>" | ||
"key": "<api-key-here>", | ||
"secret": "<secret-here>", | ||
"bucket": "<your-bucket-name>", | ||
"bucketUsWest2": "<bucket-in-us-west-2-region-here>" | ||
} | ||
@@ -210,3 +291,5 @@ ``` | ||
$ npm install | ||
$ npm test | ||
``` | ||
$ npm install | ||
$ npm test | ||
``` |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Non-existent author
Supply chain riskThe package was published by an npm account that no longer exists.
Found 1 instance in 1 package
30541
802
291
0
2
3
+ Addedxml2js@0.2.x
+ Addedsax@0.5.8(transitive)
+ Addedxml2js@0.2.8(transitive)