Comparing version 0.1.0 to 0.1.1
@@ -7,3 +7,3 @@ | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
var uri2path = require('file-uri-to-path'); | ||
var NotFoundError = require('./notfound'); | ||
@@ -35,4 +35,4 @@ var NotModifiedError = require('./notmodified'); | ||
// convert URI → Path | ||
// TODO: will probably need better Windows support for this someday (see #3) | ||
var filepath = path.normalize(decodeURIComponent(parsed.pathname)); | ||
var uri = parsed.href; | ||
var filepath = uri2path(uri); | ||
debug('normalized pathname: %j', filepath); | ||
@@ -39,0 +39,0 @@ |
0.1.1 / 2014-02-05 | ||
================== | ||
* http: initial shot at "cached redirects" logic | ||
* package: pin "ftpd" version to v0.2.4 (for tests) | ||
* test: refactor tests into their own files | ||
* file: remove unused `path` require | ||
* test: fix "file:" URI tests on Windows | ||
* file: add better Windows support for file:// URIs | ||
* http: add the Cache-Control and Expires respecting logic | ||
* http: clean up logic a bit | ||
0.1.0 / 2014-01-12 | ||
@@ -3,0 +15,0 @@ ================== |
66
http.js
@@ -37,4 +37,15 @@ | ||
if (cache && isFresh(cache)) { | ||
debug('cache is "fresh" due to previous Expires and/or Cache-Control response headers'); | ||
return fn(new NotModifiedError()); | ||
// check for a 3xx "redirect" status code on the previous cache | ||
var location = cache.headers.location; | ||
var type = (cache.statusCode / 100 | 0); | ||
if (3 == type && location) { | ||
debug('cached redirect'); | ||
fn(new Error('TODO: implement cached redirects!')); | ||
} else { | ||
// otherwise we assume that it's the destination endpoint, | ||
// since there's nowhere else to redirect to | ||
fn(new NotModifiedError()); | ||
} | ||
return; | ||
} | ||
@@ -57,9 +68,13 @@ | ||
if (!options.headers) options.headers = {}; | ||
if (cache.headers['last-modified']) { | ||
options.headers['If-Modified-Since'] = cache.headers['last-modified']; | ||
debug('added "If-Modified-Since" request header: %j', options.headers['If-Modified-Since']); | ||
var lastModified = cache.headers['last-modified']; | ||
if (lastModified != null) { | ||
options.headers['If-Modified-Since'] = lastModified; | ||
debug('added "If-Modified-Since" request header: %j', lastModified); | ||
} | ||
if (cache.headers.etag) { | ||
options.headers['If-None-Match'] = cache.headers.etag; | ||
debug('added "If-None-Match" request header: %j', cache.headers.etag); | ||
var etag = cache.headers.etag; | ||
if (etag != null) { | ||
options.headers['If-None-Match'] = etag; | ||
debug('added "If-None-Match" request header: %j', etag); | ||
} | ||
@@ -157,9 +172,38 @@ } | ||
var cacheControl = cache.headers['cache-control']; | ||
var expires = cache.headers.expires; | ||
var fresh; | ||
if (cacheControl) { | ||
// for Cache-Control rules, see: http://www.mnot.net/cache_docs/#CACHE-CONTROL | ||
debug('Cache-Control: %s', cacheControl); | ||
} | ||
var expires = cache.headers.expires; | ||
if (expires) { | ||
var parts = cacheControl.split(/,\s*?\b/); | ||
for (var i = 0; i < parts.length; i++) { | ||
var part = parts[i]; | ||
var subparts = part.split('='); | ||
var name = subparts[0]; | ||
switch (name) { | ||
case 'max-age': | ||
var val = +subparts[1]; | ||
expires = new Date(+cache.date + (val * 1000)); | ||
fresh = new Date() < expires; | ||
if (fresh) debug('cache is "fresh" due to previous "%s" Cache-Control param', part); | ||
return fresh; | ||
case 'must-revalidate': | ||
// XXX: what we supposed to do here? | ||
break; | ||
case 'no-cache': | ||
case 'no-store': | ||
debug('cache is "stale" due to explicit "%s" Cache-Control param', name); | ||
return false; | ||
} | ||
} | ||
} else if (expires) { | ||
// for Expires rules, see: http://www.mnot.net/cache_docs/#EXPIRES | ||
debug('Expires: %s', expires); | ||
fresh = new Date() < new Date(expires); | ||
if (fresh) debug('cache is "fresh" due to previous Expires response header'); | ||
return fresh; | ||
} | ||
@@ -166,0 +210,0 @@ |
{ | ||
"name": "get-uri", | ||
"version": "0.1.0", | ||
"version": "0.1.1", | ||
"description": "Returns a `stream.Readable` from a URI string", | ||
@@ -35,3 +35,3 @@ "main": "index.js", | ||
"stream-to-array": "~1.0.0", | ||
"ftpd": "~0.2.4", | ||
"ftpd": "0.2.4", | ||
"st": "~0.2.3" | ||
@@ -43,3 +43,4 @@ }, | ||
"debug": "~0.7.4", | ||
"extend": "~1.2.1" | ||
"extend": "~1.2.1", | ||
"file-uri-to-path": "0" | ||
}, | ||
@@ -46,0 +47,0 @@ "optionalDependencies": { |
@@ -32,7 +32,25 @@ get-uri | ||
To simply get a `stream.Readable` instance from a `file:` URI, try something like: | ||
``` js | ||
var getUri = require('get-uri'); | ||
// maps to a `fs.ReadStream` instance | ||
// `file:` maps to a `fs.ReadStream` instance… | ||
getUri('file:///Users/nrajlich/wat.json', function (err, rs) { | ||
if (err) throw err; | ||
rs.pipe(process.stdout); | ||
}); | ||
``` | ||
Missing Endpoints | ||
----------------- | ||
When you pass in a URI in which the resource referenced does not exist on the | ||
destination server, then a `NotFoundError` will be returned. The `code` of the | ||
error instance is set to `"ENOTFOUND"`, so you can special-case that in your code | ||
to detect when a bad filename is requested: | ||
``` js | ||
getUri('http://example.com/resource.json', function (err, rs) { | ||
if (err) { | ||
@@ -47,21 +65,3 @@ if ('ENOTFOUND' == err.code) { | ||
rs.pipe(process.stdout); | ||
// ... some time later, we need to get this same URI again, pass in the | ||
// previous `stream.Readable` instance as `cache` option to potentially | ||
// receive "ENOTMODIFIED" responses: | ||
getUri('file:///Users/nrajlich/wat.json', { cache: rs }, function (err, rs2) { | ||
if (err) { | ||
if ('ENOTFOUND' == err.code) { | ||
// bad file path requested | ||
} else if ('ENOTMODIFIED' == err.code) { | ||
// source file has not been modified since last time it was requested, | ||
// so `rs2` is undefined and you are expected to re-use results from | ||
// a previous call to `getUri()` | ||
} else { | ||
// something else bad happened... | ||
throw err; | ||
} | ||
} | ||
}); | ||
// your app code… | ||
}); | ||
@@ -85,18 +85,34 @@ ``` | ||
When the `"ENOTMODIFIED"` error occurs, then you can safely re-use the | ||
results from the previous `getUri()` call for that same URI. | ||
results from the previous `getUri()` call for that same URI: | ||
``` js | ||
// maps to a `fs.ReadStream` instance | ||
getUri('http://example.com/resource.json', function (err, rs) { | ||
if (err) throw err; | ||
Missing Endpoints | ||
----------------- | ||
// … some time later, if you need to get this same URI again, pass in the | ||
// previous `stream.Readable` instance as `cache` option to potentially | ||
// receive an "ENOTMODIFIED" response: | ||
getUri('http://example.com/resource.json', function (err, rs2) { | ||
if (err) { | ||
if ('ENOTFOUND' == err.code) { | ||
// bad file path requested | ||
} else if ('ENOTMODIFIED' == err.code) { | ||
// source file has not been modified since last time it was requested, | ||
// so `rs2` is undefined and you are expected to re-use results from | ||
// a previous call to `getUri()` | ||
} else { | ||
// something else bad happened... | ||
throw err; | ||
} | ||
} | ||
}); | ||
}); | ||
``` | ||
When you pass in a URI in which the resource referenced does not exist on the | ||
destination server, then a `NotFoundError` will be returned. The `code` of the | ||
error instance is set to `"ENOTFOUND"`, so you can special-case that in your code | ||
to detect when a bad filename is requested. | ||
API | ||
--- | ||
### getUri(String uri[, Object options]) → stream.Readable | ||
### getUri(String uri[, Object options,] Function callback) | ||
@@ -111,2 +127,5 @@ A `uri` String is required. An optional `options` object may be passed in: | ||
Invokes the given `callback` function with a `stream.Readable` instance to | ||
read the resource at the given `uri`. | ||
License | ||
@@ -113,0 +132,0 @@ ------- |
281
test/test.js
@@ -32,283 +32,2 @@ | ||
describe('"data:" protocol', function () { | ||
var cache; | ||
it('should work for URL-encoded data', function (done) { | ||
getUri('data:,Hello%2C%20World!', function (err, rs) { | ||
if (err) return done(err); | ||
cache = rs; | ||
streamToArray(rs, function (err, array) { | ||
if (err) return done(err); | ||
var buf = Buffer.concat(array); | ||
assert.equal('Hello, World!', buf.toString()); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it('should work for base64-encoded data', function (done) { | ||
getUri('data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D', function (err, rs) { | ||
if (err) return done(err); | ||
streamToArray(rs, function (err, array) { | ||
if (err) return done(err); | ||
var buf = Buffer.concat(array); | ||
assert.equal('Hello, World!', buf.toString()); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it('should return ENOTMODIFIED for the same URI with `cache`', function (done) { | ||
getUri('data:,Hello%2C%20World!', { cache: cache }, function (err, rs) { | ||
assert(err); | ||
assert.equal('ENOTMODIFIED', err.code); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe('"file:" protocol', function () { | ||
var cache; | ||
it('should work for local files', function (done) { | ||
var uri = 'file://' + __filename; | ||
fs.readFile(__filename, 'utf8', function (err, real) { | ||
if (err) return done(err); | ||
getUri(uri, function (err, rs) { | ||
if (err) return done(err); | ||
cache = rs; | ||
streamToArray(rs, function (err, array) { | ||
if (err) return done(err); | ||
var str = Buffer.concat(array).toString('utf8'); | ||
assert.equal(str, real); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
it('should return ENOTFOUND for bad filenames', function (done) { | ||
var uri = 'file://' + __filename + 'does-not-exist'; | ||
getUri(uri, function (err, rs) { | ||
assert(err); | ||
assert.equal('ENOTFOUND', err.code); | ||
done(); | ||
}); | ||
}); | ||
it('should return ENOTMODIFIED for the same URI with `cache`', function (done) { | ||
var uri = 'file://' + __filename; | ||
getUri(uri, { cache: cache }, function (err, rs) { | ||
assert(err); | ||
assert.equal('ENOTMODIFIED', err.code); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe('"ftp:" protocol', function () { | ||
var port; | ||
var cache; | ||
var server; | ||
before(function (done) { | ||
var options = { | ||
logLevel: -1, | ||
getInitialCwd: function (socket, fn) { | ||
fn(null, '/'); | ||
}, | ||
getRoot: function (socket) { | ||
return __dirname; | ||
} | ||
}; | ||
var host = '127.0.0.1'; | ||
server = new ftpd.FtpServer(host, options); | ||
server.on('client:connected', function(conn){ | ||
var username; | ||
conn.on('command:user', function(user, success, failure) { | ||
username = user; | ||
success(); | ||
}); | ||
conn.on('command:pass', function(pass, success, failure){ | ||
success(username); | ||
}); | ||
}); | ||
server.listen(0, function () { | ||
port = server.server.address().port; | ||
done(); | ||
}); | ||
}); | ||
after(function (done) { | ||
server.server.once('close', function () { | ||
done(); | ||
}); | ||
server.server.close(); | ||
}); | ||
it('should work for ftp endpoints', function (done) { | ||
var uri = 'ftp://127.0.0.1:' + port + '/test.js'; | ||
fs.readFile(__filename, 'utf8', function (err, real) { | ||
if (err) return done(err); | ||
getUri(uri, function (err, rs) { | ||
if (err) return done(err); | ||
cache = rs; | ||
streamToArray(rs, function (err, array) { | ||
if (err) return done(err); | ||
var str = Buffer.concat(array).toString('utf8'); | ||
assert.equal(str, real); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
it('should return ENOTFOUND for bad filenames', function (done) { | ||
var uri = 'ftp://127.0.0.1:' + port + '/does-not-exist'; | ||
getUri(uri, function (err, rs) { | ||
assert(err); | ||
assert.equal('ENOTFOUND', err.code); | ||
done(); | ||
}); | ||
}); | ||
it('should return ENOTMODIFIED for the same URI with `cache`', function (done) { | ||
var uri = 'ftp://127.0.0.1:' + port + '/test.js'; | ||
getUri(uri, { cache: cache }, function (err, rs) { | ||
assert(err); | ||
assert.equal('ENOTMODIFIED', err.code); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe('"http:" protocol', function () { | ||
var port; | ||
var cache; | ||
var server; | ||
before(function (done) { | ||
// setup target HTTP server | ||
server = http.createServer(st(__dirname)); | ||
server.listen(function () { | ||
port = server.address().port; | ||
done(); | ||
}); | ||
}); | ||
after(function (done) { | ||
server.once('close', function () { done(); }); | ||
server.close(); | ||
}); | ||
it('should work for HTTP endpoints', function (done) { | ||
var uri = 'http://127.0.0.1:' + port + '/test.js'; | ||
fs.readFile(__filename, 'utf8', function (err, real) { | ||
if (err) return done(err); | ||
getUri(uri, function (err, rs) { | ||
if (err) return done(err); | ||
cache = rs; | ||
streamToArray(rs, function (err, array) { | ||
if (err) return done(err); | ||
var str = Buffer.concat(array).toString('utf8'); | ||
assert.equal(str, real); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
it('should return ENOTFOUND for bad filenames', function (done) { | ||
var uri = 'http://127.0.0.1:' + port + '/does-not-exist'; | ||
getUri(uri, function (err, rs) { | ||
assert(err); | ||
assert.equal('ENOTFOUND', err.code); | ||
done(); | ||
}); | ||
}); | ||
it('should return ENOTMODIFIED for the same URI with `cache`', function (done) { | ||
var uri = 'http://127.0.0.1:' + port + '/test.js'; | ||
getUri(uri, { cache: cache }, function (err, rs) { | ||
assert(err); | ||
assert.equal('ENOTMODIFIED', err.code); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe('"https:" protocol', function () { | ||
var port; | ||
var cache; | ||
var server; | ||
before(function (done) { | ||
// setup target HTTPS server | ||
var options = { | ||
key: fs.readFileSync(__dirname + '/server.key'), | ||
cert: fs.readFileSync(__dirname + '/server.crt') | ||
}; | ||
server = https.createServer(options, st(__dirname)); | ||
server.listen(function () { | ||
port = server.address().port; | ||
done(); | ||
}); | ||
}); | ||
after(function (done) { | ||
server.once('close', function () { done(); }); | ||
server.close(); | ||
}); | ||
it('should work for HTTPS endpoints', function (done) { | ||
var uri = 'https://127.0.0.1:' + port + '/test.js'; | ||
fs.readFile(__filename, 'utf8', function (err, real) { | ||
if (err) return done(err); | ||
getUri(uri, { rejectUnauthorized: false }, function (err, rs) { | ||
if (err) return done(err); | ||
cache = rs; | ||
streamToArray(rs, function (err, array) { | ||
if (err) return done(err); | ||
var str = Buffer.concat(array).toString('utf8'); | ||
assert.equal(str, real); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
it('should return ENOTFOUND for bad filenames', function (done) { | ||
var uri = 'https://127.0.0.1:' + port + '/does-not-exist'; | ||
getUri(uri, { rejectUnauthorized: false }, function (err, rs) { | ||
assert(err); | ||
assert.equal('ENOTFOUND', err.code); | ||
done(); | ||
}); | ||
}); | ||
it('should return ENOTMODIFIED for the same URI with `cache`', function (done) { | ||
var uri = 'https://127.0.0.1:' + port + '/test.js'; | ||
getUri(uri, { cache: cache, rejectUnauthorized: false }, function (err, rs) { | ||
assert(err); | ||
assert.equal('ENOTMODIFIED', err.code); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); |
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
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
37033
21
862
158
6
6
6
+ Addedfile-uri-to-path@0
+ Addedfile-uri-to-path@0.0.2(transitive)