Comparing version 2.1.1 to 2.2.0
# restify Changelog | ||
## 2.2.2 (not yet released) | ||
## 2.2.1 (not yet released) | ||
## 2.2.0 | ||
- GH-316 drop `clone`, and just shallow copy (Trent Mick) | ||
- GH-284 preflight requests not working without access-control-request-headers | ||
- GH-283 versioned routes should use maximum match, not first (Colin O'Brien) | ||
- dtrace probes for restify clients | ||
- node-dtrace-provider@0.2.8 | ||
- backoff@2.0.0 and necessary changes | ||
## 2.1.1 | ||
@@ -6,0 +15,0 @@ |
@@ -19,5 +19,5 @@ // Copyright 2012 Mark Cavage, Inc. All rights reserved. | ||
var once = require('once'); | ||
var semver = require('semver'); | ||
var uuid = require('node-uuid'); | ||
var dtrace = require('../dtrace'); | ||
var errors = require('../errors'); | ||
@@ -36,2 +36,27 @@ | ||
function cloneRetryOptions(options, defaults) { | ||
if (options === false) { | ||
return ({ | ||
minTimeout: 1, | ||
maxTimeout: 2, | ||
retries: 1 | ||
}); | ||
} | ||
assert.optionalObject(options, 'options.retry'); | ||
var r = options || {}; | ||
assert.optionalNumber(r.minTimeout, 'options.retry.minTimeout'); | ||
assert.optionalNumber(r.maxTimeout, 'options.retry.maxTimeout'); | ||
assert.optionalNumber(r.retries, 'options.retry.retries'); | ||
assert.optionalObject(defaults, 'defaults'); | ||
defaults = defaults || {}; | ||
return ({ | ||
minTimeout: r.minTimeout || defaults.minTimeout || 1000, | ||
maxTimeout: r.maxTimeout || defaults.maxTimeout || Infinity, | ||
retries: r.retries || defaults.retries || 4 | ||
}); | ||
} | ||
function defaultUserAgent() { | ||
@@ -53,2 +78,3 @@ var UA = 'restify/' + VERSION + | ||
this.message = 'connect timeout after ' + ms + 'ms'; | ||
this.name = 'ConnectTimeoutError'; | ||
} | ||
@@ -65,2 +91,3 @@ util.inherits(ConnectTimeoutError, Error); | ||
var id = dtrace.nextId(); | ||
var log = opts.log; | ||
@@ -80,8 +107,24 @@ var proto = opts.protocol === 'https:' ? https : http; | ||
cb(new ConnectTimeoutError(opts.connectTimeout), req); | ||
var err = new ConnectTimeoutError(opts.connectTimeout); | ||
dtrace._rstfy_probes['client-error'].fire(function () { | ||
return ([id, err.toString()]); | ||
}); | ||
cb(err, req); | ||
}, opts.connectTimeout); | ||
} | ||
dtrace._rstfy_probes['client-request'].fire(function () { | ||
return ([ | ||
opts.method, | ||
opts.path, | ||
opts.headers, | ||
id | ||
]); | ||
}); | ||
var req = proto.request(opts, function onResponse(res) { | ||
clearTimeout(timer); | ||
dtrace._rstfy_probes['client-response'].fire(function () { | ||
return ([ id, res.statusCode, res.headers ]); | ||
}); | ||
log.trace({client_res: res}, 'Response received'); | ||
@@ -102,2 +145,5 @@ | ||
req.on('error', function onError(err) { | ||
dtrace._rstfy_probes['client-error'].fire(function () { | ||
return ([id, (err || {}).toString()]); | ||
}); | ||
log.trace({err: err}, 'Request failed'); | ||
@@ -152,6 +198,3 @@ clearTimeout(timer); | ||
this.name = options.name || 'HttpClient'; | ||
this.retry = (options.retry !== false) ? | ||
(options.retry || { retries: 3 }) : false; | ||
this.retry = cloneRetryOptions(options.retry); | ||
this.signRequest = options.signRequest || false; | ||
@@ -295,28 +338,16 @@ this.socketPath = options.socketPath || false; | ||
var self = this; | ||
cb = once(cb); | ||
// Make one request, then kick off the next call; note that | ||
// node-backoff always imposes an initial time delay before | ||
// starting, which we don't want. | ||
rawRequest(opts, function _callback(err, req) { | ||
if (!err || opts.retry === false) { | ||
cb(err, req); | ||
return; | ||
} | ||
var call; | ||
var retry = cloneRetryOptions(opts.retry); | ||
var retry = backoff.call(rawRequest, opts, cb); | ||
retry.setStrategy(new backoff.ExponentialStrategy({ | ||
initialDelay: self.retry.minTimeout || 1000, | ||
maxDelay: self.retry.maxTimeout || Infinity | ||
})); | ||
call = backoff.call(rawRequest, opts, cb); | ||
call.setStrategy(new backoff.ExponentialStrategy({ | ||
initialDelay: retry.minTimeout, | ||
maxDelay: retry.maxTimeout | ||
})); | ||
call.failAfter(retry.retries); | ||
call.on('backoff', this.emit.bind(this, 'attempt')); | ||
if (typeof (opts.retry.retries) === 'number') { | ||
if (opts.retry.retries !== Infinity) | ||
retry.failAfter(opts.retry.retries); | ||
} else { | ||
retry.failAfter(4); | ||
} | ||
retry.on('backoff', self.emit.bind(self, 'attempt')); | ||
}); | ||
call.start(); | ||
}; | ||
@@ -323,0 +354,0 @@ |
// Copyright 2012 Mark Cavage, Inc. All rights reserved. | ||
var assert = require('assert-plus'); | ||
var dtrace = require('dtrace-provider'); | ||
@@ -12,15 +11,26 @@ | ||
var ID = 0; | ||
var MAX_INT = Math.pow(2, 32) - 1; | ||
var PROBES = { | ||
// server_name, route_name, fd, method, url, headers (json) | ||
'route-start': ['char *', 'char *', 'int', 'char *', 'char *', | ||
'char *'], | ||
// server_name, route_name, id, method, url, headers (json) | ||
'route-start': ['char *', 'char *', 'int', 'char *', 'char *', 'json'], | ||
// server_name, route_name, handler_name, fd | ||
// server_name, route_name, handler_name, id | ||
'handler-start': ['char *', 'char *', 'char *', 'int'], | ||
// server_name, route_name, handler_name, fd | ||
// server_name, route_name, handler_name, id | ||
'handler-done': ['char *', 'char *', 'char *', 'int'], | ||
// server_name, route_name, fd, statusCode, headers (json) | ||
'route-done': ['char *', 'char *', 'int', 'int', 'char *'] | ||
// server_name, route_name, id, statusCode, headers (json) | ||
'route-done': ['char *', 'char *', 'int', 'int', 'json'], | ||
// Client probes | ||
// method, url, headers, id | ||
'client-request': ['char *', 'char *', 'json', 'int'], | ||
// id, statusCode, headers | ||
'client-response': ['int', 'int', 'json'], | ||
// id, Error.toString() | ||
'client-error': ['id', 'char *'] | ||
}; | ||
@@ -48,2 +58,9 @@ var PROVIDER; | ||
PROVIDER.enable(); | ||
PROVIDER.nextId = function nextId() { | ||
if (++ID >= MAX_INT) | ||
ID = 1; | ||
return (ID); | ||
}; | ||
} | ||
@@ -50,0 +67,0 @@ |
@@ -10,2 +10,5 @@ // Copyright 2012 Mark Cavage, Inc. All rights reserved. | ||
var shallowCopy = require('./utils').shallowCopy; | ||
function createClient(options) { | ||
@@ -15,3 +18,2 @@ var assert = require('assert-plus'); | ||
var clients = require('./clients'); | ||
var clone = require('clone'); | ||
@@ -21,7 +23,7 @@ assert.object(options, 'options'); | ||
var client; | ||
var opts = clone(options); | ||
var opts = shallowCopy(options); | ||
opts.agent = options.agent; | ||
opts.name = opts.name || 'restify'; | ||
opts.type = opts.type || 'application/octet-stream'; | ||
opts.log = options.log || bunyan.createLogger(opts.name); | ||
opts.log = opts.log || bunyan.createLogger(opts.name); | ||
@@ -48,4 +50,3 @@ switch (opts.type) { | ||
function createJsonClient(options) { | ||
var clone = require('clone'); | ||
options = options ? clone(options) : {}; | ||
options = options ? shallowCopy(options) : {}; | ||
options.type = 'json'; | ||
@@ -57,4 +58,3 @@ return (createClient(options)); | ||
function createStringClient(options) { | ||
var clone = require('clone'); | ||
options = options ? clone(options) : {}; | ||
options = options ? shallowCopy(options) : {}; | ||
options.type = 'string'; | ||
@@ -66,4 +66,3 @@ return (createClient(options)); | ||
function createHttpClient(options) { | ||
var clone = require('clone'); | ||
options = options ? clone(options) : {}; | ||
options = options ? shallowCopy(options) : {}; | ||
options.type = 'http'; | ||
@@ -76,3 +75,2 @@ return (createClient(options)); | ||
var bunyan = require('./bunyan_helper'); | ||
var clone = require('clone'); | ||
var InternalError = require('./errors').InternalError; | ||
@@ -82,11 +80,8 @@ var Router = require('./router'); | ||
var opts = clone(options || {}); | ||
var opts = shallowCopy(options || {}); | ||
var server; | ||
opts.name = opts.name || 'restify'; | ||
// clone can't clone something with prototypes so we need to | ||
// manually set opts.log and opts.router to the right objects: | ||
opts.log = | ||
opts.log ? options.log : bunyan.createLogger(opts.name); | ||
opts.router = opts.router ? options.router : new Router(opts); | ||
opts.log = opts.log || bunyan.createLogger(opts.name); | ||
opts.router = opts.router || new Router(opts); | ||
@@ -93,0 +88,0 @@ server = new Server(opts); |
// Copyright 2012 Mark Cavage, Inc. All rights reserved. | ||
var assert = require('assert-plus'); | ||
var clone = require('clone'); | ||
var shallowCopy = require('../utils').shallowCopy; | ||
///--- API | ||
@@ -16,3 +17,3 @@ | ||
if (options.properties) { | ||
props = clone(options.properties); | ||
props = shallowCopy(options.properties); | ||
} else { | ||
@@ -19,0 +20,0 @@ props = {}; |
@@ -8,6 +8,6 @@ // Copyright 2012 Mark Cavage, Inc. All rights reserved. | ||
var assert = require('assert-plus'); | ||
var clone = require('clone'); | ||
var LRU = require('lru-cache'); | ||
var semver = require('semver'); | ||
var shallowCopy = require('./utils').shallowCopy; | ||
var errors = require('./errors'); | ||
@@ -42,2 +42,11 @@ | ||
function createCachedRoute(o, path, version, route) { | ||
if (!o.hasOwnProperty(path)) | ||
o[path] = {}; | ||
if (!o[path].hasOwnProperty(version)) | ||
o[path][version] = route; | ||
} | ||
function matchContentType(type1, type2) { | ||
@@ -183,8 +192,8 @@ var matches = true; | ||
this.version = options.version || []; | ||
if (!Array.isArray(this.version)) | ||
this.version = [this.version]; | ||
assert.arrayOfString(this.version, 'options.version'); | ||
this.versions = options.versions || options.version || []; | ||
if (!Array.isArray(this.versions)) | ||
this.versions = [this.versions]; | ||
assert.arrayOfString(this.versions, 'options.versions'); | ||
this.version.forEach(function (v) { | ||
this.versions.forEach(function (v) { | ||
if (semver.valid(v)) | ||
@@ -195,3 +204,3 @@ return (true); | ||
}); | ||
this.version.sort(); | ||
this.versions.sort(); | ||
@@ -214,3 +223,3 @@ } | ||
var type = options.contentType || self.contentType; | ||
var version = options.version || self.version; | ||
var versions = options.versions || options.version || self.versions; | ||
@@ -221,5 +230,5 @@ if (!Array.isArray(type)) | ||
if (!Array.isArray(version)) | ||
version = [version]; | ||
version.sort(); | ||
if (!Array.isArray(versions)) | ||
versions = [versions]; | ||
versions.sort(); | ||
@@ -241,3 +250,3 @@ exists = routes.some(function (r) { | ||
type: type, | ||
version: version | ||
versions: versions | ||
}; | ||
@@ -258,3 +267,3 @@ routes.push(route); | ||
route.type, | ||
route.version); | ||
route.versions); | ||
@@ -292,2 +301,4 @@ return (route.name); | ||
Router.prototype.find = function find(req, res, callback) { | ||
var candidates = {}; | ||
var candidateKeys = []; | ||
var ct = req.headers['content-type'] || DEF_CT; | ||
@@ -303,3 +314,3 @@ var cacheKey = req.method + req.url + req.version() + ct; | ||
if ((cacheVal = this.cache.get(cacheKey))) { | ||
callback(null, cacheVal.name, clone(cacheVal.params)); | ||
callback(null, cacheVal.name, shallowCopy(cacheVal.params)); | ||
return; | ||
@@ -323,2 +334,3 @@ } | ||
switch (req.method) { | ||
case 'PATCH': | ||
case 'POST': | ||
@@ -341,12 +353,36 @@ case 'PUT': | ||
if (routes[i].version.length === 0 || | ||
req.version() === '*' || | ||
(ver = maxSatisfying(routes[i].version, | ||
req.version()) || false)) { | ||
// GH-283: we want to find the latest version for a given route, | ||
// not the first one. However, if neither the client nor | ||
// server specified any version, we're done, because neither | ||
// cared | ||
if (routes[i].versions.length === 0 && req.version() === '*') { | ||
r = routes[i]; | ||
break; | ||
} | ||
if (routes[i].versions.length > 0) { | ||
candidates[routes[i].versions] = routes[i]; | ||
candidateKeys.push(routes[i].versions); | ||
} | ||
} | ||
// | ||
if (!r) { | ||
candidateKeys.forEach(function (k) { | ||
var v = semver.maxSatisfying(k, req.version()); | ||
if (v && (!ver || semver.gt(v, ver.version))) { | ||
ver = { | ||
version: v, | ||
key: k | ||
}; | ||
} | ||
}); | ||
if (ver && ver.key) { | ||
r = candidates[ver.key.toString()]; | ||
} else { | ||
if (candidateKeys.length > 0) | ||
ver = false; | ||
} | ||
} | ||
// In order, we check if the route exists, in which case, we're good. | ||
@@ -359,4 +395,2 @@ // Otherwise we look to see if ver was set to false; that would tell us | ||
// to handle this having been a preflight request. | ||
// | ||
if (params && r) { | ||
@@ -368,3 +402,3 @@ cacheVal = { | ||
this.cache.set(cacheKey, cacheVal); | ||
callback(null, r.name, clone(params)); | ||
callback(null, r.name, shallowCopy(params)); | ||
return; | ||
@@ -378,3 +412,6 @@ } | ||
if (ver === false) { | ||
callback(new InvalidVersionError(req.getVersion())); | ||
callback(new InvalidVersionError('%s is not supported by %s %s', | ||
req.version() || '?', | ||
req.method, | ||
req.path())); | ||
return; | ||
@@ -403,2 +440,5 @@ } | ||
(headers || '').split(/\s*,\s*/).forEach(function (h) { | ||
if (!h) | ||
return; | ||
h = h.toLowerCase(); | ||
@@ -405,0 +445,0 @@ ok = ALLOW_HEADERS.indexOf(h) !== -1 && ok; |
@@ -11,3 +11,2 @@ // Copyright 2012 Mark Cavage, Inc. All rights reserved. | ||
var assert = require('assert-plus'); | ||
var clone = require('clone'); | ||
var mime = require('mime'); | ||
@@ -20,2 +19,3 @@ var once = require('once'); | ||
var formatters = require('./formatters'); | ||
var shallowCopy = require('./utils').shallowCopy; | ||
@@ -143,3 +143,3 @@ // Ensure these are loaded | ||
this.secure = false; | ||
this.version = options.version || null; | ||
this.versions = options.versions || options.version || []; | ||
@@ -297,3 +297,3 @@ var fmt = mergeFormatters(options.formatters); | ||
} else if (typeof (opts) === 'object') { | ||
opts = clone(opts); | ||
opts = shallowCopy(opts); | ||
} else { | ||
@@ -321,10 +321,10 @@ throw new TypeError('path (string) required'); | ||
opts.method = method.toUpperCase(); | ||
opts.version = opts.version || self.version; | ||
if (!Array.isArray(opts.version)) | ||
opts.version = [opts.version]; | ||
opts.versions = opts.versions || opts.version || self.versions; | ||
if (!Array.isArray(opts.versions)) | ||
opts.versions = [opts.versions]; | ||
if (!opts.name) { | ||
opts.name = method + '-' + (opts.path || opts.url); | ||
if (opts.version) { | ||
opts.name += '-' + opts.version.join('--'); | ||
if (opts.versions.length > 0) { | ||
opts.name += '-' + opts.versions.join('--'); | ||
} | ||
@@ -576,2 +576,3 @@ } | ||
var i = -1; | ||
var id = dtrace.nextId(); | ||
var log = this.log; | ||
@@ -620,3 +621,3 @@ var self = this; | ||
chain[i].name || ('handler-' + i), | ||
req.connection.fd | ||
id | ||
]); | ||
@@ -636,3 +637,3 @@ }); | ||
chain[i].name || ('handler-' + i), | ||
req.connection.fd | ||
id | ||
]); | ||
@@ -650,5 +651,5 @@ }); | ||
route !== null ? route : 'pre', | ||
req.connection.fd, | ||
id, | ||
res.statusCode || 200, | ||
JSON.stringify(res.headers()) | ||
res.headers() | ||
]); | ||
@@ -672,6 +673,6 @@ }); | ||
route !== null ? route : 'pre', | ||
req.connection.fd, | ||
id, | ||
req.method, | ||
req.href(), | ||
JSON.stringify(req.headers) | ||
req.headers | ||
]); | ||
@@ -703,5 +704,5 @@ }); | ||
res.serverName = this.name; | ||
res.version = this.router.version[this.router.version.length - 1]; | ||
res.version = this.router.versions[this.router.versions.length - 1]; | ||
// HTTP METHODS set for the current route: | ||
res.methods = this.router.reverse[Object.keys(this.router.reverse)[0]]; | ||
}; |
@@ -57,3 +57,18 @@ // Copyright 2012 Mark Cavage, Inc. All rights reserved. | ||
/** | ||
* Return a shallow copy of the given object; | ||
*/ | ||
function shallowCopy(obj) { | ||
if (!obj) { | ||
return (obj); | ||
} | ||
var copy = {}; | ||
Object.keys(obj).forEach(function (k) { | ||
copy[k] = obj[k]; | ||
}); | ||
return (copy); | ||
} | ||
///--- Exports | ||
@@ -63,3 +78,4 @@ | ||
parseQuality: parseQuality, | ||
sanitizePath: sanitizePath | ||
}; | ||
sanitizePath: sanitizePath, | ||
shallowCopy: shallowCopy | ||
}; |
@@ -14,2 +14,3 @@ { | ||
"Trent Mick", | ||
"Colin O'Brien", | ||
"Falco Nogatz", | ||
@@ -27,3 +28,3 @@ "Pedro Palazón", | ||
"description": "REST framework", | ||
"version": "2.1.1", | ||
"version": "2.2.0", | ||
"repository": { | ||
@@ -45,6 +46,5 @@ "type": "git", | ||
"assert-plus": "0.1.2", | ||
"backoff": "1.2.0", | ||
"backoff": "2.0.0", | ||
"bunyan": "0.18.2", | ||
"clone": "0.1.5", | ||
"dtrace-provider": "0.2.7", | ||
"dtrace-provider": "0.2.8", | ||
"formidable": "1.0.11", | ||
@@ -51,0 +51,0 @@ "http-signature": "0.9.10", |
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
213023
15
3933
+ Addedbackoff@2.0.0(transitive)
+ Addeddtrace-provider@0.2.8(transitive)
- Removedclone@0.1.5
- Removedbackoff@1.2.0(transitive)
- Removedclone@0.1.5(transitive)
Updatedbackoff@2.0.0
Updateddtrace-provider@0.2.8