Comparing version 2.6.0 to 2.6.1
# restify Changelog | ||
## Not Yet Released | ||
## 2.6.1 | ||
- #478 Add `req.timers` to audit logging plugin. | ||
- #487 RequestCaptureStream: dumpDefault, haveNonRawStreams, zero ring after dump | ||
- #407 - bunyan 0.21.3 | ||
- Add CSV/TSV parser (Dominik Lessel) | ||
- Add `req.timers`: a list of hrtime's for each handler | ||
- Set TCP SO_KEEPALIVE when default KeepAliveAgent is on (client) | ||
## 2.6.0 | ||
@@ -6,0 +13,0 @@ |
@@ -42,2 +42,20 @@ // Copyright 2012 Mark Cavage, Inc. All rights reserved. | ||
/** | ||
* A Bunyan stream to capture records in a ring buffer and only pass through | ||
* on a higher-level record. E.g. buffer up all records but only dump when | ||
* getting a WARN or above. | ||
* | ||
* @param {Object} options contains the parameters: | ||
* - {Object} stream The stream to which to write when dumping captured | ||
* records. One of `stream` or `streams` must be specified. | ||
* - {Array} streams One of `stream` or `streams` must be specified. | ||
* - {Number|String} level The level at which to trigger dumping captured | ||
* records. Defaults to bunyan.WARN. | ||
* - {Number} maxRecords Number of records to capture. Default 100. | ||
* - {Number} maxRequestIds Number of simultaneous request id capturing | ||
* buckets to maintain. Default 1000. | ||
* - {Boolean} dumpDefault If true, then dump captured records on the | ||
* *default* request id when dumping. I.e. dump records logged without | ||
* a "req_id" field. Default false. | ||
*/ | ||
function RequestCaptureStream(opts) { | ||
@@ -50,2 +68,3 @@ assert.object(opts, 'options'); | ||
assert.optionalNumber(opts.maxRequestIds, 'options.maxRequestIds'); | ||
assert.optionalBool(opts.dumpDefault, 'options.dumpDefault'); | ||
@@ -55,3 +74,3 @@ var self = this; | ||
this.level = opts.level || bunyan.WARN; | ||
this.level = opts.level ? bunyan.resolveLevel(opts.level) : bunyan.WARN; | ||
this.limit = opts.maxRecords || 100; | ||
@@ -62,4 +81,4 @@ this.maxRequestIds = opts.maxRequestIds || 1000; | ||
}); | ||
this.dumpDefault = opts.dumpDefault; | ||
this._offset = -1; | ||
@@ -75,2 +94,10 @@ this._rings = []; | ||
opts.streams.forEach(appendStream.bind(null, this.streams)); | ||
this.haveNonRawStreams = false; | ||
for (var i = 0; i < this.streams.length; i++) { | ||
if (!this.streams[i].raw) { | ||
this.haveNonRawStreams = true; | ||
break; | ||
} | ||
} | ||
} | ||
@@ -103,8 +130,28 @@ util.inherits(RequestCaptureStream, Stream); | ||
if (record.level >= this.level) { | ||
ring.records.forEach(function (r) { | ||
var ser = JSON.stringify(r, bunyan.safeCycles()) + '\n'; | ||
var i, r, ser; | ||
for (i = 0; i < ring.records.length; i++) { | ||
r = ring.records[i]; | ||
if (this.haveNonRawStreams) { | ||
ser = JSON.stringify(r, | ||
bunyan.safeCycles()) + '\n'; | ||
} | ||
self.streams.forEach(function (s) { | ||
s.stream.write(s.raw ? r : ser); | ||
}); | ||
}); | ||
} | ||
ring.records.length = 0; | ||
if (this.dumpDefault) { | ||
var defaultRing = self.requestMap.get(DEFAULT_REQ_ID); | ||
for (i = 0; i < defaultRing.records.length; i++) { | ||
r = defaultRing.records[i]; | ||
if (this.haveNonRawStreams) { | ||
ser = JSON.stringify(r, | ||
bunyan.safeCycles()) + '\n'; | ||
} | ||
self.streams.forEach(function (s) { | ||
s.stream.write(s.raw ? r : ser); | ||
}); | ||
} | ||
defaultRing.records.length = 0; | ||
} | ||
} else { | ||
@@ -111,0 +158,0 @@ ring.write(record); |
@@ -19,2 +19,3 @@ // Copyright 2012 Mark Cavage, Inc. All rights reserved. | ||
var once = require('once'); | ||
var tunnelAgent = require('tunnel-agent'); | ||
var uuid = require('node-uuid'); | ||
@@ -161,3 +162,3 @@ | ||
}); | ||
log.trace({client_res: res}, 'Upgrade Response received'); | ||
log.trace({client_res: res}, 'upgrade response received'); | ||
@@ -184,2 +185,5 @@ res.log = log; | ||
clearTimeout(timer); | ||
if (opts._keep_alive) | ||
socket.setKeepAlive(true); | ||
cb(null, req); | ||
@@ -219,3 +223,3 @@ }); | ||
this.log = options.log; | ||
if (!this.log.serializers.client_res) { | ||
if (!this.log.serializers || !this.log.serializers.client_res) { | ||
// Ensure logger has a reasonable serializer for `client_res` | ||
@@ -237,2 +241,12 @@ // logged in this module. | ||
if (process.env.https_proxy) { | ||
this.proxy = url.parse(process.env.https_proxy); | ||
} else if (process.env.http_proxy) { | ||
this.proxy = url.parse(process.env.http_proxy); | ||
} else if (options.proxy) { | ||
this.proxy = options.proxy; | ||
} else { | ||
this.proxy = false; | ||
} | ||
this.retry = cloneRetryOptions(options.retry); | ||
@@ -269,3 +283,18 @@ this.signRequest = options.signRequest || false; | ||
if (this.url.protocol === 'https:') { | ||
if (this.proxy) { | ||
if (this.url.protocol == 'https:') { | ||
if (this.proxy.protocol === 'https:') { | ||
Agent = tunnelAgent.httpsOverHttps; | ||
} else { | ||
Agent = tunnelAgent.httpsOverHttp; | ||
} | ||
} else { | ||
if (this.proxy.protocol === 'https:') { | ||
Agent = tunnelAgent.httpOverHttps; | ||
} else { | ||
Agent = tunnelAgent.httpOverHttp; | ||
} | ||
} | ||
maxSockets = http.globalAgent.maxSockets; | ||
} else if (this.url.protocol === 'https:') { | ||
Agent = KeepAliveAgent.Secure; | ||
@@ -278,14 +307,23 @@ maxSockets = https.globalAgent.maxSockets; | ||
this.agent = new Agent({ | ||
cert: self.cert, | ||
ca: self.ca, | ||
ciphers: self.ciphers, | ||
key: self.key, | ||
maxSockets: maxSockets, | ||
maxKeepAliveRequests: 0, | ||
maxKeepAliveTime: 0, | ||
passphrase: self.passphrase, | ||
pfx: self.pfx, | ||
rejectUnauthorized: self.rejectUnauthorized | ||
}); | ||
if (this.proxy) { | ||
this.agent = new Agent({ | ||
proxy: self.proxy, | ||
rejectUnauthorized: self.rejectUnauthorized, | ||
ca: self.ca | ||
}); | ||
} else { | ||
this.agent = new Agent({ | ||
cert: self.cert, | ||
ca: self.ca, | ||
ciphers: self.ciphers, | ||
key: self.key, | ||
maxSockets: maxSockets, | ||
maxKeepAliveRequests: 0, | ||
maxKeepAliveTime: 0, | ||
passphrase: self.passphrase, | ||
pfx: self.pfx, | ||
rejectUnauthorized: self.rejectUnauthorized | ||
}); | ||
this._keep_alive = true; | ||
} | ||
} | ||
@@ -300,5 +338,7 @@ } | ||
Object.keys((sockets || {})).forEach(function (k) { | ||
sockets[k].forEach(function (s) { | ||
s.end(); | ||
}); | ||
if (Array.isArray(sockets[k])) { | ||
sockets[k].forEach(function (s) { | ||
s.end(); | ||
}); | ||
} | ||
}); | ||
@@ -400,2 +440,3 @@ | ||
opts._keep_alive = this._keep_alive; | ||
call = backoff.call(rawRequest, opts, cb); | ||
@@ -447,3 +488,3 @@ call.setStrategy(new backoff.ExponentialStrategy({ | ||
Object.keys(self.url).forEach(function (k) { | ||
Object.keys(this.url).forEach(function (k) { | ||
if (!opts[k]) | ||
@@ -466,3 +507,3 @@ opts[k] = self.url[k]; | ||
delete opts.headers['content-md5']; | ||
if (opts.headers['content-length']) | ||
if (opts.headers['content-length'] && method !== 'DELETE') | ||
delete opts.headers['content-length']; | ||
@@ -469,0 +510,0 @@ if (opts.headers['transfer-encoding']) |
@@ -34,2 +34,9 @@ // Copyright 2012 Mark Cavage, Inc. All rights reserved. | ||
var timers = {}; | ||
(req.timers || []).forEach(function (time) { | ||
var t = time.time; | ||
var _t = Math.floor((1000000 * t[0]) + | ||
(t[1] / 1000)); | ||
timers[time.name] = _t; | ||
}); | ||
return ({ | ||
@@ -43,3 +50,4 @@ method: req.method, | ||
body: options.body === true ? | ||
req.body : undefined | ||
req.body : undefined, | ||
timers: timers | ||
}); | ||
@@ -46,0 +54,0 @@ }, |
@@ -65,3 +65,5 @@ // Copyright 2012 Mark Cavage, Inc. All rights reserved. | ||
try { | ||
return (httpSignature.parseRequest(request, OPTIONS)); | ||
return (httpSignature.parseRequest(request, { | ||
algorithms: OPTIONS.algorithms | ||
})); | ||
} catch (e) { | ||
@@ -68,0 +70,0 @@ throw new InvalidHeaderError('Authorization header invalid: ' + |
@@ -11,2 +11,3 @@ // Copyright 2012 Mark Cavage, Inc. All rights reserved. | ||
var multipartParser = require('./multipart_parser'); | ||
var fieldedTextParser = require('./fielded_text_body_parser.js'); | ||
@@ -32,2 +33,3 @@ | ||
var parseMultipart = multipartParser(options); | ||
var parseFieldedText = fieldedTextParser(options); | ||
@@ -65,2 +67,12 @@ function parseBody(req, res, next) { | ||
break; | ||
case 'text/tsv': | ||
parser = parseFieldedText; | ||
break; | ||
case 'text/tab-separated-values': | ||
parser = parseFieldedText; | ||
break; | ||
case 'text/csv': | ||
parser = parseFieldedText; | ||
break; | ||
default: | ||
@@ -67,0 +79,0 @@ break; |
@@ -56,3 +56,4 @@ // Copyright 2012 Mark Cavage, Inc. All rights reserved. | ||
if ((req.getContentLength() === 0 && !req.isChunked()) || | ||
req.contentType() === 'multipart/form-data') { | ||
req.contentType() === 'multipart/form-data' || | ||
req.contentType() === 'application/octet-stream') { | ||
next(); | ||
@@ -59,0 +60,0 @@ return; |
@@ -100,4 +100,4 @@ // Copyright 2013 Mark Cavage, Inc. All rights reserved. | ||
function corsOnHeader() { | ||
origin = req.headers['origin']; | ||
if (opts.credentials) { | ||
origin = req.headers['origin']; | ||
res.setHeader(AC_ALLOW_ORIGIN, origin); | ||
@@ -104,0 +104,0 @@ res.setHeader(AC_ALLOW_CREDS, 'true'); |
// Copyright 2012 Mark Cavage, Inc. All rights reserved. | ||
var crypto = require('crypto'); | ||
var assert = require('assert-plus'); | ||
@@ -6,0 +4,0 @@ |
@@ -36,3 +36,3 @@ // Copyright 2012 Mark Cavage, Inc. All rights reserved. | ||
next(new ResourceNotFoundError(err, | ||
req.path())); | ||
req.path())); | ||
return; | ||
@@ -45,3 +45,3 @@ } else if (!stats.isFile()) { | ||
if (res.handledGzip && isGzip) { | ||
res.handledGzip(); | ||
res.handledGzip(); | ||
} | ||
@@ -76,3 +76,3 @@ | ||
// Serve an index.html page or similar | ||
file = path.join(opts.directory, opts.default); | ||
file = path.join(file, opts.default); | ||
fs.stat(file, function (dirErr, dirStats) { | ||
@@ -100,5 +100,19 @@ serveFileFromStats(file, | ||
function serve(req, res, next) { | ||
var file = path.join(opts.directory, | ||
decodeURIComponent(req.path())); | ||
var file = decodeURIComponent(req.path()); | ||
var route; | ||
var regex; | ||
var stats; | ||
if (opts.directory) { | ||
route = req.route.path.toString(); | ||
route = route.substring(1, route.length-4); | ||
route = route.replace(/\\\//g,'/'); | ||
if (opts.directory.slice(-1) != '/') | ||
opts.directory += '/'; | ||
regex = new RegExp(route, 'g'); | ||
file = req.path().replace(regex, opts.directory); | ||
} | ||
if (req.method !== 'GET' && req.method !== 'HEAD') { | ||
@@ -110,3 +124,19 @@ next(new MethodNotAllowedError(req.method)); | ||
if (!re.test(file.replace(/\\/g, '/'))) { | ||
next(new NotAuthorizedError(req.path())); | ||
if (opts['default'] && | ||
file.indexOf(opts['default']) < 0) { | ||
if (file.slice(-1) == '/') { | ||
file = file.substring(0, | ||
file.length - 1); | ||
} | ||
if (fs.existsSync(file)) { | ||
stats = fs.lstatSync(file); | ||
if (stats.isDirectory()) { | ||
if (fs.existsSync(file)) { | ||
file = file + '/' + | ||
opts['default']; | ||
} | ||
} | ||
} | ||
} | ||
serveNormal(file, req, res, next); | ||
return; | ||
@@ -121,18 +151,19 @@ } | ||
if (opts.gzip && req.acceptsEncoding('gzip')) { | ||
fs.stat(file + '.gz', function (err, stats) { | ||
if (!err) { | ||
res.setHeader('Content-Encoding', 'gzip'); | ||
serveFileFromStats(file, | ||
err, | ||
stats, | ||
true, | ||
req, | ||
res, | ||
next); | ||
} else { | ||
serveNormal(file, req, res, next); | ||
} | ||
}); | ||
fs.stat(file + '.gz', function (err, _stats) { | ||
if (!err) { | ||
res.setHeader('Content-Encoding', | ||
'gzip'); | ||
serveFileFromStats(file, | ||
err, | ||
_stats, | ||
true, | ||
req, | ||
res, | ||
next); | ||
} else { | ||
serveNormal(file, req, res, next); | ||
} | ||
}); | ||
} else { | ||
serveNormal(file, req, res, next); | ||
serveNormal(file, req, res, next); | ||
} | ||
@@ -139,0 +170,0 @@ |
@@ -57,22 +57,32 @@ // Copyright 2012 Mark Cavage, Inc. All rights reserved. | ||
if (type && type.indexOf(';') !== '-1') | ||
type = type.split(';')[0]; | ||
if (!type) { | ||
for (var i = 0; i < this.acceptable.length; i++) { | ||
if (this.req.accepts(this.acceptable[i])) { | ||
type = this.acceptable[i]; | ||
break; | ||
} | ||
} | ||
if (!(formatter = this.formatters[type])) { | ||
if (!type) { | ||
for (var i = 0; i < this.acceptable.length; i++) { | ||
if (this.req.accepts(this.acceptable[i])) { | ||
type = this.acceptable[i]; | ||
break; | ||
} | ||
} | ||
} else { | ||
if (type.indexOf('/') === -1) | ||
type = mime.lookup(type); | ||
// The importance of a status code outside of the | ||
// 2xx range probably outweighs that of unable being to | ||
// format the response body | ||
if (this.statusCode >= 200 && this.statusCode < 300) | ||
this.statusCode = 406; | ||
if (this.acceptable.indexOf(type) === -1) | ||
type = 'application/octet-stream'; | ||
return (null); | ||
} | ||
} else if (type.indexOf(';') !== '-1') { | ||
type = type.split(';')[0]; | ||
} | ||
if (!(formatter = this.formatters[type])) { | ||
if (type.indexOf('/') === -1) | ||
type = mime.lookup(type); | ||
if (this.acceptable.indexOf(type) === -1) | ||
type = 'application/octet-stream'; | ||
formatter = this.formatters[type] || this.formatters['*/*']; | ||
if (!formatter) { | ||
@@ -85,3 +95,2 @@ log.warn({ | ||
} | ||
this.setHeader('Content-Type', type); | ||
} | ||
@@ -91,5 +100,6 @@ | ||
type = type + '; charset=' + this._charSet; | ||
this.setHeader('Content-Type', type); | ||
} | ||
this.setHeader('Content-Type', type); | ||
if (body instanceof Error && body.statusCode !== undefined) | ||
@@ -264,2 +274,3 @@ this.statusCode = body.statusCode; | ||
this.removeHeader('Content-Type'); | ||
this.removeHeader('Content-Encoding'); | ||
} | ||
@@ -266,0 +277,0 @@ |
@@ -371,5 +371,11 @@ // Copyright 2012 Mark Cavage, Inc. All rights reserved. | ||
var v = semver.maxSatisfying(k, req.version()); | ||
if (v && (!r || semver.gt(v, r.versions))) { | ||
r = c.r; | ||
params = c.p; | ||
if (v) { | ||
if (!r || | ||
r.versions.some(function (v2) { | ||
return (semver.gt(v, v2)); | ||
})) { | ||
r = c.r; | ||
params = c.p; | ||
} | ||
} | ||
@@ -376,0 +382,0 @@ }); |
@@ -656,2 +656,3 @@ // Copyright 2012 Mark Cavage, Inc. All rights reserved. | ||
var self = this; | ||
var t; | ||
@@ -700,7 +701,13 @@ function next(arg) { | ||
if ((i + 1) > 0 && chain[i] && !chain[i]._skip) { | ||
var _name = chain[i].name || ('handler-' + i); | ||
req.timers.push({ | ||
name: _name, | ||
time: process.hrtime(t) | ||
}); | ||
dtrace._rstfy_probes['handler-done'].fire(function () { | ||
return ([ | ||
self.name, | ||
route !== null ? route : 'pre', | ||
chain[i].name || ('handler-' + i), | ||
route !== null ? route.name : 'pre', | ||
_name, | ||
id | ||
@@ -716,2 +723,3 @@ ]); | ||
t = process.hrtime(); | ||
if (log.trace()) | ||
@@ -723,3 +731,3 @@ log.trace('running %s', chain[i].name || '?'); | ||
self.name, | ||
route !== null ? route : 'pre', | ||
route !== null ? route.name : 'pre', | ||
chain[i].name || ('handler-' + i), | ||
@@ -738,3 +746,3 @@ id | ||
self.name, | ||
route !== null ? route : 'pre', | ||
route !== null ? route.name : 'pre', | ||
id, | ||
@@ -760,3 +768,3 @@ res.statusCode || 200, | ||
self.name, | ||
route !== null ? route : 'pre', | ||
route !== null ? route.name : 'pre', | ||
id, | ||
@@ -769,2 +777,3 @@ req.method, | ||
req.timers = []; | ||
d = domain.create(); | ||
@@ -771,0 +780,0 @@ d.add(req); |
@@ -17,2 +17,3 @@ { | ||
"Erik Kristensen", | ||
"Domikik Lessel", | ||
"Steve Mason", | ||
@@ -34,3 +35,3 @@ "Trent Mick", | ||
"description": "REST framework", | ||
"version": "2.6.0", | ||
"version": "2.6.1", | ||
"repository": { | ||
@@ -51,5 +52,6 @@ "type": "git", | ||
"dependencies": { | ||
"assert-plus": "0.1.2", | ||
"backoff": "2.2.0", | ||
"bunyan": "0.21.1", | ||
"assert-plus": "0.1.4", | ||
"backoff": "2.3.0", | ||
"bunyan": "0.22.0", | ||
"csv": "0.3.6", | ||
"deep-equal": "0.0.0", | ||
@@ -60,10 +62,11 @@ "escape-regexp-component": "1.0.2", | ||
"keep-alive-agent": "0.0.1", | ||
"lru-cache": "2.3.0", | ||
"mime": "1.2.9", | ||
"negotiator": "0.2.5", | ||
"lru-cache": "2.3.1", | ||
"mime": "1.2.11", | ||
"negotiator": "0.3.0", | ||
"node-uuid": "1.4.0", | ||
"once": "1.1.1", | ||
"qs": "0.6.4", | ||
"semver": "1.1.4", | ||
"spdy": "1.8.2", | ||
"once": "1.3.0", | ||
"qs": "0.6.5", | ||
"semver": "2.2.1", | ||
"spdy": "1.14.12", | ||
"tunnel-agent": "0.3.0", | ||
"verror": "1.3.6" | ||
@@ -75,5 +78,5 @@ }, | ||
"devDependencies": { | ||
"cover": "0.2.8", | ||
"filed": "0.0.7", | ||
"nodeunit": "0.8.0", | ||
"cover": "0.2.9", | ||
"filed": "0.1.0", | ||
"nodeunit": "0.8.1", | ||
"watershed": "0.3.0" | ||
@@ -80,0 +83,0 @@ }, |
@@ -12,37 +12,39 @@ # restify | ||
## Server | ||
```javascript | ||
var restify = require('restify'); | ||
var restify = require('restify'); | ||
var server = restify.createServer({ | ||
name: 'myapp', | ||
version: '1.0.0' | ||
}); | ||
server.use(restify.acceptParser(server.acceptable)); | ||
server.use(restify.queryParser()); | ||
server.use(restify.bodyParser()); | ||
var server = restify.createServer({ | ||
name: 'myapp', | ||
version: '1.0.0' | ||
}); | ||
server.use(restify.acceptParser(server.acceptable)); | ||
server.use(restify.queryParser()); | ||
server.use(restify.bodyParser()); | ||
server.get('/echo/:name', function (req, res, next) { | ||
res.send(req.params); | ||
return next(); | ||
}); | ||
server.get('/echo/:name', function (req, res, next) { | ||
res.send(req.params); | ||
return next(); | ||
}); | ||
server.listen(8080, function () { | ||
console.log('%s listening at %s', server.name, server.url); | ||
}); | ||
``` | ||
server.listen(8080, function () { | ||
console.log('%s listening at %s', server.name, server.url); | ||
}); | ||
## Client | ||
```javascript | ||
var assert = require('assert'); | ||
var restify = require('restify'); | ||
var assert = require('assert'); | ||
var restify = require('restify'); | ||
var client = restify.createJsonClient({ | ||
url: 'http://localhost:8080', | ||
version: '~1.0' | ||
}); | ||
var client = restify.createJsonClient({ | ||
url: 'http://localhost:8080', | ||
version: '~1.0' | ||
}); | ||
client.get('/echo/mark', function (err, req, res, obj) { | ||
assert.ifError(err); | ||
console.log('Server returned: %j', obj); | ||
}); | ||
``` | ||
client.get('/echo/mark', function (err, req, res, obj) { | ||
assert.ifError(err); | ||
console.log('Server returned: %j', obj); | ||
}); | ||
# Installation | ||
@@ -49,0 +51,0 @@ |
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
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances 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
203871
51
4547
85
20
8
+ Addedcsv@0.3.6
+ Addedtunnel-agent@0.3.0
+ Addedassert-plus@0.1.4(transitive)
+ Addedbackoff@2.3.0(transitive)
+ Addedbunyan@0.22.0(transitive)
+ Addedcsv@0.3.6(transitive)
+ Addedlru-cache@2.3.1(transitive)
+ Addedmime@1.2.11(transitive)
+ Addednegotiator@0.3.0(transitive)
+ Addedonce@1.3.0(transitive)
+ Addedqs@0.6.5(transitive)
+ Addedsemver@2.2.1(transitive)
+ Addedspdy@1.14.12(transitive)
+ Addedtunnel-agent@0.3.0(transitive)
- Removedbackoff@2.2.0(transitive)
- Removedbunyan@0.21.1(transitive)
- Removedlru-cache@2.3.0(transitive)
- Removedmime@1.2.9(transitive)
- Removednegotiator@0.2.5(transitive)
- Removedonce@1.1.1(transitive)
- Removedqs@0.6.4(transitive)
- Removedsemver@1.1.4(transitive)
- Removedspdy@1.8.2(transitive)
Updatedassert-plus@0.1.4
Updatedbackoff@2.3.0
Updatedbunyan@0.22.0
Updatedlru-cache@2.3.1
Updatedmime@1.2.11
Updatednegotiator@0.3.0
Updatedonce@1.3.0
Updatedqs@0.6.5
Updatedsemver@2.2.1
Updatedspdy@1.14.12