Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

restify

Package Overview
Dependencies
Maintainers
1
Versions
184
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

restify - npm Package Compare versions

Comparing version 0.5.6 to 1.0.0-1-rc

.npmignore

1245

lib/server.js

@@ -1,1030 +0,475 @@

// Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved.
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert');
var crypto = require('crypto');
var EventEmitter = require('events').EventEmitter;
var http = require('http');
var https = require('https');
var path = require('path');
var querystring = require('querystring');
var url = require('url');
var util = require('util');
var formidable = require('formidable');
var semver = require('semver');
var uuid = require('node-uuid');
var xml2js = require('xml2js');
var errors = require('./errors');
var Request = require('./request');
var Response = require('./response');
var Route = require('./route');
var HttpCodes = require('./http_codes');
var RestCodes = require('./rest_codes');
var log = require('./log');
var errors = require('./error');
var sprintf = require('./sprintf').sprintf;
// Just force this to extend http.ServerResponse
require('./http_extra');
var newError = errors.newError;
///--- Globals
var BadMethodError = errors.BadMethodError;
var InvalidVersionError = errors.InvalidVersionError;
var ResourceNotFoundError = errors.ResourceNotFoundError;
///--- Internal Helpers
/**
* Cleans up sloppy URL paths, like /foo////bar/// to /foo/bar.
*
* @param {String} path the HTTP resource path.
* @return {String} Cleaned up form of path.
*/
function _sanitizePath(path) {
assert.ok(path);
///--- Helpers
if (log.trace())
log.trace('_sanitizePath: path=%s', path);
function argsToChain() {
assert.ok(arguments.length);
// Be nice like apache and strip out any //my//foo//bar///blah
var _path = path.replace(/\/\/+/g, '/');
var args = arguments[0];
if (args.length < 0)
throw new TypeError('handler (Function) required');
// Kill a trailing '/'
if (_path.lastIndexOf('/') === (_path.length - 1) &&
_path.length > 1) {
_path = _path.substr(0, _path.length - 1);
}
var chain = [];
if (log.trace())
log.trace('_sanitizePath: returning %s', _path);
function process(handlers) {
handlers.forEach(function(h) {
if (Array.isArray(h))
return process(h);
if (!typeof(h) === 'function')
throw new TypeError('handlers must be Functions');
return decodeURIComponent(_path);
}
/**
* Checks if a mount matches, and if so, returns an object with all
* the :param variables.
*
* @param {String} path (request.url.pathname).
* @param {Object} route (what was mounted).
*/
function _matches(path, route) {
assert.ok(path);
assert.ok(route);
if (route.regex)
return route.url.exec(path);
if (path === route.url)
return {}; // there were no params if it was an exact match
var params = route.urlComponents;
var components = path.split('/').splice(1);
var len = components.length;
if (components.length !== params.length)
return null;
var parsed = {};
for (var i = 0; i < params.length; i++) {
// Don't use URL.parse, as it doesn't handle strings
// with ':' in them for this case. Regardless of what the
// RFC says about this, people do it.
var frag = components[i];
if (frag.indexOf('?') !== -1)
frag = frag.split('?', 2)[0];
if (params[i] === frag)
continue;
if (params[i].charAt(0) === ':') {
if (/.+\.(xml|json)$/.test(frag))
frag = frag.split(/\.(xml|json)$/)[0];
parsed[params[i].substr(1)] = frag;
continue;
}
return null;
return chain.push(h);
});
}
process(Array.prototype.slice.call(args, 0));
return parsed;
return chain;
}
function _parseAccept(request, response) {
assert.ok(request);
assert.ok(response);
assert.ok(request._config.acceptable);
assert.ok(request._config.acceptable.length);
function logRequest(req) {
assert.ok(req);
if (!request.headers.accept) {
log.trace('_parseAccept: no accept header sent, using `default`');
response._accept = request.headers.accept = request._config.acceptable[0];
return true;
}
if (req.log.isTraceEnabled())
req.log.trace('New Request:\n\n%s', req.toString());
}
var mediaRanges = request.headers.accept.split(',');
for (var i = 0; i < mediaRanges.length; i++) {
var _accept = new RegExp(mediaRanges[i].split(';')[0].replace(/\*/g, '.*'));
for (var j = 0; j < request._config.acceptable.length; j++) {
if (_accept.test(request._config.acceptable[j])) {
response._accept = request._config.acceptable[j];
log.trace('Parsed accept type as: %s', response._accept);
return true;
}
}
}
var msg = JSON.stringify({
code: 'InvalidHeader',
message: '\'Accept: ' + request.headers.accept + '\' not supported.',
supported: request._config.acceptable
}, null, 2);
response.send(HttpCodes.NotAcceptable, msg, {'content-type': 'text/plain'});
return false;
function default404Handler(req, res) {
res.send(new ResourceNotFoundError(req.url + ' not found'));
}
function _parseAuthorization(req, res) {
req.authorization = {};
req.username = 'anonymous';
if (!req.headers.authorization) {
log.trace('No authorization header present.');
return true;
}
var pieces = req.headers.authorization.split(' ', 2);
if (!pieces || pieces.length !== 2) {
res.sendError(newError({
httpCode: HttpCodes.BadRequest,
restCode: RestCodes.InvalidHeader,
message: 'BasicAuth content is invalid.'
}));
return false;
}
req.authorization = {
scheme: pieces[0],
credentials: pieces[1]
};
if (pieces[0] === 'Basic') {
var decoded = (new Buffer(pieces[1], 'base64')).toString('utf8');
if (!decoded) {
res.sendError(newError({
httpCode: HttpCodes.BadRequest,
restCode: RestCodes.InvalidHeader,
message: 'Authorization: Basic content is invalid (not base64).'
}));
return false;
}
if (decoded !== null) {
var idx = decoded.indexOf(':');
if (idx === -1) {
pieces = [decoded];
} else {
pieces = [decoded.slice(0, idx), decoded.slice(idx + 1)];
}
}
if (!(pieces !== null ? pieces[0] : null) ||
!(pieces !== null ? pieces[1] : null)) {
res.sendError(newError({
httpCode: HttpCodes.BadRequest,
restCode: RestCodes.InvalidHeader,
message: 'Authorization: Basic content is invalid.'
}));
return false;
}
req.authorization.basic = {
username: pieces[0],
password: pieces[1]
};
req.username = pieces[0];
function default405Handler(req, res, methods) {
res.header('Allow', methods.join(', '));
if (req.method === 'OPTIONS') {
res.send(200);
} else {
log.debug('Unknown authorization scheme %s. Skipping processing',
req.authorization.scheme);
var msg = req.url + ' does not support ' + req.method;
res.send(new BadMethodError(msg));
}
return true;
}
function _parseDate(request, response) {
if (request.headers.date) {
try {
var date = new Date(request.headers.date);
var now = new Date();
function defaultBadVersionHandler(req, res, versions) {
var msg = req.method + ' ' + req.url + ' supports versions: ' +
versions.join(', ');
if (log.trace())
log.trace('Date: sent=%d, now=%d, allowed=%d',
date.getTime(), now.getTime(), request._config.clockSkew);
if ((now.getTime() - date.getTime()) > request._config.clockSkew) {
response.sendError(newError({
httpCode: HttpCodes.BadRequest,
restCode: RestCodes.InvalidArgument,
message: 'Date header is too old'
}));
return false;
}
} catch (e) {
if (log.trace())
log.trace('Bad Date header: ' + e);
response.sendError(newError({
httpCode: HttpCodes.BadRequest,
restCode: RestCodes.InvalidArgument,
message: 'Date header is invalid'
}));
return false;
}
}
return true;
res.send(new InvalidVersionError(msg));
}
function _parseApiVersion(request, response) {
if (request.headers['x-api-version'])
request._version = request.headers['x-api-version'];
return true;
function toPort(x) {
x = parseInt(x, 10);
return (x >= 0 ? x : false);
}
function _parseQueryString(request, response) {
request._url = url.parse(request.url);
if (request._url.query) {
var _qs = querystring.parse(request._url.query);
for (var k in _qs) {
if (_qs.hasOwnProperty(k)) {
assert.ok(!request.params[k]);
request.params[k] = _qs[k];
}
}
}
return true;
function isPipeName(s) {
return (typeof(s) === 'string' && toPort(s) === false);
}
function _parseHead(request, response) {
assert.ok(request);
assert.ok(response);
log.trace('_parseHead:\n%s %s HTTP/%s\nHeaders: %o',
request.method,
request.url,
request.httpVersion,
request.headers);
///--- API
if (!_parseAccept(request, response)) return false;
if (!_parseAuthorization(request, response)) return false;
if (!_parseDate(request, response)) return false;
if (!_parseApiVersion(request, response)) return false;
if (!_parseQueryString(request, response)) return false;
/**
* Constructor. Creates a REST API Server.
*
* - options {Object} construction arguments. (log4js required).
*/
function Server(options) {
if (typeof(options) !== 'object')
throw new TypeError('options (Object) required');
if (typeof(options.dtrace) !== 'object')
throw new TypeError('options.dtrace (Object) required');
if (typeof(options.log4js) !== 'object')
throw new TypeError('options.log4js (Object) required');
return true;
}
EventEmitter.call(this);
this.chain = [];
this.formatters = options.formatters || {};
this.log4js = options.log4js;
this.log = this.log4js.getLogger('Server');
this.name = options.name || 'restify';
this.routes = [];
this.version = options.version || false;
function _parseRequest(request, response, next) {
assert.ok(request);
assert.ok(response);
assert.ok(next);
var contentType = request.contentType();
if (contentType === 'multipart/form-data') {
var form = formidable.IncomingForm();
form.maxFieldsSize = request._config.maxRequestSize;
form.on('error', function(err) {
return response.sendError(newError({
httpCode: HttpCodes.BadRequest,
restCode: RestCodes.BadRequest,
message: err.toString()
}));
var secure = false;
if (options.certificate && options.key) {
secure = true;
this.server = https.createServer({
cert: options.certificate,
key: options.key
});
} else {
this.server = http.createServer();
}
form.on('field', function(field, value) {
log.trace('_parseRequest(multipart) field=%s, value=%s', field, value);
request.params[field] = value;
});
var self = this;
this.server.on('error', function(err) {
self.emit('error', err);
});
form.on('end', function() {
log.trace('_parseRequset(multipart): req.params=%o', request.params);
return next();
});
form.parse(request);
this.server.on('clientError', function(err) {
self.emit('clientError', err);
});
} else {
request.body = '';
request.on('data', function(chunk) {
request.body += chunk;
if (request.body.length > request._config.maxRequestSize) {
return response.sendError(newError({
httpCode: HttpCodes.RequestTooLarge,
restCode: RestCodes.RequestTooLarge,
message: 'maximum HTTP data size is 8k'
}));
}
});
this.server.on('close', function() {
self.emit('close');
});
request.on('end', function() {
function done(err, bParams) {
if (err) {
return response.sendError(newError({
httpCode: HttpCodes.BadRequest,
restCode: RestCodes.InvalidArgument,
message: 'Invalid Content: ' + err.message
}));
}
this.server.on('connection', function(socket) {
self.emit('connection', socket);
});
Object.keys(bParams).forEach(function(k) {
if (request.params.hasOwnProperty(k)) {
return response.sendError(newError({
httpCode: HttpCodes.Conflict,
restCode: RestCodes.InvalidArgument,
message: 'duplicate parameter detected: ' + k
}));
}
request.params[k] = bParams[k];
});
this.server.on('upgrade', function(request, socket, headPacket) {
return self.emit('upgrade', request, socket, headPacket);
});
log.trace('_parseRequest: params parsed as: %o', request.params);
return next();
}
this.server.on('request', function(req, res) {
return self._request(req, res);
});
if (request.body) {
log.trace('_parseRequest: req.body=%s', request.body);
this.server.on('checkContinue', function(req, res) {
return self._request(req, res, true);
});
var contentLen = request.headers['content-length'];
if (contentLen !== undefined) {
var actualLen = Buffer.byteLength(request.body, 'utf8');
if (parseInt(contentLen, 10) !== actualLen) {
return response.sendError(newError({
httpCode: HttpCodes.BadRequest,
restCode: RestCodes.InvalidHeader,
message: 'Content-Length=' + contentLen +
' didn\'t match actual length=' + actualLen
}));
}
}
var bParams;
if (request._config.contentHandlers[contentType]) {
try {
bParams = request._config.contentHandlers[contentType](request.body,
request,
response,
done);
if (bParams)
return done(null, bParams);
} catch (e) {
return done(e);
}
} else if (contentType) {
return response.sendError(newError({
httpCode: HttpCodes.UnsupportedMediaType,
restCode: RestCodes.InvalidArgument,
message: contentType + ' unsupported'
}));
}
} else {
return done(null, {});
}
this.__defineGetter__('acceptable', function() {
var accept = Object.keys(self.formatters) || [];
Response.ACCEPTABLE.forEach(function(a) {
if (accept.indexOf(a) === -1)
accept.push(a);
});
}
}
return accept;
});
function _handleNoRoute(server, request, response) {
assert.ok(server);
assert.ok(request);
assert.ok(response);
this.__defineGetter__('name', function() {
return options.name || 'restify';
});
var body = null;
var code = HttpCodes.NotFound;
var headers = {};
this.__defineGetter__('dtrace', function() {
return options.dtrace;
});
// This is such a one-off that it's saner to just handle it as such
if (request.method === 'OPTIONS' &&
request.url === '*') {
code = HttpCodes.Ok;
} else {
var urls = server.routes.urls;
for (var u in urls) {
if (urls.hasOwnProperty(u)) {
var route = urls[u];
var methods = [];
var versions = [];
this.__defineGetter__('url', function() {
if (self.socketPath)
return 'http://' + self.socketPath;
var matched = false;
for (var i = 0; i < route.length; i++) {
if (methods.indexOf(route[i].method) === -1)
methods.push(route[i].method);
if (route[i].version && versions.indexOf(route[i].version) === -1)
versions.push(route[i].version);
if (_matches(request.url, route[i]))
matched = true;
}
if (matched) {
code = HttpCodes.BadMethod;
response._allowedMethods = methods;
if (versions.length) {
headers['x-api-versions'] = versions.join(', ');
if (methods.indexOf(request.method) !== -1) {
code = HttpCodes.RetryWith;
body = {
code: 'InvalidVersion',
message: 'Retry with an x-api-version header'
};
}
}
if (request.method === 'OPTIONS') {
code = HttpCodes.Ok;
headers.Allow = methods.join(', ');
}
}
}
}
}
response.send(code, body, headers);
return log.w3cLog(request, response, function() {});
var str = secure ? 'https://' : 'http://';
str += self.address().address;
str += ':';
str += self.address().port;
return str;
});
}
util.inherits(Server, EventEmitter);
module.exports = Server;
// --- Publicly available API
Server.prototype.address = function address() {
return this.server.address();
};
module.exports = {
/**
* Gets the server up and listening.
*
* You can call like:
* server.listen(80)
* server.listen(80, '127.0.0.1')
* server.listen('/tmp/server.sock')
*
* And pass in a callback to any of those forms. Also, by default, invoking
* this method will trigger DTrace probes to be enabled; to not do that, pass
* in 'false' as the second to last parameter.
*
* @param {Function} callback optionally get notified when listening.
* @throws {TypeError} on bad input.
*/
Server.prototype.listen = function listen() {
var callback = false;
var dtrace = true;
var self = this;
/**
* Creates a new restify HTTP server.
*
* @param {Object} options a hash of configuration parameters:
* - serverName: String to send back in the Server header.
* Default: node.js.
* - maxRequestSize: Max request size to allow, in bytes.
* Default: 8192.
* - accept: Array of valid MIME types to allow in Accept.
* Default: application/json.
* - version: Default API version to support; setting this
* means clients are required to send an
* X-Api-Version header.
* Default: None.
* - clockSkew: If a Date header is present, allow N seconds
* of skew.
* Default: 300
* - logTo: a `Writable Stream` where log messages should go.
* Default: process.stderr.
* - contentHandlers: An object of
* 'type'-> function(body, req, res).
* Built in content types are:
* - application/json
* - application/x-www-form-urlencoded
* - contentWriters: An object of
* 'type' -> function(obj, req, res).
* - Additionally supports application/javascript (jsonp)
* - headers: An object of global headers that are sent back
* on all requests. Restify automatically sets:
* - X-Api-Version (if versioned)
* - X-Request-Id
* - X-Response-Time
* - Content-(Length|Type|MD5)
* - Access-Control-Allow-(Origin|Methods|Headers)
* - Access-Control-Expose-Headers
* If you don't set those particular keys, restify
* fills in default functions; if you do set them,
* you can fully override restify's defaults.
* Note that like contentWriters, this is an object
* with a string key as the header name, and the
* value is a function of the form f(response)
* which must return a string.
*
* @return {Object} node HTTP server, with restify "magic".
*/
createServer: function(options) {
var k;
var server;
function listenCallback() {
if (dtrace)
self.dtrace.enable();
function httpMain(request, response) {
assert.ok(request);
assert.ok(response);
return callback ? callback.call(self) : false;
}
var route;
var path = _sanitizePath(request.url);
request.url = path;
request.username = '';
if (!arguments.length)
return this.server.listen(listenCallback);
request.requestId = response.requestId = uuid().toLowerCase();
request.startTime = response.startTime = new Date().getTime();
callback = arguments[arguments.length - 1];
if (typeof(callback) !== 'function')
callback = false;
request._config = server._config;
request.params = {};
request.uriParams = {};
if (arguments.length >= 2 && arguments[arguments.length - 2] === false)
dtrace = false;
response.request = request;
response._method = request.method;
response._allowedMethods = ['OPTIONS'];
response._config = server._config;
response._sent = false;
response._errorSent = false;
response._formatError = server._config.formatError;
switch (typeof(arguments[0])) {
case 'function':
return this.server.listen(listenCallback);
// HTTP and HTTPS are different -> joyent/node GH #1005
var addr = request.connection.remoteAddress;
if (!addr) {
if (request.connection.socket) {
addr = request.connection.socket.remoteAddress;
} else {
addr = 'unknown';
}
}
request._remoteAddress = addr;
response._remoteAddress = addr;
case 'string':
if (isPipeName(arguments[0]))
return this.server.listen(arguments[0], listenCallback);
if (!_parseHead(request, response))
return log.w3cLog(request, response, function() {});
throw new TypeError(arguments[0] + ' is not a named pipe');
if (!server.routes[request.method])
return _handleNoRoute(server, request, response);
case 'number':
var host = arguments[1];
return this.server.listen(arguments[0],
typeof(host) === 'string' ? host : '0.0.0.0',
listenCallback);
var routes = server.routes[request.method];
for (var i = 0; i < routes.length; i++) {
default:
throw new TypeError('port (Number) required');
}
};
var params = _matches(path, routes[i]);
if (params) {
// If the server isn't using versioning, just ignore
// whatever the client sent as a version header.
// If the server is, the client MUST send a version
// header. Unless the server is configured
// with weak versioning.
var ok = true;
if (routes[i].version && !server.weakVersions) {
if (request._version) {
if (routes[i].semver) {
ok = semver.satisfies(routes[i].version, request._version);
} else {
ok = (routes[i].version === request._version);
}
} else {
ok = false;
}
}
if (ok) {
request.uriParams = params;
route = routes[i];
if (route.version)
response._version = route.version;
/**
* Shuts down this server, and invokes callback (optionally) when done.
*
* @param {Function} callback optional callback to invoke when done.
*/
Server.prototype.close = function close(callback) {
if (callback) {
if (typeof(callback) !== 'function')
throw new TypeError('callback must be a function');
for (var j = 0; j < server.routes.urls[route.url].length; j++) {
var r = server.routes.urls[route.url][j];
response._allowedMethods.push(r.method);
}
break;
}
}
}
this.server.once('close', function() {
return callback();
});
}
if (!route)
return _handleNoRoute(server, request, response);
return this.server.close();
};
log.trace('%s %s route found -> %o', request.method, request.url, route);
var handler = 0;
var chain;
var stage = 0;
// Register all the routing methods
['del', 'get', 'head', 'post', 'put'].forEach(function(method) {
function runChain() {
return _parseRequest(request, response, function(had_err) {
var next = arguments.callee;
/**
* Mounts a chain on the given path against this HTTP verb
*
* @param {Object} options the URL to handle, at minimum.
* @return {Route} the newly created route.
*/
Server.prototype[method] = function(options) {
if (arguments.length < 2)
throw new Error('At least one handler (Function) required');
if (had_err)
response.sendError(had_err);
if (typeof(options) !== 'object' && typeof(options) !== 'string')
throw new TypeError('path (String) required');
if (chain.length > 1) {
// Check if we need to skip to the post chain
if ((stage === 0 && response._sent) ||
(stage === 1 && response._errorSent)) {
stage = 2;
handler = 0;
}
}
var args = Array.prototype.slice.call(arguments, 1);
if (!chain[stage])
return;
if (method === 'del')
method = 'DELETE';
if (!chain[stage][handler]) {
if (++stage >= chain.length)
return;
return this._addRoute(method.toUpperCase(), options, args);
};
});
handler = 0;
}
if (chain[stage][handler])
return chain[stage][handler++].call(this, request, response, next);
});
}
/**
* Removes a route from the server.
*
* You can either pass in the route name or the route object as `name`.
*
* @param {String} name the route name.
* @return {Boolean} true if route was removed, false if not.
* @throws {TypeError} on bad input.
*/
Server.prototype.rm = function rm(name) {
if (typeof(name) !== 'string' && !(name instanceof Route))
throw new TypeError('name (String) required');
if (route.handlers.pre &&
route.handlers.main &&
route.handlers.post) {
chain = [
route.handlers.pre,
route.handlers.main,
route.handlers.post
];
} else {
chain = [route.handlers.main];
}
return runChain();
} // end httpMain
if (options && options.cert && options.key) {
server = https.createServer(options, httpMain);
} else {
server = http.createServer(httpMain);
for (var i = 0; i < this.routes.length; i++) {
if (this.routes[i].name === name || this.routes[i] === name) {
this.routes.splice(i, 1);
return true;
}
}
server.logLevel = function(level) {
return log.level(level);
};
return false;
};
server.routes = {};
server._config = {};
server._config.contentHandlers = {};
server._config.contentWriters = {};
server._config.headers = {};
if (!options)
options = {};
/**
* Installs a list of handlers to run _before_ the "normal" handlers of all
* routes.
*
* You can pass in any combination of functions or array of functions.
*
* @throws {TypeError} on input error.
*/
Server.prototype.use = function use() {
var chain = argsToChain(arguments);
if (options.serverName)
server._config.serverName = options.serverName;
if (chain.length) {
var self = this;
chain.forEach(function(h) {
self.chain.push(h);
});
if (options.apiVersion && !options.version)
options.version = options.apiVersion;
this.routes.forEach(function(r) {
r.use(chain);
});
}
if (options.version)
server._config.version = options.version;
return this;
};
if (options.maxRequestSize)
server._config.maxRequestSize = options.maxRequestSize;
if (options.acceptable)
server._config.acceptable = options.acceptable;
if (options.accept)
server._config.acceptable = options.accept;
///--- Private methods
if (options.clockSkew)
server._config.clockSkew = options.clockSkew * 1000;
Server.prototype._addRoute = function _addRoute(method, options, handlers) {
var self = this;
if (options.logTo)
log.stderr(options.logTo);
var chain = this.chain.slice(0);
argsToChain(handlers).forEach(function(h) {
chain.push(h);
});
if (options.fullErrors)
server._config.fullErrors = true;
if (typeof(options) !== 'object')
options = { url: options };
if (options.sendErrorLogger)
server._config.sendErrorLogger = options.sendErrorLogger;
var route = new Route({
log4js: self.log4js,
method: method,
url: options.path || options.url,
handlers: chain,
name: options.name,
version: options.version || self.version,
dtrace: self.dtrace
});
route.on('error', function(err) {
self.emit('error', err);
});
route.on('done', function(req, res) {
self.emit('after', req, res, route.name);
});
// begin contentHandlers/writers
if (!options.contentHandlers)
options.contentHandlers = {};
if (!options.contentWriters)
options.contentWriters = {};
if (typeof(options.contentHandlers) !== 'object')
throw new TypeError('contentHandlers must be an object');
if (typeof(options.contentWriters) !== 'object')
throw new TypeError('contentWriters must be an object');
this.routes.forEach(function(r) {
if (r.matchesUrl({ url: options.url })) {
if (r.methods.indexOf(method) === -1)
r.methods.push(method);
}
});
if (!options.contentHandlers['application/json'])
options.contentHandlers['application/json'] = function(body, req, res) {
return JSON.parse(body);
};
this.routes.push(route);
return route;
};
if (!options.contentHandlers['application/x-www-form-urlencoded'])
options.contentHandlers['application/x-www-form-urlencoded'] =
function(body, req, res) {
return querystring.parse(body) || {};
};
if (!options.contentWriters['application/json'])
options.contentWriters['application/json'] = function(obj, req, res) {
return JSON.stringify(obj);
};
Server.prototype._request = function _request(req, res, expectContinue) {
var self = this;
if (!options.contentWriters['application/x-www-form-urlencoded'])
options.contentWriters['application/x-www-form-urlencoded'] =
function(obj, req, res) {
return querystring.stringify(obj) + '\n';
};
var request = new Request({
log4js: self.log4js,
request: req
});
var response = new Response({
log4js: self.log4js,
request: request,
response: res,
formatters: self.formatters,
expectContinue: expectContinue
});
if (!options.contentHandlers['application/xml'])
options.contentHandlers['application/xml'] =
function(body, req, res, callback) {
var parser = new xml2js.Parser();
parser.addListener('end', function(result) {
if (!result)
result = {};
logRequest(request);
Object.keys(result).forEach(function(k) {
try {
if (typeof(result[k]) === 'object' &&
typeof(result[k]['@']) === 'object' &&
result[k]['@'].type &&
result[k]['#']) {
switch (result[k]['@'].type) {
case 'integer':
result[k] = parseInt(result[k]['#'], 10);
break;
case 'boolean':
result[k] = /^true$/i.test(result[k]['#']);
break;
default:
result[k] = result[k]['#'];
break;
}
}
} catch (e) {}
});
if (result.id && typeof(result.id) === 'object')
delete result.id;
return callback(null, result);
});
parser.parseString(body);
};
var route = this._findRoute(request, response);
if (!route)
return false;
if (!options.contentWriters['application/xml']) {
options.contentWriters['application/xml'] =
function(obj) {
assert.equal(typeof(obj), 'object');
response.serverName = this.name;
return route.run(request, response);
};
var res = '<?xml version="1.0" encoding="UTF-8"?>\n';
function serialize(key, val, indent) {
var str = '';
Server.prototype._findRoute = function _findRoute(req, res) {
assert.ok(req);
assert.ok(res);
switch (typeof(val)) {
case 'string':
case 'boolean':
str += sprintf('%s<%s>%s</%s>\n', indent, key, val + '', key);
break;
case 'number':
str += sprintf('%s<%s type="integer">%s</%s>\n',
indent, key, val + '', key);
break;
var params;
var route;
var methods = [];
var versions = [];
case 'object':
if (Array.isArray(val)) {
val.forEach(function(v) {
str += serialize(key, v, indent + ' ');
});
} else if (val === null) {
str += sprintf('%s<%s/>\n', indent, key);
} else {
str += sprintf('%s<%s>\n', indent, key);
Object.keys(val).forEach(function(k) {
str += serialize(k, val[k], indent + ' ');
});
str += sprintf('%s</%s>\n', indent, key);
}
break;
default:
break;
}
for (var i = 0; i < this.routes.length; i++) {
var r = this.routes[i];
return str;
if ((params = r.matchesUrl(req))) {
if (r.matchesMethod(req)) {
if (r.matchesVersion(req)) {
route = r;
break;
} else {
if (r.version && versions.indexOf(r.version) === -1)
versions.push(r.version);
}
Object.keys(obj).forEach(function(key) {
res += serialize(key, obj[key], '');
});
return res;
};
}
if (!options.contentWriters['application/javascript'])
options.contentWriters['application/javascript'] =
function(obj, req, res) {
var query = url.parse(req.url, true).query;
var cbName = query && query.callback ? query.callback : 'callback';
var response = {
code: res.code,
data: obj
};
res.code = 200;
return cbName + '(' + JSON.stringify(response) + ');';
};
Object.keys(options.contentHandlers).forEach(function(k) {
if (typeof(options.contentHandlers[k]) !== 'function')
throw new TypeError('contentHandlers must be functions');
server._config.contentHandlers[k] = options.contentHandlers[k];
});
Object.keys(options.contentWriters).forEach(function(k) {
if (typeof(options.contentWriters[k]) !== 'function')
throw new TypeError('contentWriters must be functions');
server._config.contentWriters[k] = options.contentWriters[k];
});
// end contentHandlers/writers
if (options.formatError)
server._config.formatError = options.formatError;
if (options.headers) {
if (typeof(options.headers) !== 'object')
throw new TypeError('headers must be an object');
for (k in options.headers) {
if (options.headers.hasOwnProperty(k)) {
if (typeof(options.headers[k]) !== 'function')
throw new TypeError('headers values must be functions');
server._config.headers[k] = options.headers[k];
}
} else {
if (methods.indexOf(r.method) === -1)
methods.push(r.method);
}
}
}
if (!server._config.formatError)
server._config.formatError = function(res, e) {
if (res._accept === 'application/xml')
e = { error: e };
if (route) {
req.params = params || {};
res.methods = route.methods;
res.version = route.version;
} else {
res.methods = methods;
res.versions = versions;
return e;
};
if (versions.length) {
if (!this.listeners('VersionNotAllowed').length)
this.once('VersionNotAllowed', defaultBadVersionHandler);
if (!server._config.serverName)
server._config.serverName = 'node.js';
this.emit('VersionNotAllowed', req, res, versions);
} else if (methods.length) {
if (!this.listeners('MethodNotAllowed').length)
this.once('MethodNotAllowed', default405Handler);
if (!server._config.maxRequestSize)
server._config.maxRequestSize = 8192;
this.emit('MethodNotAllowed', req, res, methods);
} else {
if (!this.listeners('NotFound').length)
this.once('NotFound', default404Handler);
if (!server._config.clockSkew)
server._config.clockSkew = 300 * 1000; // Default 5m
if (!server.sendErrorLogLevel)
server._config.sendErrorLogger = log.warn;
if (!server._config.acceptable) {
server._config.acceptable = [
'application/json'
];
this.emit('NotFound', req, res);
}
server._config._acceptable = {};
for (var i = 0; i < server._config.acceptable.length; i++) {
var tmp = server._config.acceptable[i].split('/');
if (!server._config._acceptable[tmp[0]]) {
server._config._acceptable[tmp[0]] = [tmp[1]];
} else {
var found = false;
for (var j = 0; j < server._config._acceptable[tmp[0]].length; j++) {
if (server._config._acceptable[tmp[0]][j] === tmp[1]) {
found = true;
break;
}
}
if (!found) {
server._config._acceptable[tmp[0]].push(tmp[1]);
}
}
}
log.trace('server will accept types: %o', server._config.acceptable);
var foundXApiVersion = false;
var foundXRequestId = false;
var foundXResponseTime = false;
var foundContentLength = false;
var foundContentType = false;
var foundContentMD5 = false;
var foundACAO = false;
var foundACAM = false;
var foundACAH = false;
var foundACEH = false;
for (k in server._config.headers) {
if (server._config.headers.hasOwnProperty(k)) {
var h = k.toLowerCase();
switch (h) {
case 'x-api-version':
foundXApiVersion = true;
break;
case 'x-request-id':
foundXRequestId = true;
break;
case 'x-response-time':
foundXResponseTime = true;
break;
case 'content-length':
foundContentLength = true;
break;
case 'content-type':
foundContentType = true;
break;
case 'content-md5':
foundContentMD5 = true;
break;
case 'access-control-allow-origin':
foundACAO = true;
break;
case 'access-control-allow-method':
foundACAM = true;
break;
case 'access-control-allow-headers':
foundACAH = true;
break;
case 'access-control-expose-headers':
foundACEH = true;
break;
}
}
}
if (!foundXApiVersion) {
server._config.headers['X-Api-Version'] = function(res) {
return res._version;
};
}
if (!foundXRequestId) {
server._config.headers['X-Request-Id'] = function(res) {
return res.requestId;
};
}
if (!foundXResponseTime) {
server._config.headers['X-Response-Time'] = function(res) {
return res._time;
};
}
if (!foundContentLength) {
server._config.headers['Content-Length'] = function(res) {
if (!res.options.noEnd && res._data) {
res._bytes = Buffer.byteLength(res._data, 'utf8');
return res._bytes;
}
};
}
if (!foundContentMD5) {
server._config.headers['Content-MD5'] = function(res) {
if (res._data && res.options.code !== 204) {
if (!res.options.noContentMD5) {
var hash = crypto.createHash('md5');
hash.update(res._data);
return hash.digest('base64');
}
}
};
}
if (!foundContentType) {
server._config.headers['Content-Type'] = function(res) {
if (res._data && res.options.code !== 204)
return res._accept;
};
}
if (!foundACAO) {
server._config.headers['Access-Control-Allow-Origin'] = function(res) {
return '*';
};
}
if (!foundACAM) {
server._config.headers['Access-Control-Allow-Methods'] = function(res) {
if (res._allowedMethods && res._allowedMethods.length)
return res._allowedMethods.join(', ');
};
}
if (!foundACAH) {
server._config.headers['Access-Control-Allow-Headers'] = function(res) {
return [
'Accept',
'Content-Type',
'Content-Length',
'Date',
'X-Api-Version'
].join(', ');
};
}
if (!foundACEH) {
server._config.headers['Access-Control-Expose-Headers'] = function(res) {
return [
'X-Api-Version',
'X-Request-Id',
'X-Response-Time'
].join(', ');
};
}
return server;
}
return route || false;
};
{
"author": "Mark Cavage <mcavage@gmail.com>",
"name": "restify",
"description": "REST framework specifically meant for web service APIs",
"version": "0.5.6",
"homepage": "http://mcavage.github.com/node-restify",
"description": "REST framework",
"version": "1.0.0-1-rc",
"publishConfig": { "tag": "beta" },
"repository": {

@@ -9,32 +12,25 @@ "type": "git",

},
"author": "Mark Cavage <mcavage@gmail.com> (http://www.joyent.com)",
"main": "lib/restify.js",
"main": "lib/index.js",
"directories": {
"bin": "./bin",
"lib": "./lib"
},
"engines": {
"node": ">=0.6"
},
"dependencies": {
"formidable": "1.0.8",
"httpu": "1.0.1",
"node-uuid": "1.3.1",
"dtrace-provider": "0.0.5",
"http-signature": "0.9.7",
"mime": "1.2.4",
"node-uuid": "1.2.0",
"retry": "0.5.0",
"semver": "1.0.13",
"xml2js": "0.1.13"
"sprintf": "0.1.1",
"semver": "1.0.12"
},
"scripts": {
"pretest": "gjslint --nojsdoc -r . -x lib/sprintf.js -e node_modules",
"test": "./node_modules/.bin/whiskey --quiet --sequential --timeout 4000 -t \"`find tst -name *.test.js | xargs`\""
},
"man": [
"./docs/restify.3",
"./docs/restify-client.7",
"./docs/restify-log.7",
"./docs/restify-request.7",
"./docs/restify-response.7",
"./docs/restify-routes.7",
"./docs/restify-throttle.7",
"./docs/restify-versions.7"
],
"devDependencies": {
"whiskey": "0.6.3"
"tap": "0.1.3"
},
"engines": {
"node": ">=0.4"
"scripts": {
"test": "./node_modules/.bin/tap ./tst/*.test.js"
}
}

@@ -1,46 +0,1 @@

node-restify is meant to do one thing: make it easy to build an API webservice
in node.js that is correct as per the HTTP RFC. That's it. It's not MVC, it
doesn't bring in a lot of baggage, it's just a small framework to let you
build a web service API.
## Usage
var restify = require('restify');
var server = restify.createServer();
server.get('/my/:name', function(req, res) {
res.send(200, {
name: req.uriParams.name
});
});
server.post('/my', function(req, res) {
// name could be in the query string, in a form-urlencoded body, or a
// JSON body
res.send(201, {
name: req.params.name
});
});
server.del('/my/:name', function(req, res) {
res.send(204);
});
server.listen(8080);
## Installation
npm install restify
## For More Information
See <http://mcavage.github.com/node-restify>.
## License
MIT.
## Bugs
See <https://github.com/mcavage/node-restify/issues>.
See http://mcavage.github.com/node-restify.
// Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved.
var http = require('httpu');
var test = require('tap').test;
var uuid = require('node-uuid');
var common = require('./lib/common');
var restify = require('../lib/restify');
restify.log.level(restify.LogLevel.Trace);
var log4js = require('../lib/log4js_stub');
var restify = require('../lib');
// --- Globals
var client = null;
var server = null;
var socket = '/tmp/.' + uuid();
///--- Globals
var PORT = process.env.UNIT_TEST_PORT || 12345;
var client;
var server;
// --- Tests
exports.setUp = function(test, assert) {
server = restify.createServer({
apiVersion: '1.2.3',
serverName: 'RESTify'
///--- Helpers
function sendJson(req, res, next) {
res.send({
hello: req.params.hello || req.params.name || null
});
return next();
}
function handle(req, res, next) {
var code = req.params.code || 200;
req.params.name = req.uriParams.name;
res.send(code, req.params);
}
server.put('/test/:name', handle);
server.post('/test/:name', handle);
server.get('/test/:name', handle);
server.del('/test/:name', handle);
server.head('/test/:name', handle);
server.head('/fail', function(req, res, next) {
res.send(503);
});
function sendText(req, res, next) {
res.send('hello ' + (req.params.hello || req.params.name || ''));
server.listen(socket, function() {
client = restify.createClient({
socketPath: socket,
version: '1.2.3',
retryOptions: {
retries: 1
}
});
test.finish();
});
};
return next();
}
exports.test_put_utf8 = function(test, assert) {
var req = {
path: '/test/foo',
expect: 200,
body: {
foo: 'Iñtërnâtiônàlizætiøn',
code: 200
}
};
client.put(req, function(err, obj) {
assert.ifError(err);
assert.ok(obj);
assert.equal(obj.name, 'foo');
assert.equal(obj.foo, 'Iñtërnâtiônàlizætiøn');
test.finish();
});
};
exports.test_put_expect = function(test, assert) {
var req = {
path: '/test/foo',
expect: 200,
body: {
foo: 'bar',
code: 200
}
};
///--- Tests
client.put(req, function(err, obj) {
assert.ifError(err);
assert.ok(obj);
assert.equal(obj.name, 'foo');
assert.equal(obj.foo, 'bar');
test.finish();
log4js.setGlobalLogLevel('TRACE');
test('setup', function(t) {
server = restify.createServer({
log4js: log4js
});
};
t.ok(server);
server.use(restify.acceptParser(['json', 'text/plain']));
server.use(restify.dateParser());
server.use(restify.authorizationParser());
server.use(restify.queryParser());
server.use(restify.bodyParser());
exports.test_put_no_expect = function(test, assert) {
var req = {
path: '/test/foo',
expect: [204],
body: {
foo: 'bar',
code: 204
}
};
server.get('/json/:name', sendJson);
server.head('/json/:name', sendJson);
server.put('/json/:name', sendJson);
server.post('/json/:name', sendJson);
client.put(req, function(err, obj) {
assert.ifError(err);
test.finish();
server.del('/str/:name', sendText);
server.get('/str/:name', sendText);
server.head('/str/:name', sendText);
server.put('/str/:name', sendText);
server.post('/str/:name', sendText);
server.listen(PORT, '127.0.0.1', function() {
t.end();
});
};
});
exports.test_post_expect = function(test, assert) {
var req = {
path: '/test/foo',
expect: 201,
body: {
foo: 'bar'
},
query: {
code: 201
}
};
client.post(req, function(err, obj, headers) {
assert.ifError(err);
assert.ok(obj);
assert.ok(headers);
assert.equal(obj.name, 'foo');
assert.equal(obj.foo, 'bar');
test.finish();
test('create json client', function(t) {
client = restify.createClient({
log4js: log4js,
url: 'http://127.0.0.1:' + PORT,
type: 'json'
});
};
t.ok(client);
t.ok(client instanceof restify.JsonClient);
t.end();
});
exports.test_post_expect = function(test, assert) {
var req = {
path: '/test/foo',
body: {
foo: 'bar'
},
query: {
code: 200
}
};
client.post(req, function(err, obj, headers) {
assert.ifError(err);
assert.ok(obj);
assert.ok(headers);
assert.equal(obj.name, 'foo');
assert.equal(obj.foo, 'bar');
test.finish();
test('GET json', function(t) {
client.get('/json/mcavage', function(err, req, res, obj) {
t.ifError(err);
t.ok(req);
t.ok(res);
t.equivalent(obj, {hello: 'mcavage'});
t.end();
});
};
});
exports.test_get_expect = function(test, assert) {
var req = {
path: '/test/foo',
expect: 200,
query: {
foo: 'bar',
code: 200
}
};
client.get(req, function(err, obj) {
assert.ifError(err);
assert.ok(obj);
assert.equal(obj.name, 'foo');
assert.equal(obj.foo, 'bar');
test.finish();
test('Check error (404)', function(t) {
client.get('/' + uuid(), function(err, req, res, obj) {
t.ok(err);
t.ok(err.message);
t.equal(err.statusCode, 404);
t.ok(req);
t.ok(res);
t.ok(obj);
t.equal(obj.code, 'ResourceNotFound');
t.ok(obj.message);
t.end();
});
};
});
exports.test_get_no_expect = function(test, assert) {
var req = {
path: '/test/foo',
query: {
foo: 'bar',
code: 200
}
};
client.get(req, function(err, obj) {
assert.ifError(err);
assert.ok(obj);
assert.equal(obj.name, 'foo');
assert.equal(obj.foo, 'bar');
test.finish();
test('HEAD json', function(t) {
client.head('/json/mcavage', function(err, req, res) {
t.ifError(err);
t.ok(req);
t.ok(res);
t.end();
});
};
});
exports.test_del_expect = function(test, assert) {
var req = {
path: '/test/foo',
expect: 200,
query: {
code: 200
}
};
client.del(req, function(err, headers) {
assert.ifError(err);
assert.ok(headers);
test.finish();
test('POST json', function(t) {
client.post('/json/mcavage', { hello: 'foo' }, function(err, req, res, obj) {
t.ifError(err);
t.ok(req);
t.ok(res);
t.equivalent(obj, {hello: 'foo'});
t.end();
});
};
});
exports.test_del_no_expect = function(test, assert) {
var req = {
path: '/test/foo',
query: {
code: 204
}
};
client.del(req, function(err, headers) {
assert.ifError(err);
assert.ok(headers);
test.finish();
test('PUT json', function(t) {
client.post('/json/mcavage', { hello: 'foo' }, function(err, req, res, obj) {
t.ifError(err);
t.ok(req);
t.ok(res);
t.equivalent(obj, {hello: 'foo'});
t.end();
});
};
});
exports.test_head_expect = function(test, assert) {
var req = {
path: '/test/foo',
expect: 200,
query: {
code: 200
}
};
client.head(req, function(err, headers) {
assert.ifError(err);
assert.ok(headers);
assert.ok(headers['content-length']);
test.finish();
test('create string client', function(t) {
client = restify.createClient({
log4js: log4js,
url: 'http://127.0.0.1:' + PORT,
type: 'string'
});
};
t.ok(client);
t.ok(client instanceof restify.StringClient);
t.end();
});
exports.test_head_no_expect = function(test, assert) {
var req = {
path: '/test/foo',
query: {
code: 204
}
};
client.head(req, function(err, headers) {
assert.ifError(err);
assert.ok(headers);
test.finish();
test('GET text', function(t) {
client.get('/str/mcavage', function(err, req, res, data) {
t.ifError(err);
t.ok(req);
t.ok(res);
t.equal(res.body, data);
t.equal(data, 'hello mcavage');
t.end();
});
};
});
exports.test_retries = function(test, assert) {
var req = {
path: '/fail'
};
client.head(req, function(err, headers) {
assert.ok(!headers);
assert.ok(err);
assert.equal(err.name, 'HttpError');
assert.equal(err.httpCode, 503);
assert.equal(err.restCode, 'RetriesExceeded');
assert.equal(err.message, 'Maximum number of retries exceeded: 2');
test.finish();
test('HEAD text', function(t) {
client.head('/str/mcavage', function(err, req, res) {
t.ifError(err);
t.ok(req);
t.ok(res);
t.end();
});
};
});
exports.test_form_url_encoding = function(test, assert) {
var _client = restify.createClient({
socketPath: socket,
version: '1.2.3',
contentType: 'application/x-www-form-urlencoded',
retryOptions: {
retries: 1
}
test('Check error (404)', function(t) {
client.get('/' + uuid(), function(err, req, res, message) {
t.ok(err);
t.ok(err.message);
t.equal(err.statusCode, 404);
t.ok(req);
t.ok(res);
t.ok(message);
t.end();
});
});
var req = {
path: '/test/foo',
body: {
foo: 'bar'
},
query: {
code: 200
}
};
_client.post(req, function(err, obj, headers) {
assert.ifError(err);
assert.ok(obj);
assert.ok(headers);
assert.equal(obj.name, 'foo');
assert.equal(obj.foo, 'bar');
test.finish();
test('POST text', function(t) {
client.post('/str/mcavage', 'hello=foo', function(err, req, res, data) {
t.ifError(err);
t.ok(req);
t.ok(res);
t.equal(res.body, data);
t.equal(data, 'hello foo');
t.end();
});
};
});
exports.test_head_string = function(test, assert) {
client.head('/test/foo?code=204', function(err, headers) {
assert.ifError(err);
assert.ok(headers);
test.finish();
test('POST text (object)', function(t) {
client.post('/str/mcavage', {hello: 'foo'}, function(err, req, res, data) {
t.ifError(err);
t.ok(req);
t.ok(res);
t.equal(res.body, data);
t.equal(data, 'hello foo');
t.end();
});
};
});
exports.test_get_string = function(test, assert) {
client.get('/test/foo', function(err, headers) {
assert.ifError(err);
assert.ok(headers);
test.finish();
test('PUT text', function(t) {
client.put('/str/mcavage', 'hello=foo', function(err, req, res, data) {
t.ifError(err);
t.ok(req);
t.ok(res);
t.equal(res.body, data);
t.equal(data, 'hello foo');
t.end();
});
};
});
exports.test_put_string = function(test, assert) {
client.put('/test/foo', function(err, headers) {
assert.ifError(err);
assert.ok(headers);
test.finish();
test('DELETE text', function(t) {
client.del('/str/mcavage', function(err, req, res) {
t.ifError(err);
t.ok(req);
t.ok(res);
t.end();
});
};
});
exports.test_post_string = function(test, assert) {
client.post('/test/foo', function(err, headers) {
assert.ifError(err);
assert.ok(headers);
test.finish();
test('create raw client', function(t) {
client = restify.createClient({
log4js: log4js,
url: 'http://127.0.0.1:' + PORT,
type: 'http',
accept: 'text/plain'
});
};
t.ok(client);
t.ok(client instanceof restify.HttpClient);
t.end();
});
exports.test_del_string = function(test, assert) {
client.del('/test/foo', function(err, headers) {
assert.ifError(err);
assert.ok(headers);
test.finish();
test('GET raw', function(t) {
client.get('/str/mcavage', function(connectErr, req) {
t.ifError(connectErr);
t.ok(req);
req.on('result', function(err, res) {
t.ifError(err);
res.body = '';
res.setEncoding('utf8');
res.on('data', function(chunk) {
res.body += chunk;
});
res.on('end', function() {
t.equal(res.body, 'hello mcavage');
t.end();
});
});
});
};
});
exports.test_url_in_constructor = function(test, assert) {
var client = restify.createClient({
path: '/test/foo',
socketPath: socket,
version: '1.2.3',
retryOptions: {
retries: 1
test('POST raw', function(t) {
var opts = {
path: '/str/mcavage',
headers: {
'content-type': 'application/x-www-form-urlencoded'
}
});
};
client.post(opts, function(connectErr, req) {
t.ifError(connectErr);
client.del(function(err, headers) {
assert.ifError(err);
assert.ok(headers);
test.finish();
});
};
req.write('hello=snoopy');
req.end();
req.on('result', function(err, res) {
t.ifError(err);
res.body = '';
res.setEncoding('utf8');
res.on('data', function(chunk) {
res.body += chunk;
});
exports.test_url_prefix = function(test, assert) {
var client = restify.createClient({
path: '/test',
socketPath: socket,
version: '1.2.3',
retryOptions: {
retries: 1
}
res.on('end', function() {
t.equal(res.body, 'hello snoopy');
t.end();
});
});
});
});
client.del({ path: '/foo' }, function(err, headers) {
assert.ifError(err);
assert.ok(headers);
test.finish();
test('teardown', function(t) {
server.close(function() {
t.end();
});
};
});
exports.tearDown = function(test, assert) {
server.on('close', function() {
test.finish();
});
server.close();
};

@@ -1,553 +0,453 @@

// Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved.
var fs = require('fs');
var http = require('httpu');
var https = require('httpu');
var uuid = require('node-uuid');
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
var common = require('./lib/common');
var restify = require('../lib/restify');
var http = require('http');
var newError = restify.newError;
var log = restify.log;
log.level(log.Level.Trace);
var d = require('dtrace-provider');
var test = require('tap').test;
var uuid = require('node-uuid');
var HttpError = require('../lib/errors').HttpError;
var RestError = require('../lib/errors').RestError;
var log4js = require('../lib/log4js_stub');
var Request = require('../lib/request');
var Response = require('../lib/response');
var Server = require('../lib/server');
// --- Globals
var socket = '/tmp/.' + uuid();
///--- Globals
var DTRACE = d.createDTraceProvider('restifyUnitTest');
var PORT = process.env.UNIT_TEST_PORT || 12345;
var _handler = function(req, res, next) {
res.send(200);
return next();
};
///--- Tests
// --- Helpers
test('throws on missing options', function(t) {
t.throws(function() {
return new Server();
}, new TypeError('options (Object) required'));
t.end();
});
function _pad(val) {
if (parseInt(val, 10) < 10) {
val = '0' + val;
}
return val;
}
test('throws on missing log4js', function(t) {
t.throws(function() {
return new Server({});
}, new TypeError('options.dtrace (Object) required'));
t.end();
});
function _rfc822(date) {
var months = ['Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'];
var days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
return days[date.getUTCDay()] + ', ' +
_pad(date.getUTCDate()) + ' ' +
months[date.getUTCMonth()] + ' ' +
date.getUTCFullYear() + ' ' +
_pad(date.getUTCHours()) + ':' +
_pad(date.getUTCMinutes()) + ':' +
_pad(date.getUTCSeconds()) +
' GMT';
}
test('throws on missing log4js', function(t) {
t.throws(function() {
return new Server({ dtrace: {} });
}, new TypeError('options.log4js (Object) required'));
t.end();
});
// --- Tests
test('ok', function(t) {
t.ok(new Server({ dtrace: DTRACE, log4js: log4js }));
t.end();
});
exports.test_create_no_options = function(test, assert) {
var server = restify.createServer();
var socket = '/tmp/.' + uuid();
server.get('/', _handler);
server.listen(socket, function() {
var opts = common.newOptions(socket, '/');
http.request(opts, function(res) {
common.checkResponse(assert, res);
assert.equal(res.headers.server, 'node.js');
assert.equal(res.statusCode, 200);
server.on('close', function() {
test.finish();
});
server.close();
}).end();
});
};
test('ok (ssl)', function(t) {
// Lame, just make sure we go down the https path
try {
t.ok(new Server({
dtrace: DTRACE,
log4js: log4js,
certificate: 'hello',
key: 'world'
}));
t.fail('HTTPS server not created');
} catch (e) {
// noop
}
t.end();
});
exports.test_create_empty_options = function(test, assert) {
var server = restify.createServer({});
var socket = '/tmp/.' + uuid();
server.get('/', _handler);
server.listen(socket, function() {
var opts = common.newOptions(socket, '/');
http.request(opts, function(res) {
common.checkResponse(assert, res);
assert.equal(res.headers.server, 'node.js');
assert.equal(res.statusCode, 200);
server.on('close', function() {
test.finish();
});
server.close();
}).end();
test('listen and close (port only)', function(t) {
var server = new Server({ dtrace: DTRACE, log4js: log4js });
server.listen(PORT, function() {
server.close(function() {
t.end();
});
});
};
});
exports.test_server_name = function(test, assert) {
var server = restify.createServer({
serverName: 'foo'
test('listen and close (port and hostname)', function(t) {
var server = new Server({ dtrace: DTRACE, log4js: log4js });
server.listen(PORT, '127.0.0.1', function() {
server.close(function() {
t.end();
});
});
var socket = '/tmp/.' + uuid();
});
server.get('/', _handler);
server.listen(socket, function() {
var opts = common.newOptions(socket, '/');
http.request(opts, function(res) {
common.checkResponse(assert, res);
assert.equal(res.headers.server, 'foo');
assert.equal(res.statusCode, 200);
server.on('close', function() {
test.finish();
});
server.close();
}).end();
test('listen and close (socketPath)', function(t) {
var server = new Server({ dtrace: DTRACE, log4js: log4js });
server.listen('/tmp/.' + uuid(), function() {
server.close(function() {
t.end();
});
});
};
});
exports.test_max_request_size = function(test, assert) {
var server = restify.createServer({
maxRequestSize: 5
test('get (path only)', function(t) {
var server = new Server({ dtrace: DTRACE, log4js: log4js });
server.get('/foo/:id', function tester(req, res, next) {
t.ok(req.params);
t.equal(req.params.id, 'bar');
res.send();
return next();
});
var socket = '/tmp/.' + uuid();
server.post('/', _handler);
server.listen(socket, function() {
var opts = common.newOptions(socket, '/');
opts.method = 'POST';
var req = http.request(opts, function(res) {
common.checkResponse(assert, res);
assert.equal(res.statusCode, 413);
server.on('close', function() {
test.finish();
});
server.close();
var done = 0;
server.listen(PORT, function() {
var opts = {
hostname: 'localhost',
port: PORT,
path: '/foo/bar',
agent: false
};
http.get(opts, function(res) {
t.equal(res.statusCode, 200);
if (++done == 2) {
server.close(function() {
t.end();
});
}
});
req.write(JSON.stringify({ ThisIsALongString: uuid()}, null, 2));
req.end();
});
};
exports.test_clock_ok = function(test, assert) {
var server = restify.createServer();
var socket = '/tmp/.' + uuid();
server.get('/', _handler);
server.listen(socket, function() {
var opts = common.newOptions(socket, '/');
opts.headers.Date = _rfc822(new Date());
http.request(opts, function(res) {
common.checkResponse(assert, res);
assert.equal(res.statusCode, 200);
server.on('close', function() {
test.finish();
server.on('after', function(req, res) {
t.ok(req);
t.ok(res);
if (++done == 2) {
server.close(function() {
t.end();
});
server.close();
}).end();
}
});
};
});
exports.test_clock_skew = function(test, assert) {
var server = restify.createServer();
var socket = '/tmp/.' + uuid();
test('get (path and version ok)', function(t) {
var server = new Server({ dtrace: DTRACE, log4js: log4js });
server.get({
url: '/foo/:id',
version: '1.2.3'
}, function tester(req, res, next) {
t.ok(req.params);
t.equal(req.params.id, 'bar');
res.send();
return next();
});
server.get('/', _handler);
server.listen(socket, function() {
var opts = common.newOptions(socket, '/');
opts.headers.Date = _rfc822(new Date(1995, 11, 17, 3, 24, 0));
var done = 0;
server.listen(PORT, function() {
var opts = {
hostname: 'localhost',
port: PORT,
path: '/foo/bar',
agent: false,
headers: {
'accept-version': '~1.2'
}
};
http.get(opts, function(res) {
t.equal(res.statusCode, 200);
if (++done == 2) {
server.close(function() {
t.end();
});
}
});
});
http.request(opts, function(res) {
res._skipAllowedMethods = true;
common.checkResponse(assert, res);
assert.equal(res.statusCode, 400);
server.on('close', function() {
test.finish();
server.on('after', function(req, res) {
t.ok(req);
t.ok(res);
if (++done == 2) {
server.close(function() {
t.end();
});
server.close();
}).end();
}
});
};
});
exports.test_regex_route = function(test, assert) {
var server = restify.createServer();
var socket = '/tmp/.' + uuid();
test('get (path and version not ok)', function(t) {
var server = new Server({ dtrace: DTRACE, log4js: log4js });
server.get({
url: '/foo/:id',
version: '1.2.3'
}, function(req, res, next) {
t.ok(req.params);
t.equal(req.params.id, 'bar');
res.send();
return next();
});
server.get(/^\/users?(?:\/(\d+)(?:\.\.(\d+))?)?/, _handler);
server.listen(socket, function() {
var opts = common.newOptions(socket, '/users/1..15');
server.get({
url: '/foo/:id',
version: '1.2.4'
}, function(req, res, next) {
t.ok(req.params);
t.equal(req.params.id, 'bar');
res.send();
return next();
});
http.request(opts, function(res) {
common.checkResponse(assert, res);
assert.equal(res.statusCode, 200);
server.on('close', function() {
test.finish();
server.listen(PORT, function() {
var opts = {
hostname: 'localhost',
port: PORT,
path: '/foo/bar',
agent: false,
headers: {
'accept': 'text/plain',
'accept-version': '~2.1'
}
};
http.get(opts, function(res) {
t.equal(res.statusCode, 400);
res.setEncoding('utf8');
res.body = '';
res.on('data', function(chunk) {
res.body += chunk;
});
server.close();
}).end();
res.on('end', function() {
t.equal(res.body, 'GET /foo/bar supports versions: 1.2.3, 1.2.4');
server.close(function() {
t.end();
});
});
});
});
};
});
exports.test_create_ssl = function(test, assert) {
var server = restify.createServer({
cert: fs.readFileSync(__dirname + '/test_cert.pem', 'ascii'),
key: fs.readFileSync(__dirname + '/test_key.pem', 'ascii')
test('use + get (path only)', function(t) {
var server = new Server({ dtrace: DTRACE, log4js: log4js });
var handler = 0;
server.use(function(req, res, next) {
handler++;
return next();
});
assert.ok(server);
assert.ok(server.cert);
assert.ok(server.key);
server.get('/', function(req, res, next) { res.send(200); return next(); });
server.listen(socket, function() {
// Can't actually drive requests over httpu for SSL.
server.on('close', function() {
test.finish();
});
server.close();
server.get('/foo/:id', function tester(req, res, next) {
t.ok(req.params);
t.equal(req.params.id, 'bar');
handler++;
res.send();
return next();
});
};
exports.test_abort_pre_send = function(test, assert) {
var server = restify.createServer();
var socket = '/tmp/.' + uuid();
server.get('/',
[function(req, res, next) {
res.send(200);
return next();
}],
function(req, res, next) {
assert.ok(false, 'FAIL! main handler invoked');
},
[function(req, res, next) {
server.on('close', function() {
test.finish();
});
server.close();
}]);
server.listen(socket, function() {
var opts = common.newOptions(socket, '/');
http.request(opts, function(res) {
common.checkResponse(assert, res);
assert.equal(res.statusCode, 200);
}).end();
server.listen(PORT, function() {
var opts = {
hostname: 'localhost',
port: PORT,
path: '/foo/bar',
agent: false
};
http.get(opts, function(res) {
t.equal(res.statusCode, 200);
t.equal(handler, 2);
server.close(function() {
t.end();
});
});
});
};
});
exports.test_abort_pre_error = function(test, assert) {
var server = restify.createServer();
var socket = '/tmp/.' + uuid();
server.get('/',
[function(req, res, next) {
res.sendError(newError());
return next();
}],
function(req, res, next) {
assert.ok(false, 'FAIL! main handler invoked');
},
[function(req, res, next) {
server.on('close', function() {
test.finish();
});
server.close();
}]);
test('rm', function(t) {
var server = new Server({ dtrace: DTRACE, log4js: log4js });
server.listen(socket, function() {
var opts = common.newOptions(socket, '/');
http.request(opts, function(res) {
common.checkResponse(assert, res);
assert.equal(res.statusCode, 500);
}).end();
server.get('/foo/:id', function(req, res, next) {
return next();
});
};
exports.test_abort_main_error = function(test, assert) {
var server = restify.createServer();
var socket = '/tmp/.' + uuid();
server.get('/',
[function(req, res, next) {
return next();
}],
function(req, res, next) {
res.sendError(newError());
return next();
},
function(req, res, next) {
assert.ok(false, 'FAIL! main handler invoked');
},
[function(req, res, next) {
server.on('close', function() {
test.finish();
});
server.close();
}]);
server.listen(socket, function() {
var opts = common.newOptions(socket, '/');
http.request(opts, function(res) {
common.checkResponse(assert, res);
assert.equal(res.statusCode, 500);
}).end();
server.get('/bar/:id', function(req, res, next) {
t.ok(req.params);
t.equal(req.params.id, 'foo');
res.send();
return next();
});
};
t.ok(server.rm('GET /foo/:id'));
exports.test_main_no_abort = function(test, assert) {
var server = restify.createServer();
var socket = '/tmp/.' + uuid();
var called = false;
server.get('/',
[function(req, res, next) {
return next();
}],
function(req, res, next) {
res.send(200);
return next();
},
function(req, res, next) {
called = true;
return next();
},
[function(req, res, next) {
server.on('close', function() {
test.finish();
});
assert.ok(called);
server.close();
}]);
server.listen(socket, function() {
var opts = common.newOptions(socket, '/');
http.request(opts, function(res) {
common.checkResponse(assert, res);
assert.equal(res.statusCode, 200);
}).end();
server.listen(PORT, function() {
var opts = {
hostname: 'localhost',
port: PORT,
path: '/foo/bar',
agent: false
};
http.get(opts, function(res) {
t.equal(res.statusCode, 404);
opts.path = '/bar/foo';
http.get(opts, function(res2) {
t.equal(res2.statusCode, 200);
server.close(function() {
t.end();
});
});
});
});
};
});
exports.test_gh_27 = function(test, assert) {
var server = restify.createServer();
test('405', function(t) {
var server = new Server({ dtrace: DTRACE, log4js: log4js });
var called = false;
function before(req, res, next) {
function anything() {
return next();
}
return anything();
}
server.get('/foo', [before], function(req, res, next) {
called = true;
res.send(200);
server.post('/foo/:id', function tester(req, res, next) {
t.ok(req.params);
t.equal(req.params.id, 'bar');
res.send();
return next();
});
var socket = '/tmp/.' + uuid();
server.listen(socket, function() {
var opts = common.newOptions(socket, '/foo');
http.request(opts, function(res) {
common.checkResponse(assert, res);
assert.equal(res.statusCode, 200);
server.on('close', function() {
test.finish();
server.listen(PORT, function() {
var opts = {
hostname: 'localhost',
port: PORT,
path: '/foo/bar',
agent: false
};
http.get(opts, function(res) {
t.equal(res.statusCode, 405);
t.equal(res.headers.allow, 'POST');
server.close(function() {
t.end();
});
assert.ok(called);
server.close();
}).end();
});
});
};
});
exports.test_custom_content = function(test, assert) {
var server = restify.createServer({
contentHandlers: {
'application/foo': function(body) {
assert.ok(body);
test('PUT ok', function(t) {
var server = new Server({ dtrace: DTRACE, log4js: log4js });
return JSON.parse(body);
}
},
contentWriters: {
'application/foo': function(obj) {
assert.ok(obj);
return JSON.stringify(obj);
}
},
accept: ['application/json', 'application/foo']
});
server.post('/custom_content', function(req, res, next) {
assert.equal(req.params.json, 'foo');
res.send(200, {foo: 'bar'});
server.put('/foo/:id', function tester(req, res, next) {
t.ok(req.params);
t.equal(req.params.id, 'bar');
res.send();
return next();
});
var socket = '/tmp/.' + uuid();
server.listen(socket, function() {
var content = JSON.stringify({json: 'foo'});
var opts = common.newOptions(socket, '/custom_content');
opts.method = 'POST';
opts.headers.Accept = 'application/foo';
opts.headers['Content-Type'] = 'application/foo';
opts.headers['Content-Length'] = content.length;
var req = http.request(opts, function(res) {
common.checkResponse(assert, res);
assert.equal(res.statusCode, 200);
common.checkContent(assert, res, function() {
assert.equal(res.params.foo, 'bar');
server.on('close', function() {
test.finish();
});
server.close();
}, 'application/foo');
});
req.write(content);
req.end();
server.listen(PORT, function() {
var opts = {
hostname: 'localhost',
port: PORT,
path: '/foo/bar',
method: 'PUT',
agent: false
};
http.request(opts, function(res) {
t.equal(res.statusCode, 200);
server.close(function() {
t.end();
});
}).end();
});
};
});
exports.test_custom_headers = function(test, assert) {
var server = restify.createServer({
headers: {
'access-control-allow-headers': function(res) {
return [
'x-unit-test'
].join(', ');
},
'X-Unit-Test': function(res) {
return 'foo';
}
}
});
test('HEAD ok', function(t) {
var server = new Server({ dtrace: DTRACE, log4js: log4js });
server.get('/custom_headers', function(req, res, next) {
res.send(200);
server.head('/foo/:id', function tester(req, res, next) {
t.ok(req.params);
t.equal(req.params.id, 'bar');
res.send('hi there');
return next();
});
var socket = '/tmp/.' + uuid();
server.listen(socket, function() {
var content = JSON.stringify({json: 'foo'});
var opts = common.newOptions(socket, '/custom_headers');
var req = http.request(opts, function(res) {
common.checkResponse(assert, res);
assert.equal(res.statusCode, 200);
console.log(res.headers);
assert.equal(res.headers['access-control-allow-headers'], 'x-unit-test');
assert.equal(res.headers['x-unit-test'], 'foo');
server.on('close', function() {
test.finish();
server.listen(PORT, function() {
var opts = {
hostname: 'localhost',
port: PORT,
path: '/foo/bar',
method: 'HEAD',
agent: false
};
http.request(opts, function(res) {
t.equal(res.statusCode, 200);
res.on('data', function(chunk) {
t.fail('Data was sent on HEAD');
});
server.close();
});
req.write(content);
req.end();
server.close(function() {
t.end();
});
}).end();
});
};
});
exports.test_send_error = function(test, assert) {
var server = restify.createServer();
var socket = '/tmp/.' + uuid();
test('DELETE ok', function(t) {
var server = new Server({ dtrace: DTRACE, log4js: log4js });
server.get('/foo', function(req, res, next) {
return next(newError());
server.del('/foo/:id', function tester(req, res, next) {
t.ok(req.params);
t.equal(req.params.id, 'bar');
res.send(204, 'hi there');
return next();
});
server.listen(socket, function() {
var opts = common.newOptions(socket, '/users/1..15');
http.get({
path: '/foo',
socketPath: socket
}, function(res) {
common.checkResponse(assert, res);
assert.equal(res.statusCode, 500);
server.on('close', function() {
test.finish();
server.listen(PORT, function() {
var opts = {
hostname: 'localhost',
port: PORT,
path: '/foo/bar',
method: 'DELETE',
agent: false
};
http.request(opts, function(res) {
t.equal(res.statusCode, 204);
res.on('data', function(chunk) {
t.fail('Data was sent on 204');
});
server.close();
server.close(function() {
t.end();
});
}).end();
});
};
});
// GH-49
exports.test_route_with_content_type_suffix = function(test, assert) {
var server = restify.createServer();
var socket = '/tmp/.' + uuid();
test('OPTIONS', function(t) {
var server = new Server({ dtrace: DTRACE, log4js: log4js });
server.get('/:foo', function(req, res, next) {
assert.ok(req.uriParams.foo);
var found = false;
if (req.uriParams.foo === 'blah' || req.uriParams.foo === 'mark.cavage')
found = true;
assert.ok(found);
res.send(202);
server.get('/foo/:id', function tester(req, res, next) {
t.ok(req.params);
t.equal(req.params.id, 'bar');
res.send();
return next();
});
server.on('close', function() {
test.finish();
});
server.listen(socket, function() {
var opts = common.newOptions(socket, '/blah.xml');
server.listen(PORT, function() {
var opts = {
hostname: 'localhost',
port: PORT,
path: '/foo/bar',
method: 'OPTIONS',
agent: false
};
http.request(opts, function(res) {
common.checkResponse(assert, res);
assert.equal(res.statusCode, 202);
opts = common.newOptions(socket, '/mark.cavage');
http.request(opts, function(res) {
common.checkResponse(assert, res);
assert.equal(res.statusCode, 202);
server.close();
}).end();
t.equal(res.statusCode, 200);
t.ok(res.headers.allow);
server.close(function() {
t.end();
});
}).end();
});
};
});
// Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved.
var http = require('httpu');
var http = require('http');
var d = require('dtrace-provider');
var test = require('tap').test;
var uuid = require('node-uuid');
var common = require('./lib/common');
var restify = require('../lib/restify');
restify.log.level(restify.LogLevel.Trace);
var createClient = require('../lib').createClient;
var HttpError = require('../lib/errors').HttpError;
var RestError = require('../lib/errors').RestError;
var log4js = require('../lib/log4js_stub');
var Request = require('../lib/request');
var Response = require('../lib/response');
var Server = require('../lib/server');
var throttle = require('../lib/plugins/throttle');
// --- Globals
var options = {};
var server = null;
var socket = '/tmp/.' + uuid();
///--- Globals
var DTRACE = d.createDTraceProvider('throttleUnitTest');
var PORT = process.env.UNIT_TEST_PORT || 12345;
var client;
var server;
var username = uuid();

@@ -20,11 +31,16 @@ var password = uuid();

// --- Tests
//--- Tests
exports.setUp = function(test, assert) {
server = restify.createServer({
apiVersion: '1.2.3',
serverName: 'RESTify'
test('setup', function(t) {
server = new Server({ dtrace: DTRACE, log4js: log4js });
t.ok(server);
server.use(function(req, res, next) {
if (req.params.name)
req.username = req.params.name;
return next();
});
var throttle = restify.createThrottle({
server.use(throttle({
burst: 1,

@@ -43,100 +59,92 @@ rate: 0.5,

}
}));
server.get('/test/:name', function(req, res, next) {
res.send();
return next();
});
server.get('/test/:name',
function(req, res, next) {
req.username = req.uriParams.name;
return next();
},
throttle,
function(req, res, next) {
res.send(200);
return next();
}
);
server.listen(socket, function() {
test.finish();
server.listen(PORT, '127.0.0.1', function() {
client = createClient({
dtrace: DTRACE,
log4js: log4js,
name: 'throttleUnitTest',
type: 'string',
url: 'http://127.0.0.1:' + PORT
});
t.ok(client);
t.end();
});
};
});
exports.test_ok = function(test, assert) {
var opts = common.newOptions(socket, '/test/throttleMe');
opts.method = 'GET';
test('ok', function(t) {
client.get('/test/throttleMe', function(err, req, res, body) {
t.ifError(err);
t.equal(res.statusCode, 200);
t.end();
});
});
http.request(opts, function(res) {
common.checkResponse(assert, res);
assert.equal(res.statusCode, 200);
test.finish();
}).end();
};
test('throttled', function(t) {
client.get('/test/throttleMe', function(err, req, res, body) {
t.ok(err);
t.equal(err.statusCode, 429);
t.ok(err.message);
t.equal(res.statusCode, 429);
setTimeout(function() { t.end(); }, 2100);
});
});
exports.test_throttled = function(test, assert) {
var opts = common.newOptions(socket, '/test/throttleMe');
opts.method = 'GET';
http.request(opts, function(res) {
common.checkResponse(assert, res);
assert.equal(res.statusCode, 420);
common.checkContent(assert, res, function() {
assert.ok(res.params);
assert.equal(res.params.code, 'RequestThrottled');
assert.ok(res.params.message);
setTimeout(function() { test.finish(); }, 2100);
});
}).end();
};
test('ok after tokens', function(t) {
client.get('/test/throttleMe', function(err, req, res, body) {
t.ifError(err);
t.equal(res.statusCode, 200);
t.end();
});
});
exports.test_ok_after_window = function(test, assert) {
var opts = common.newOptions(socket, '/test/throttleMe');
opts.method = 'GET';
test('override limited', function(t) {
client.get('/test/special', function(err, req, res, body) {
t.ifError(err);
t.equal(res.statusCode, 200);
t.end();
});
});
http.request(opts, function(res) {
common.checkResponse(assert, res);
assert.equal(res.statusCode, 200);
test.finish();
}).end();
};
test('override limited (not throttled)', function(t) {
client.get('/test/special', function(err, req, res, body) {
t.ifError(err);
t.equal(res.statusCode, 200);
t.end();
});
});
exports.test_override_limited = function(test, assert) {
var opts = common.newOptions(socket, '/test/special');
opts.method = 'GET';
http.request(opts, function(res) {
common.checkResponse(assert, res);
assert.equal(res.statusCode, 200);
http.request(opts, function(res) {
common.checkResponse(assert, res);
assert.equal(res.statusCode, 200);
test.finish();
}).end();
}).end();
};
test('override unlimited', function(t) {
client.get('/test/admin', function(err, req, res, body) {
t.ifError(err);
t.equal(res.statusCode, 200);
t.end();
});
});
exports.test_override_unlimited = function(test, assert) {
var opts = common.newOptions(socket, '/test/admin');
opts.method = 'GET';
test('override unlimited (not throttled)', function(t) {
client.get('/test/admin', function(err, req, res, body) {
t.ifError(err);
t.equal(res.statusCode, 200);
t.end();
});
});
http.request(opts, function(res) {
common.checkResponse(assert, res);
assert.equal(res.statusCode, 200);
http.request(opts, function(res) {
common.checkResponse(assert, res);
assert.equal(res.statusCode, 200);
test.finish();
}).end();
}).end();
};
exports.tearDown = function(test, assert) {
server.on('close', function() {
test.finish();
test('teardown', function(t) {
server.close(function() {
t.end();
});
server.close();
};
});

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc