Skip to content

Commit

Permalink
Merge pull request #880 from spumko/perf
Browse files Browse the repository at this point in the history
Performance and hawk options
  • Loading branch information
geek committed May 23, 2013
2 parents 62ceb44 + fd67995 commit e8ccb01
Show file tree
Hide file tree
Showing 17 changed files with 290 additions and 255 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ infrastructure. The framework supports a powerful plugin architecture for pain-f

For the latest updates and release information follow [@hapijs](https://twitter.com/hapijs) on twitter.

Current version: **1.2.x**
Current version: **1.3.x**

Node version: **0.10** required

Expand Down
6 changes: 3 additions & 3 deletions docs/Reference.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 1.2.x API Reference
# 1.3.x API Reference

- [`Hapi.Server`](#hapiserver)
- [`new Server([host], [port], [options])`](#new-serverhost-port-options)
Expand Down Expand Up @@ -1059,7 +1059,7 @@ authentication. The scheme requires the following options:
- `err` - an internal error.
- `credentials` - a credentials object passed back to the application in `request.auth.credentials`. Return `null` or `undefined` to
indicate unknown credentials (which is not considered an error state).
- `hostHeaderName` - optional name of the HTTP request header used to transmit host information. Defaults to ''host''.
- `hawk` - optional protocol options passed to `Hawk.server.authenticate()`.

```javascript
var Hapi = require('hapi');
Expand Down Expand Up @@ -1096,7 +1096,7 @@ be used with 'GET' requests and requires the following options:
- `err` - an internal error.
- `credentials` - a credentials object passed back to the application in `request.auth.credentials`. Return `null` or `undefined` to
indicate unknown credentials (which is not considered an error state).
- `hostHeaderName` - optional name of the HTTP request header used to transmit host information. Defaults to ''host''.
- `hawk` - optional protocol options passed to `Hawk.server.authenticateBewit()`.

```javascript
var Hapi = require('hapi');
Expand Down
7 changes: 5 additions & 2 deletions lib/auth/bewit.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,18 @@ exports = module.exports = internals.Scheme = function (server, options) {
Utils.assert(server, 'Server is required');

this.settings = Utils.clone(options);
this.settings.hostHeaderName = this.settings.hostHeaderName || 'host';
this.settings.hawk = this.settings.hawk || {};
if (this.settings.hostHeaderName) {
this.settings.hawk.hostHeaderName = this.settings.hostHeaderName; // For backwards compatiblity
}
};


// Hawk Authentication

internals.Scheme.prototype.authenticate = function (request, callback) {

Hawk.uri.authenticate(request.raw.req, this.settings.getCredentialsFunc, { hostHeaderName: this.settings.hostHeaderName }, function (err, credentials, bewit) {
Hawk.server.authenticateBewit(request.raw.req, this.settings.getCredentialsFunc, this.settings.hawk, function (err, credentials, bewit) {

return callback(err, credentials, { artifacts: bewit });
});
Expand Down
7 changes: 5 additions & 2 deletions lib/auth/hawk.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,18 @@ exports = module.exports = internals.Scheme = function (server, options) {
Utils.assert(server, 'Server is required');

this.settings = Utils.clone(options);
this.settings.hostHeaderName = this.settings.hostHeaderName || 'host';
this.settings.hawk = this.settings.hawk || {};
if (this.settings.hostHeaderName) {
this.settings.hawk.hostHeaderName = this.settings.hostHeaderName; // For backwards compatiblity
}
};


// Hawk Authentication

internals.Scheme.prototype.authenticate = function (request, callback) {

Hawk.server.authenticate(request.raw.req, this.settings.getCredentialsFunc, { hostHeaderName: this.settings.hostHeaderName }, function (err, credentials, artifacts) {
Hawk.server.authenticate(request.raw.req, this.settings.getCredentialsFunc, this.settings.hawk, function (err, credentials, artifacts) {

return callback(err, credentials, { artifacts: artifacts });
});
Expand Down
28 changes: 17 additions & 11 deletions lib/payload.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,17 +201,7 @@ internals.parse = function (payload, mime, headers, callback) {
// JSON

if (mime === 'application/json') {
var obj = null;
var error = null;

try {
obj = JSON.parse(payload.toString('utf-8'));
}
catch (exp) {
error = Boom.badRequest('Invalid request payload format');
}

return callback(error, obj);
return internals.jsonParse(payload, callback); // Isolate try...catch for V8 optimization
}

// Form-encoded
Expand Down Expand Up @@ -276,6 +266,22 @@ internals.parse = function (payload, mime, headers, callback) {
};


internals.jsonParse = function (payload, next) {

var obj = null;
var error = null;

try {
obj = JSON.parse(payload.toString('utf-8'));
}
catch (exp) {
error = Boom.badRequest('Invalid request payload format');
}

return next(error, obj);
};


internals.Replay = function (headers, payload) {

Stream.Readable.call(this);
Expand Down
160 changes: 76 additions & 84 deletions lib/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ exports = module.exports = internals.Request = function (server, req, res, optio
Utils.assert(server, 'server must be provided');
Utils.assert(req, 'req must be provided');
Utils.assert(res, 'res must be provided');

options = options || {};
Utils.assert(options, 'options must be provided');

// Public members

Expand Down Expand Up @@ -109,6 +108,14 @@ exports = module.exports = internals.Request = function (server, req, res, optio

this._paramsArray = []; // Array of path parameters in path order

// Set socket timeout

if (req.socket &&
server.settings.timeout.socket !== undefined) {

req.socket.setTimeout(server.settings.timeout.socket || 0);
}

// Log request

var about = {
Expand All @@ -133,14 +140,21 @@ internals.Request.prototype._setUrl = function (url) {

// Uppercase %encoded values

var uppercase = this.path.replace(/%[0-9a-fA-F][0-9a-fA-F]/g, function (encoded) { return encoded.toUpperCase(); });
var uppercase = this.path.replace(/%[0-9a-fA-F][0-9a-fA-F]/g, function (encoded) {

return encoded.toUpperCase();
});

// Decode non-reserved path characters: a-z A-Z 0-9 _!$&'()*+,;=:@-.~
// ! (%21) $ (%24) & (%26) ' (%27) ( (%28) ) (%29) * (%2A) + (%2B) , (%2C) - (%2D) . (%2E)
// 0-9 (%30-39) : (%3A) ; (%3B) = (%3D)
// @ (%40) A-Z (%41-5A) _ (%5F) a-z (%61-7A) ~ (%7E)

var decoded = uppercase.replace(/%(?:2[146-9A-E]|3[\dABD]|4[\dA-F]|5[\dAF]|6[1-9A-F]|7[\dAE])/g, function (encoded) { return String.fromCharCode(parseInt(encoded.substring(1), 16)); });
var decoded = uppercase.replace(/%(?:2[146-9A-E]|3[\dABD]|4[\dA-F]|5[\dAF]|6[1-9A-F]|7[\dAE])/g, function (encoded) {

return String.fromCharCode(parseInt(encoded.substring(1), 16));
});

this.path = decoded;
}
};
Expand Down Expand Up @@ -221,7 +235,7 @@ internals.Request.prototype.getLog = function (tags) {
};


internals.Request.prototype._onRequestExt = function (callback) {
internals.Request.prototype._execute = function () {

var self = this;

Expand All @@ -230,109 +244,95 @@ internals.Request.prototype._onRequestExt = function (callback) {
this.setUrl = this._setUrl;
this.setMethod = this._setMethod;

// Execute onRequest extensions (can change request method and url)

this.server._ext.invoke(this, 'onRequest', function (err) {

// Undecorate request

delete self.setUrl;
delete self.setMethod;

if (!err) {
return callback();
if (err) {
self._route = self.server._router.notfound; // Only settings are used, not the handler
self.route = self._route.settings;
self._reply(err);
return;
}

// Send error response

self._route = self.server._router.notfound; // Only settings are used, not the handler
self.route = self.server._router.notfound.settings;
self._reply(err, function () {

return callback(err);
});
});
};


internals.Request.prototype._execute = function (route) {
// Lookup route

var self = this;
self._route = self.server._router.route(self);
self.route = self._route.settings;

this._route = route;
this.route = route.settings;
var serverTimeout = self.server.settings.timeout.server;
if (serverTimeout) {
serverTimeout -= (Date.now() - self._timestamp); // Calculate the timeout from when the request was constructed
var timeoutReply = function () {

var serverTimeout = self.server.settings.timeout.server;
if (serverTimeout) {
serverTimeout -= (Date.now() - self._timestamp); // Calculate the timeout from when the request was constructed
var timeoutReply = function () {
self._reply(Boom.serverTimeout());
};

self._reply(Boom.serverTimeout());
};
if (serverTimeout <= 0) {
return timeoutReply();
}

if (serverTimeout <= 0) {
return timeoutReply();
self._serverTimeoutId = setTimeout(timeoutReply, serverTimeout);
}

self._serverTimeoutId = setTimeout(timeoutReply, serverTimeout);
}

var ext = function (event) {

return function (request, next) {
var funcs = [
// 'onRequest' above
State.parseCookies,
'onPreAuth',
Auth.authenticate, // Authenticates the raw.req object
Payload.read,
Auth.authenticatePayload,
'onPostAuth',
Validation.path,
internals.queryExtensions,
Validation.query,
Validation.payload,
'onPreHandler',
internals.handler, // Must not call next() with an Error
'onPostHandler', // An error from here on will override any result set in handler()
Validation.response
// 'onPreResponse' in _reply // Always called
];

Async.forEachSeries(funcs, function (func, next) {

if (self._isReplied) {
self.log(['hapi', 'server', 'timeout']);
return next(true); // Argument is ignored but aborts the series
}

self.server._ext.invoke(self, event, next);
};
};
if (typeof func === 'string') {
self.server._ext.invoke(self, func, next);
return;
}

var funcs = [
// 'onRequest' in Server
State.parseCookies,
ext('onPreAuth'),
Auth.authenticate, // Authenticates the raw.req object
Payload.read,
Auth.authenticatePayload,
ext('onPostAuth'),
Validation.path,
internals.queryExtensions,
Validation.query,
Validation.payload,
ext('onPreHandler'),
internals.handler, // Must not call next() with an Error
ext('onPostHandler'), // An error from here on will override any result set in handler()
Validation.response
// 'onPreResponse' in _reply // Always called
];

Async.forEachSeries(funcs, function (func, next) {

if (self._isReplied) {
self.log(['hapi', 'server', 'timeout']);
return next(true); // Argument is ignored but aborts the series
}
func(self, next);

func(self, next);
},
function (err) {
},
function (err) {

self._reply(err);
self._reply(err);
});
});
};


internals.Request.prototype._reply = function (exit, callback) {
internals.Request.prototype._reply = function (exit) {

var self = this;

if (this._isReplied) { // Prevent any future responses to this request
if (callback) {
Utils.nextTick(callback)();
}

return;
}

this._isReplied = true;

clearTimeout(self._serverTimeoutId);
clearTimeout(this._serverTimeoutId);

var process = function () {

Expand Down Expand Up @@ -401,10 +401,6 @@ internals.Request.prototype._reply = function (exit, callback) {

self.raw.req.removeAllListeners();
self.raw.res.removeAllListeners();

if (callback) {
callback();
}
};

process();
Expand Down Expand Up @@ -440,14 +436,12 @@ internals.Request.prototype._replyInterface = function (next, withProperties) {
var response = null;
var wasProcessed = false;

// Chain finalizers

var process = function () {

if (wasProcessed) {
return;
}

wasProcessed = true;
return next(response);
};
Expand All @@ -472,8 +466,6 @@ internals.Request.prototype._replyInterface = function (next, withProperties) {
process();
};

// Chain initializers

reply.redirect = function (uri) {

delete self.reply;
Expand Down
1 change: 0 additions & 1 deletion lib/response/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ exports._generate = function (result, onSend) {
delete response.hold;

if (!response.send) {
delete response.hold;
onSend();
}
});
Expand Down
Loading

0 comments on commit e8ccb01

Please sign in to comment.