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

hapi

Package Overview
Dependencies
Maintainers
2
Versions
295
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

hapi - npm Package Compare versions

Comparing version 0.8.0 to 0.8.1

24

lib/error.js

@@ -49,19 +49,17 @@ // Load modules

exports._oauth = function (code, description) {
exports.format = function (error) {
return exports.create('OAuth', 400, description, { type: 'oauth', error: code });
};
if (error.hasOwnProperty('toResponse') &&
typeof error.toResponse === 'function') {
return error.toResponse();
}
exports.format = function (error) {
var err = {
error: error.text,
code: error.code,
message: (error.code >= 500 && error.code < 600 ? 'An internal server error occurred' : error.message)
};
if (error.type === 'oauth') {
return { error: error.error, error_description: error.text };
}
else if (error.code >= 500 && error.code < 600) {
return { error: error.text, message: 'An internal server error occurred', code: error.code };
}
else {
return { error: error.text, message: error.message, code: error.code };
}
return err;
};

@@ -68,0 +66,0 @@

@@ -104,3 +104,3 @@ // Load modules

if (isBailed) {
return; // next() already called
return; // next() already called
}

@@ -114,28 +114,27 @@

if (level === 'parse') {
request.payload = {};
if (level !== 'parse') { // 'raw'
return next();
}
unwrapper(payload, encoding, request, function (err, result) {
request.payload = {};
if (err) {
return next(Err.badRequest("Invalid gzip: " + err));
}
unwrapper(payload, encoding, request, function (err, result) {
if (result) {
try {
request.payload = parserFunc(result);
}
catch (err) {
return next(Err.badRequest('Invalid JSON body'));
}
if (err) {
return next(Err.badRequest("Invalid gzip: " + err));
}
if (result) {
try {
request.payload = parserFunc(result);
}
catch (err) {
return next(Err.badRequest('Invalid JSON body'));
}
}
return next();
})
}
else {
return next();
}
})
});
};

@@ -20,6 +20,9 @@ // Load modules

Utils.assert(options, 'Missing options');
Utils.assert(options.host, 'Missing destination host option');
Utils.assert(!options.passThrough || !route.cache.isMode('server'), 'Cannot use pass-through proxy mode with caching')
Utils.assert(options.host || options.mapUri, 'Missing options.host and no options.mapUri');
Utils.assert(!options.passThrough || !route.cache.isMode('server'), 'Cannot use pass-through proxy mode with caching');
Utils.assert(!options.mapUri || typeof options.mapUri === 'function', 'options.mapUri must be a function');
Utils.assert(!options.postResponse || typeof options.postResponse === 'function', 'options.postResponse must be a function');
Utils.assert(!options.hasOwnProperty('isCustomPostResponse'), 'Cannot manually set options.isCustomPostResponse');
this.settings = Utils.clone(options); // Options can be reused
this.settings = Utils.clone(options); // Options can be reused
this.settings.protocol = this.settings.protocol || 'http';

@@ -29,2 +32,7 @@ this.settings.port = this.settings.port || (this.settings.protocol === 'http' ? 80 : 443);

this.settings.passHeaders = this.settings.passThrough || false;
this.settings.mapUri = this.settings.mapUri || internals.mapUri; // function (request, settings, function (err, uri, query))
this.settings.isCustomPostResponse = !!this.settings.postResponse;
this.settings.postResponse = this.settings.postResponse || internals.postResponse; // function (request, settings, response, payload)
return this;
};

@@ -39,87 +47,94 @@

var req = request.raw.req;
self.settings.mapUri(request, self.settings, function (err, uri, query) {
var options = {
url: self.settings.protocol + '://' + self.settings.host + ':' + self.settings.port + request.path,
method: request.method,
qs: request.query,
headers: {}
};
if (err) {
return request.reply(err);
}
if (self.settings.passThrough) {
options.headers = Utils.clone(req.headers);
delete options.headers.host;
}
var req = request.raw.req;
if (self.settings.xforward) {
options.headers['x-forwarded-for'] = (options.headers['x-forwarded-for'] ? options.headers['x-forwarded-for'] + ',' : '') + req.connection.remoteAddress || req.socket.remoteAddress;
options.headers['x-forwarded-port'] = (options.headers['x-forwarded-port'] ? options.headers['x-forwarded-port'] + ',' : '') + req.connection.remotePort || req.socket.remotePort;
options.headers['x-forwarded-proto'] = (options.headers['x-forwarded-proto'] ? options.headers['x-forwarded-proto'] + ',' : '') + self.settings.protocol;
}
var options = {
url: uri,
method: request.method,
qs: query,
headers: {}
};
if (request.method === 'get' ||
request.method === 'head') {
if (self.settings.passThrough) { // Never set with cache
options.headers = Utils.clone(req.headers);
delete options.headers.host;
}
// No caching (pipe)
if (!request._route ||
!request._route.cache.isMode('server')) {
if (self.settings.xforward) {
options.headers['x-forwarded-for'] = (options.headers['x-forwarded-for'] ? options.headers['x-forwarded-for'] + ',' : '') + req.connection.remoteAddress || req.socket.remoteAddress;
options.headers['x-forwarded-port'] = (options.headers['x-forwarded-port'] ? options.headers['x-forwarded-port'] + ',' : '') + req.connection.remotePort || req.socket.remotePort;
options.headers['x-forwarded-proto'] = (options.headers['x-forwarded-proto'] ? options.headers['x-forwarded-proto'] + ',' : '') + self.settings.protocol;
}
var reqStream = Request(options);
var isGet = (request.method === 'get' || request.method === 'head');
reqStream.on('response', function(resStream) {
request.reply.stream(resStream);
if (self.settings.isCustomPostResponse || // Custom response method
(isGet && request._route && request._route.cache.isMode('server'))) { // GET/HEAD with Cache
// Callback interface
Request(options, function (err, response, payload) {
// Request handles all redirect responses (3xx) and will return an err if redirection fails
if (err) {
return request.reply(Err.internal('Proxy error', err));
}
return self.settings.postResponse(request, self.settings, response, payload);
});
return;
}
else {
// Cache (parse response)
Request(options, function (err, response, payload) {
// Stream interface
if (err) {
return request.reply(Err.internal('Proxy error', err));
}
if (!isGet &&
request.rawBody) {
if (response.statusCode >= 400) {
return request.reply(Err.internal('Error proxy response', payload));
options.headers['Content-Type'] = req.headers['content-type'];
options.body = request.rawBody;
}
if (response.headers['content-type']) {
request.reply.type(response.headers['content-type']);
}
var reqStream = Request(options);
var headerKeys = Object.keys(response.headers);
for (var i = 0, il = headerKeys.length; i < il; ++i) {
var headerKey = headerKeys[i];
if (headerKey !== 'content-length') {
request.reply.header(headerKey, response.headers[headerKey]);
}
if (!isGet &&
request._route &&
request._route.config.payload === 'stream') {
request.raw.req.pipe(reqStream);
}
return request.reply(payload);
});
}
else {
if (request.rawBody) {
options.headers['Content-Type'] = req.headers['content-type'];
options.body = request.rawBody;
reqStream.on('response', function (resStream) {
request.reply.stream(resStream); // Request._respond will pass-through headers and status code
});
}
});
};
};
var reqStream = Request(options);
if (request._route &&
request._route.config.payload === 'stream') {
internals.mapUri = function (request, settings, callback) {
request.raw.req.pipe(reqStream);
}
return callback(null, settings.protocol + '://' + settings.host + ':' + settings.port + request.path, request.query);
};
reqStream.on('response', function(resStream) {
request.reply.stream(resStream); // Request._respond will pass-through headers and status code
});
}
};
internals.postResponse = function (request, settings, response, payload) {
if (response.statusCode >= 400) {
return request.reply(Err.internal('Error proxy response', { code: response.statusCode, payload: payload }));
}
if (response.headers['content-type']) {
request.reply.type(response.headers['content-type']);
}
return request.reply(payload);
};

@@ -299,2 +299,3 @@ // Load modules

Utils.assert(stream instanceof Stream, 'request.reply.stream() requires a stream');
Utils.assert(!self._route || !self._route.cache.isMode('server'), 'Cannot reply using a stream when caching enabled');
process(stream);

@@ -332,3 +333,3 @@ };

this.reply.header = function(name, value) {
this.reply.header = function (name, value) {

@@ -434,3 +435,3 @@ response.options.headers = response.options.headers || {};

var self = this;
return function (callback) {

@@ -552,3 +553,3 @@

internals.Request.prototype._setCache = function(headers) {
internals.Request.prototype._setCache = function (headers) {

@@ -704,7 +705,7 @@ if (this._route &&

// Check if data is a node HTTP response (data.*) or a (mikeal's) Request object (data.response.*)
if (!self._route ||
!self._route.config.proxy ||
self._route.config.proxy.passThrough) { // Pass headers only if not proxy or proxy with pass-through set
var responseHeaders = data.response ? data.response.headers : data.headers;

@@ -720,3 +721,3 @@ if (responseHeaders) {

self.raw.req.on('close', function() {
self.raw.req.on('close', function () {

@@ -723,0 +724,0 @@ data.destroy.bind(data);

// Load modules
var Fs = require('fs');
var Http = require('http');

@@ -95,8 +94,3 @@ var Https = require('https');

if (this.settings.tls) {
var tls = {
key: Fs.readFileSync(this.settings.tls.key),
cert: Fs.readFileSync(this.settings.tls.cert)
};
this.listener = Https.createServer(tls, this._dispatch());
this.listener = Https.createServer(this.settings.tls, this._dispatch());
}

@@ -103,0 +97,0 @@ else {

@@ -184,11 +184,11 @@ // Load modules

else {
request.reply(Err._oauth('invalid_grant', 'Mismatching refresh token client id'));
request.reply(internals.error('invalid_grant', 'Mismatching refresh token client id'));
}
}
else {
request.reply(Err._oauth('invalid_grant', 'Invalid refresh token'));
request.reply(internals.error('invalid_grant', 'Invalid refresh token'));
}
}
else {
request.reply(Err._oauth('invalid_request', 'Missing refresh_token'));
request.reply(internals.error('invalid_request', 'Missing refresh_token'));
}

@@ -210,3 +210,3 @@ }

// Unsupported grant type
request.reply(Err._oauth('unsupported_grant_type', 'Unknown or unsupported grant type'));
request.reply(internals.error('unsupported_grant_type', 'Unknown or unsupported grant type'));
}

@@ -216,3 +216,3 @@ }

// Bad client authentication
request.reply(Err._oauth('invalid_client', 'Invalid client identifier or secret'));
request.reply(internals.error('invalid_client', 'Invalid client identifier or secret'));
}

@@ -222,3 +222,3 @@ }

// Unknown client
request.reply(Err._oauth('invalid_client', 'Invalid client identifier or secret'));
request.reply(internals.error('invalid_client', 'Invalid client identifier or secret'));
}

@@ -392,3 +392,3 @@ });

return Err._oauth('invalid_request', 'Request cannot include both Basic and payload client authentication');
return internals.error('invalid_request', 'Request cannot include both Basic and payload client authentication');
}

@@ -406,3 +406,3 @@

else {
return Err._oauth('invalid_request', 'Unsupported HTTP authentication scheme');
return internals.error('invalid_request', 'Unsupported HTTP authentication scheme');
}

@@ -417,3 +417,3 @@ }

else {
return Err._oauth('invalid_request', 'Request missing client authentication');
return internals.error('invalid_request', 'Request missing client authentication');
}

@@ -485,1 +485,20 @@ }

internals.error = function (code, description) {
var err = new Error();
err.message = 'OAuth';
err.code = 400;
err.text = description;
err.type = 'oauth';
err.error = code;
err.toResponse = function () {
return { error: code, error_description: description };
};
return err;
};

@@ -5,3 +5,3 @@ {

"homepage": "http://hapijs.com",
"version": "0.8.0",
"version": "0.8.1",
"author": "Eran Hammer <eran@hueniverse.com> (http://hueniverse.com)",

@@ -8,0 +8,0 @@ "contributors":[

@@ -394,3 +394,3 @@ ![hapi Logo](https://raw.github.com/walmartlabs/hapi/master/images/hapi.png)

### Configuration options
### Configuration options

@@ -404,2 +404,3 @@ * `path` - the absolute path or regular expression to match against incoming requests. Path comparison is configured using the server [`router`](#router) option. String paths can include named identifiers prefixed with _':'_ as described in [Path Parameters](#path-processing).

* `tags` - route tags (array of strings).
* `handler` - an alternative location for the route handler function. Same as the `handler` option in the parent level. Can only include one handler per route.
* `query` - validation rules for incoming requests' query component (the key-value part of the URI between _?_ and _#_). Defaults to no query parameters allowed. See [Query Validation](#query-validation) for more information.

@@ -426,2 +427,31 @@ * `schema` - validation rules for incoming requests' payload (request body). Defaults to no validation (any payload allowed). Set to an empty object _'{}'_ to forbid payloads. See [Payload Validation](#payload-validation) for more information.

The `config` option was defined for easily spliting the routing table definition from the individual route information. For example:
```javascript
var Hapi = require('hapi');
var server = new Hapi.Server();
// Option 1 - add handler directly in route definition
var handler1 = function (request) {
request.reply('ok');
}
server.addRoute({ method: 'GET', path: '/option1', handler: handler1 });
// Option 2 - add handler in seprate config object
var config2 = {
payload: 'raw',
// ... additional config options ...
handler: function (request) {
request.reply('ok');
}
};
server.addRoute({ method: 'GET', path: '/option2', config: config2});
```
### Override Route Defaults

@@ -471,2 +501,3 @@

- _'path'_ - the request URI's path component.
- _'method'_ - the request method as a _lowercase_ string. (Examples: `'get'`, `'post'`).
- _'query'_ - an object containing the query parameters.

@@ -473,0 +504,0 @@ - _'params'_ - an object containing the path named parameters as described in [Path Parameters](#parameters).

// Load modules
var Stream = require('stream');
var expect = require('chai').expect;

@@ -27,4 +28,9 @@ var Sinon = require('sinon');

var badHandler = function (request) {
request.reply.stream(new Stream);
};
function setupServer(done) {
_server = new Hapi.Server('0.0.0.0', 18085);
_server = new Hapi.Server('0.0.0.0', 18085, { cache: 'memory' });
_server.addRoutes([

@@ -34,3 +40,4 @@ { method: 'GET', path: '/profile', config: { handler: profileHandler, cache: { mode: 'client', expiresIn: 120000 } } },

{ method: 'GET', path: '/item2', config: { handler: activeItemHandler, cache: { mode: 'none' } } },
{ method: 'GET', path: '/item3', config: { handler: activeItemHandler, cache: { mode: 'client', expiresIn: 120000 } } }
{ method: 'GET', path: '/item3', config: { handler: activeItemHandler, cache: { mode: 'client', expiresIn: 120000 } } },
{ method: 'GET', path: '/bad', config: { handler: badHandler, cache: { expiresIn: 120000 } } }
]);

@@ -92,2 +99,12 @@ _server.listener.on('listening', function() {

});
it('throws error when returning a stream in a cached endpoint handler', function (done) {
function test() {
makeRequest('/bad', function (rawRes) {});
}
expect(test).to.throw(Error);
done();
});
});

@@ -7,124 +7,119 @@ // Load modules

require('../suite')(function (useRedis, useMongo) {
describe('Proxy', function() {
describe('Proxy', function () {
before(startServer);
before(startServer);
var _server = null;
var _serverUrl = 'http://127.0.0.1:18092';
var _server = null;
var _serverUrl = 'http://127.0.0.1:18092';
function startServer(done) {
function startServer(done) {
var listening = false;
var routeCache = null;
var config = null;
var listening = false;
var config = null;
if (useRedis) {
routeCache = {
mode: 'server',
expiresIn: 500
};
var routeCache = {
mode: 'server',
expiresIn: 500
};
config = {
cache: {
engine: 'redis',
host: '127.0.0.1',
port: 6379
}
};
config = {
cache: {
engine: 'memory',
host: '127.0.0.1',
port: 6379
}
};
var dummyServer = new Hapi.Server('0.0.0.0', 18093);
dummyServer.addRoutes([{ method: 'GET', path: '/profile', config: { handler: profile } },
{ method: 'GET', path: '/item', config: { handler: activeItem } },
{ method: 'POST', path: '/item', config: { handler: item } }]);
var dummyServer = new Hapi.Server('0.0.0.0', 18093);
dummyServer.addRoutes([{ method: 'GET', path: '/profile', config: { handler: profile } },
{ method: 'GET', path: '/item', config: { handler: activeItem } },
{ method: 'POST', path: '/item', config: { handler: item } }]);
_server = new Hapi.Server('0.0.0.0', 18092, config);
_server.addRoutes([
{ method: 'GET', path: '/profile', config: { proxy: { host: '127.0.0.1', port: 18093, xforward: true, passThrough: true } } },
{ method: 'GET', path: '/item', config: { proxy: { host: '127.0.0.1', port: 18093 }, cache: routeCache } },
{ method: 'POST', path: '/item', config: { proxy: { host: '127.0.0.1', port: 18093 } } }
]);
_server = new Hapi.Server('0.0.0.0', 18092, config);
_server.addRoutes([
{ method: 'GET', path: '/profile', config: { proxy: { host: '127.0.0.1', port: 18093, xforward: true, passThrough: true } } },
{ method: 'GET', path: '/item', config: { proxy: { host: '127.0.0.1', port: 18093 }, cache: routeCache } },
{ method: 'POST', path: '/item', config: { proxy: { host: '127.0.0.1', port: 18093 } } }
]);
dummyServer.listener.on('listening', function() {
if (listening) {
done();
}
else {
listening = true;
}
});
_server.listener.on('listening', function() {
if (listening) {
done();
}
else {
listening = true;
}
});
dummyServer.listener.on('listening', function () {
if (listening) {
done();
}
else {
listening = true;
}
});
_server.listener.on('listening', function () {
if (listening) {
done();
}
else {
listening = true;
}
});
dummyServer.start();
_server.start();
}
dummyServer.start();
_server.start();
}
function profile(request) {
request.reply({
'id': 'fa0dbda9b1b',
'name': 'John Doe'
});
}
function profile(request) {
request.reply({
'id': 'fa0dbda9b1b',
'name': 'John Doe'
});
}
function activeItem(request) {
request.reply({
'id': '55cf687663',
'name': 'Active Item'
});
}
function activeItem(request) {
request.reply({
'id': '55cf687663',
'name': 'Active Item'
});
}
function item(request) {
request.reply.created('http://google.com')({
'id': '55cf687663',
'name': 'Item'
});
}
function item(request) {
request.reply.created('http://google.com')({
'id': '55cf687663',
'name': 'Item'
});
}
function makeRequest(options, callback) {
var next = function(err, res) {
return callback(res);
};
function makeRequest(options, callback) {
var next = function (err, res) {
return callback(res);
};
options = options || {};
options.path = options.path || '/';
options.method = options.method || 'get';
options = options || {};
options.path = options.path || '/';
options.method = options.method || 'get';
Request({
method: options.method,
url: _serverUrl + options.path
}, next);
}
Request({
method: options.method,
url: _serverUrl + options.path
}, next);
}
it('forwards on the response when making a GET request', function(done) {
makeRequest({ path: '/profile' }, function(rawRes) {
expect(rawRes.statusCode).to.equal(200);
expect(rawRes.body).to.contain('John Doe');
done();
});
it('forwards on the response when making a GET request', function (done) {
makeRequest({ path: '/profile' }, function (rawRes) {
expect(rawRes.statusCode).to.equal(200);
expect(rawRes.body).to.contain('John Doe');
done();
});
});
it('forwards on the response when making a GET request to a route that also accepts a POST', function(done) {
makeRequest({ path: '/item' }, function(rawRes) {
expect(rawRes.statusCode).to.equal(200);
expect(rawRes.body).to.contain('Active Item');
done();
});
it('forwards on the response when making a GET request to a route that also accepts a POST', function (done) {
makeRequest({ path: '/item' }, function (rawRes) {
expect(rawRes.statusCode).to.equal(200);
expect(rawRes.body).to.contain('Active Item');
done();
});
});
it('forwards on the status code when making a POST request', function(done) {
makeRequest({ path: '/item', method: 'post' }, function(rawRes) {
expect(rawRes.statusCode).to.equal(201);
expect(rawRes.body).to.contain('Item');
done();
});
it('forwards on the status code when making a POST request', function (done) {
makeRequest({ path: '/item', method: 'post' }, function (rawRes) {
expect(rawRes.statusCode).to.equal(201);
expect(rawRes.body).to.contain('Item');
done();
});
});
});
var expect = require('chai').expect;
var Error = process.env.TEST_COV ? require('../../lib-cov/error') : require('../../lib/error');
var Err = process.env.TEST_COV ? require('../../lib-cov/error') : require('../../lib/error');
describe('Error', function() {
describe('Err', function() {

@@ -9,3 +9,3 @@ describe('#badRequest', function() {

it('returns a 400 error code', function(done) {
expect(Error.badRequest().code).to.equal(400);
expect(Err.badRequest().code).to.equal(400);
done();

@@ -15,3 +15,3 @@ });

it('sets the message with the passed in message', function(done) {
expect(Error.badRequest('my message').message).to.equal('my message');
expect(Err.badRequest('my message').message).to.equal('my message');
done();

@@ -24,3 +24,3 @@ });

it('returns a 401 error code', function(done) {
expect(Error.unauthorized().code).to.equal(401);
expect(Err.unauthorized().code).to.equal(401);
done();

@@ -30,3 +30,3 @@ });

it('sets the message with the passed in message', function(done) {
expect(Error.unauthorized('my message').message).to.equal('my message');
expect(Err.unauthorized('my message').message).to.equal('my message');
done();

@@ -39,3 +39,3 @@ });

it('returns a 403 error code', function(done) {
expect(Error.forbidden().code).to.equal(403);
expect(Err.forbidden().code).to.equal(403);
done();

@@ -45,3 +45,3 @@ });

it('sets the message with the passed in message', function(done) {
expect(Error.forbidden('my message').message).to.equal('my message');
expect(Err.forbidden('my message').message).to.equal('my message');
done();

@@ -54,3 +54,3 @@ });

it('returns a 404 error code', function(done) {
expect(Error.notFound().code).to.equal(404);
expect(Err.notFound().code).to.equal(404);
done();

@@ -60,3 +60,3 @@ });

it('sets the message with the passed in message', function(done) {
expect(Error.notFound('my message').message).to.equal('my message');
expect(Err.notFound('my message').message).to.equal('my message');
done();

@@ -69,3 +69,3 @@ });

it('returns a 500 error code', function(done) {
expect(Error.internal().code).to.equal(500);
expect(Err.internal().code).to.equal(500);
done();

@@ -75,3 +75,3 @@ });

it('sets the message with the passed in message', function(done) {
expect(Error.internal('my message').message).to.equal('my message');
expect(Err.internal('my message').message).to.equal('my message');
done();

@@ -81,3 +81,3 @@ });

it('passes data on the callback if its passed in', function(done) {
expect(Error.internal('my message', { my: 'data' }).data.my).to.equal('data');
expect(Err.internal('my message', { my: 'data' }).data.my).to.equal('data');
done();

@@ -87,24 +87,13 @@ });

describe('#_oauth', function() {
describe('#format', function() {
it('returns a 400 error code', function(done) {
expect(Error._oauth('mycode', 'mymessage').code).to.equal(400);
done();
});
it('formats a custom error', function (done) {
it('sets the passed in text', function(done) {
expect(Error._oauth('mycode', 'mymessage').text).to.equal('mymessage');
done();
});
var err = new Error();
err.toResponse = function () {
it('sets the passed in code', function(done) {
expect(Error._oauth('mycode', 'mymessage').error).to.equal('mycode');
done();
});
});
return { test: true };
};
describe('#format', function() {
it('formats oauth errors separately', function(done) {
expect(Error.format({ type: 'oauth', text: 'myerror' }).error_description).to.equal('myerror');
expect(Err.format(err).test).to.equal(true);
done();

@@ -114,3 +103,3 @@ });

it('formats internal errors with a standard message', function(done) {
expect(Error.format({ code: 500 }).message).to.equal('An internal server error occurred');
expect(Err.format({ code: 500 }).message).to.equal('An internal server error occurred');
done();

@@ -117,0 +106,0 @@ });

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