diff --git a/README.md b/README.md index 8790c78e5..8be5cfaaa 100755 --- a/README.md +++ b/README.md @@ -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 diff --git a/docs/Reference.md b/docs/Reference.md index 1c3665a89..2db78c690 100755 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -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) @@ -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'); @@ -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'); diff --git a/lib/auth/bewit.js b/lib/auth/bewit.js index 3acfd0bfa..7728440e1 100755 --- a/lib/auth/bewit.js +++ b/lib/auth/bewit.js @@ -19,7 +19,10 @@ 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 + } }; @@ -27,7 +30,7 @@ exports = module.exports = internals.Scheme = function (server, options) { 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 }); }); diff --git a/lib/auth/hawk.js b/lib/auth/hawk.js index 4c49af533..5fb2a8544 100755 --- a/lib/auth/hawk.js +++ b/lib/auth/hawk.js @@ -19,7 +19,10 @@ 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 + } }; @@ -27,7 +30,7 @@ exports = module.exports = internals.Scheme = function (server, options) { 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 }); }); diff --git a/lib/payload.js b/lib/payload.js index 543c1e03a..b5fdd6cf9 100755 --- a/lib/payload.js +++ b/lib/payload.js @@ -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 @@ -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); diff --git a/lib/request.js b/lib/request.js index 7418716cc..5e26d1472 100755 --- a/lib/request.js +++ b/lib/request.js @@ -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 @@ -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 = { @@ -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; } }; @@ -221,7 +235,7 @@ internals.Request.prototype.getLog = function (tags) { }; -internals.Request.prototype._onRequestExt = function (callback) { +internals.Request.prototype._execute = function () { var self = this; @@ -230,6 +244,8 @@ 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 @@ -237,102 +253,86 @@ internals.Request.prototype._onRequestExt = function (callback) { 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 () { @@ -401,10 +401,6 @@ internals.Request.prototype._reply = function (exit, callback) { self.raw.req.removeAllListeners(); self.raw.res.removeAllListeners(); - - if (callback) { - callback(); - } }; process(); @@ -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); }; @@ -472,8 +466,6 @@ internals.Request.prototype._replyInterface = function (next, withProperties) { process(); }; - // Chain initializers - reply.redirect = function (uri) { delete self.reply; diff --git a/lib/response/index.js b/lib/response/index.js index 2faeb4795..fb7b42831 100755 --- a/lib/response/index.js +++ b/lib/response/index.js @@ -109,7 +109,6 @@ exports._generate = function (result, onSend) { delete response.hold; if (!response.send) { - delete response.hold; onSend(); } }); diff --git a/lib/route.js b/lib/route.js index e2cbd7429..a7047ea5b 100755 --- a/lib/route.js +++ b/lib/route.js @@ -40,7 +40,7 @@ exports = module.exports = internals.Route = function (options, server, env) { this.env = env || {}; // Plugin-specific environment this.method = options.method.toLowerCase(); Utils.assert(this.method !== 'head', 'Cannot add HEAD route'); - this.path = options.path; + this.path = (this.server.settings.router.isCaseSensitive ? options.path : options.path.toLowerCase()); this.settings.method = this.method; // Expose method in settings Utils.assert(this.path.match(internals.Route.validatePathRegex), 'Invalid path:', this.path); @@ -68,7 +68,7 @@ exports = module.exports = internals.Route = function (options, server, env) { // Parse path - this._generateRegex(); // Sets this.regexp, this.params, this.fingerprint + this._parsePath(); // Sets this.params, this.fingerprint, this._segments // Cache @@ -199,16 +199,17 @@ internals.Route.validatePathRegex = /(^\/$)|(^(\/(([\w\!\$&'\(\)\*\+\,;\=\:@\-\. internals.Route.validatePathEncodedRegex = /%(?:2[146-9A-E]|3[\dABD]|4[\dA-F]|5[\dAF]|6[1-9A-F]|7[\dAE])/g; -internals.Route.prototype._generateRegex = function () { +internals.Route.prototype._parsePath = function () { + + this._segments = []; // Split on / var segments = this.path.split('/'); var params = {}; - var pathRX = ''; var fingers = []; - var paramRegex = /^\{(\w+)(?:(\*)(\d+)?)?(\?)?\}$/; // $1: name, $2: *, $3: segments, $4: optional + var paramRegex = /^\{(\w+)(?:(\*)(\d+)?)?(\?)?\}$/; // $1: name, $2: *, $3: segments, $4: empty-ok for (var i = 1, il = segments.length; i < il; ++i) { // Skip first empty segment var segment = segments[i]; @@ -220,45 +221,26 @@ internals.Route.prototype._generateRegex = function () { var name = param[1]; var isMulti = !!param[2]; var multiCount = param[3] && parseInt(param[3], 10); - var isOptional = !!param[4]; + var isEmptyOk = !!param[4]; Utils.assert(!params[name], 'Cannot repeat the same parameter name'); params[name] = true; - var segmentRX = '\\/'; - if (isMulti) { - if (multiCount) { - segmentRX += '((?:[^\\/]+)(?:\\/(?:[^\\/]+)){' + (multiCount - 1) + '})'; - } - else { - segmentRX += '(.*)'; - } - } - else { - segmentRX += '([^\\/]+)'; - } - - if (isOptional || - (isMulti && !multiCount)) { - - pathRX += '(?:(?:\\/)|(?:' + segmentRX + '))'; - } - else { - pathRX += segmentRX; - } - if (isMulti) { if (multiCount) { for (var m = 0; m < multiCount; ++m) { - fingers.push('/?'); + fingers.push('?'); + this._segments.push({ name: name, count: multiCount }); } } else { - fingers.push('/*'); + fingers.push('#'); + this._segments.push({ isWildcard: true, name: name }); } } else { - fingers.push('/?'); + fingers.push('?'); + this._segments.push({ name: name, isEmptyOk: isEmptyOk }); } } else { @@ -266,69 +248,119 @@ internals.Route.prototype._generateRegex = function () { // Literal if (segment) { - pathRX += '\\/' + Utils.escapeRegex(segment); - if (this.server.settings.router.isCaseSensitive) { - fingers.push('/' + segment); - } - else { - fingers.push('/' + segment.toLowerCase()); - } + fingers.push(segment); + this._segments.push({ literal: segment }); } else { - pathRX += '\\/'; - fingers.push('/'); + fingers.push(''); + this._segments.push({ literal: '' }); } } } - if (this.server.settings.router.isCaseSensitive) { - this.regexp = new RegExp('^' + pathRX + '$'); + this.fingerprint = '/' + fingers.join('/'); + this.params = Object.keys(params); +}; + + +internals.Route.prototype.match = function (request) { + + var params = {}; + var paramsArray = []; + + var match = this._test(request.path, params, paramsArray); + if (match) { + request.params = params; + request._paramsArray = paramsArray; } - else { - this.regexp = new RegExp('^' + pathRX + '$', 'i'); + + return !!match; +}; + + +internals.setParam = function (name, value, params, paramsArray, isEmptyOk) { + + var isValid = (isEmptyOk || value); + + if (isValid && + value && + params) { + + var decoded = internals.decodeURIComponent(value); + if (decoded === null) { + isValid = false; + } + else { + params[name] = decoded; + paramsArray.push(decoded); + } } - this.fingerprint = fingers.join(''); - this._fingerprintParts = fingers; - this.params = Object.keys(params); + return isValid; }; -internals.Route.prototype.match = function (request) { +internals.decodeURIComponent = function (value) { + + try { + return decodeURIComponent(value); + } + catch (err) { + return null; + } +}; - var match = this.regexp.exec(request.path); - if (!match) { - return false; +internals.Route.prototype._test = function (path, params, paramsArray) { + + var match = true; + + if (!this.params.length) { + + // Literal path + + match = (this.path === (this.server.settings.router.isCaseSensitive ? path : path.toLowerCase())); } + else { + + // Parameterized path + + var paths = path.split('/'); + var pl = paths.length - 1; + var sl = this._segments.length; - request.params = {}; - request._paramsArray = []; + if (pl === sl || + pl >= sl && this._segments[sl - 1].isWildcard) { - if (this.params.length > 0) { - for (var i = 1, il = match.length; i < il; ++i) { - var key = this.params[i - 1]; - if (key) { - try { - request.params[key] = (typeof match[i] === 'string' ? decodeURIComponent(match[i]) : match[i]); - request._paramsArray.push(request.params[key]); + for (var i = 0; match && i < sl; ++i) { + var segment = this._segments[i]; + if (segment.isWildcard) { + match = internals.setParam(segment.name, paths.slice(i + 1).join('/'), params, paramsArray, true); } - catch (err) { - // decodeURIComponent can throw - return false; + else if (segment.count) { + match = internals.setParam(segment.name, paths.slice(i + 1, i + 1 + segment.count).join('/'), params, paramsArray); + i += (segment.count - 1); + } + else if (segment.name) { + match = internals.setParam(segment.name, paths[i + 1], params, paramsArray, segment.isEmptyOk); + } + else { + match = (segment.literal === (this.server.settings.router.isCaseSensitive ? paths[i + 1] : paths[i + 1].toLowerCase())); } } } + else { + match = false; + } } - return true; + return match; }; internals.Route.prototype.test = function (path) { - var match = this.regexp.exec(path); - return !!match; + return this._test(path, null, null); }; @@ -341,16 +373,16 @@ exports.sort = function (a, b) { // Prepare fingerprints - var aFingers = a._fingerprintParts; - var bFingers = b._fingerprintParts; + var aFingers = a._segments; + var bFingers = b._segments; var al = aFingers.length; var bl = bFingers.length; - // Comare fingerprints + // Compare fingerprints - if ((aFingers[al - 1] === '/*') ^ (bFingers[bl - 1] === '/*')) { - return (aFingers[al - 1] === '/*' ? bFirst : aFirst); + if ((aFingers[al - 1].isWildcard) ^ (bFingers[bl - 1].isWildcard)) { + return (aFingers[al - 1].isWildcard ? bFirst : aFirst); } var size = Math.min(al, bl); @@ -359,20 +391,23 @@ exports.sort = function (a, b) { var aSegment = aFingers[i]; var bSegment = bFingers[i]; - if (aSegment === bSegment) { + if ((aSegment.isWildcard && bSegment.isWildcard) || + (aSegment.name && bSegment.name) || + (aSegment.literal == bSegment.literal)) { + continue; } - if (aSegment === '/*' || - bSegment === '/*') { + if (aSegment.isWildcard || + bSegment.isWildcard) { - return (aSegment === '/*' ? bFirst : aFirst); + return (aSegment.isWildcard ? bFirst : aFirst); } - if (aSegment === '/?' || - bSegment === '/?') { + if (aSegment.name || + bSegment.name) { - if (aSegment === '/?') { + if (aSegment.name) { return (al >= bl ? bFirst : aFirst); } else { @@ -381,11 +416,11 @@ exports.sort = function (a, b) { } if (al === bl) { - if (aSegment.length === bSegment.length) { - return (aSegment > bSegment ? bFirst : aFirst); + if (aSegment.literal.length === bSegment.literal.length) { + return (aSegment.literal > bSegment.literal ? bFirst : aFirst); } - return (aSegment.length > bSegment.length ? bFirst : aFirst); + return (aSegment.literal.length > bSegment.literal.length ? bFirst : aFirst); } return (al > bl ? bFirst : aFirst); diff --git a/lib/router.js b/lib/router.js index c7980d9c3..a647817c3 100755 --- a/lib/router.js +++ b/lib/router.js @@ -13,7 +13,7 @@ module.exports = internals.Router = function (server) { this.server = server; this.table = {}; // Key: HTTP method or * for catch-all, value: sorted array of routes - this.vhosts = {}; // Key: hostname, value: see this.table + this.vhosts = null; // {} where Key: hostname, value: see this.table this.notfound = new Route({ method: 'notfound', @@ -43,26 +43,28 @@ module.exports = internals.Router = function (server) { internals.Router.prototype.route = function (request) { var method = (request.method === 'head' ? 'get' : request.method); - var vhost = (request.info.host && this.vhosts[request.info.host.split(':')[0]]); + var vhost = (this.vhosts && request.info.host && this.vhosts[request.info.host.split(':')[0]]); var lookup = function (table, verb) { + var match = null; var routes = table[verb] || []; for (var i = 0, il = routes.length; i < il; ++i) { var route = routes[i]; if (route.match(request)) { - return route; + match = route; + break; } } - return null; + return match; }; // Lookup route var route = (vhost && lookup(vhost, method)) || lookup(this.table, method) || - (method === 'options' && this.cors && this.cors) || + (method === 'options' && this.cors) || (vhost && lookup(vhost, '*')) || lookup(this.table, '*') || this.notfound; @@ -86,6 +88,7 @@ internals.Router.prototype.add = function (configs, env) { vhosts.forEach(function (vhost) { if (vhost !== '*') { + self.vhosts = self.vhosts || {}; self.vhosts[vhost] = self.vhosts[vhost] || {}; } @@ -128,7 +131,7 @@ internals.Router.prototype.routingTable = function (host) { }); }; - collect(host && this.vhosts[host]); + collect(host && this.vhosts && this.vhosts[host]); collect(this.table); return result; diff --git a/lib/server.js b/lib/server.js index 8371a8b7f..90fc37db8 100755 --- a/lib/server.js +++ b/lib/server.js @@ -151,29 +151,12 @@ internals.Server.prototype._dispatch = function (options) { var self = this; - return function (req, res) { + options = options || {}; - // Create request object + return function (req, res) { var request = new Request(self, req, res, options); - if (req.socket && - self.settings.timeout.socket !== undefined) { - - req.socket.setTimeout(self.settings.timeout.socket || 0); - } - - // Execute onRequest extensions (can change request method and url) - - request._onRequestExt(function (err) { - - if (err) { - return; // Handled by the request - } - - // Lookup route - - request._execute(self._router.route(request)); - }); + request._execute(); }; }; diff --git a/lib/views.js b/lib/views.js index 5950bc757..c45ff769e 100755 --- a/lib/views.js +++ b/lib/views.js @@ -166,8 +166,7 @@ internals.Manager.prototype.render = function (filename, context, options, callb var self = this; context = context || {}; - callback = Utils.nextTick(arguments.length === 4 ? arguments[3] : arguments[2]); - options = (arguments.length === 4 ? arguments[2] : {}) || {}; + options = options || {}; var engine = null; diff --git a/package.json b/package.json index b4fee176a..6542784a9 100755 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "hapi", "description": "HTTP Server framework", "homepage": "http://hapijs.com", - "version": "1.2.0", + "version": "1.3.0", "author": "Eran Hammer (http://hueniverse.com)", "contributors": [ { @@ -33,7 +33,7 @@ "boom": "0.4.x", "joi": "0.3.x", "catbox": "0.6.x", - "hawk": "0.13.x", + "hawk": "0.15.x", "shot": "0.4.x", "cryptiles": "0.2.x", "iron": "0.3.x", diff --git a/test/integration/auth.js b/test/integration/auth.js index c9213b56b..560ca19ab 100755 --- a/test/integration/auth.js +++ b/test/integration/auth.js @@ -654,6 +654,31 @@ describe('Auth', function () { var request = { method: 'POST', url: '/hawk', headers: { authorization: hawkHeader('john', '/hawk').field, custom: 'example.com:8080' } }; + var config = { + auth: { + scheme: 'hawk', + getCredentialsFunc: getCredentials, + hawk: { + hostHeaderName: 'custom' + } + } + }; + + var server = new Hapi.Server(config); + server.route({ method: 'POST', path: '/hawk', handler: hawkHandler, config: { auth: 'default' } }); + + server.inject(request, function (res) { + + expect(res.statusCode).to.equal(200); + expect(res.result).to.equal('Success'); + done(); + }); + }); + + it('returns a reply on successful auth when using a custom host header key (compatibility)', function (done) { + + var request = { method: 'POST', url: '/hawk', headers: { authorization: hawkHeader('john', '/hawk').field, custom: 'example.com:8080' } }; + var config = { auth: { scheme: 'hawk', diff --git a/test/integration/route.js b/test/integration/route.js index 34fd76f86..b1d7984fa 100755 --- a/test/integration/route.js +++ b/test/integration/route.js @@ -48,7 +48,7 @@ describe('Route', function () { for (var ai = 0, al = paths.length; ai < al; ++ai) { var a = { path: paths[ai], server: { settings: { router: { isCaseSensitive: true } } } }; - Route.prototype._generateRegex.call(a); + Route.prototype._parsePath.call(a); for (var bi = 0, bl = paths.length; bi < bl; ++bi) { if (ai === bi) { @@ -56,7 +56,7 @@ describe('Route', function () { } var b = { path: paths[bi], server: { settings: { router: { isCaseSensitive: true } } } }; - Route.prototype._generateRegex.call(b); + Route.prototype._parsePath.call(b); var a2b = Route.sort(a, b); var b2a = Route.sort(b, a); diff --git a/test/unit/request.js b/test/unit/request.js index 1002ca791..ad3b410c2 100755 --- a/test/unit/request.js +++ b/test/unit/request.js @@ -59,7 +59,7 @@ describe('Request', function () { var fn = function () { - var request = new Request(null, _req, _res); + var request = new Request(null, _req, _res, {}); }; expect(fn).throws(Error, 'server must be provided'); @@ -70,7 +70,7 @@ describe('Request', function () { var fn = function () { - var request = new Request(server, null, _res); + var request = new Request(server, null, _res, {}); }; expect(fn).throws(Error, 'req must be provided'); @@ -81,7 +81,7 @@ describe('Request', function () { var fn = function () { - var request = new Request(server, _req, null); + var request = new Request(server, _req, null, {}); }; expect(fn).throws(Error, 'res must be provided'); @@ -92,7 +92,7 @@ describe('Request', function () { var fn = function () { - var request = new Request(server, _req, _res); + var request = new Request(server, _req, _res, {}); }; expect(fn).not.to.throw(Error); done(); @@ -102,7 +102,7 @@ describe('Request', function () { it('changes method with a lowercase version of the value passed in', function (done) { - var request = new Request(server, _req, _res); + var request = new Request(server, _req, _res, {}); request._setMethod('GET'); expect(request.method).to.equal('get'); @@ -114,7 +114,7 @@ describe('Request', function () { it('sets url, path, and query', function (done) { - var request = new Request(server, _req, _res); + var request = new Request(server, _req, _res, {}); var url = 'http://localhost/page?param1=something'; request._setUrl(url); @@ -130,7 +130,7 @@ describe('Request', function () { it('adds a log event to the request', function (done) { - var request = new Request(server, _req, _res); + var request = new Request(server, _req, _res, {}); request.log('1', 'log event 1', Date.now()); request.log(['2'], 'log event 2', new Date(Date.now())); request.log(['3', '4']); @@ -150,7 +150,7 @@ describe('Request', function () { it('doesn\'t throw an error when logging without data and debug is configured', function (done) { var debugServer = new Hapi.Server({ debug: { request: ['uncaught'] } }); - var request = new Request(debugServer, _req, _res); + var request = new Request(debugServer, _req, _res, {}); var fn = function () { @@ -177,7 +177,7 @@ describe('Request', function () { it('doesn\'t normalize a path when normalization is disabled', function (done) { - var request = new Request(server, _req, _res); + var request = new Request(server, _req, _res, {}); var url = 'http://localhost' + rawPath + '?param1=something'; request._setUrl(url); @@ -192,7 +192,7 @@ describe('Request', function () { var normServer = new Hapi.Server({ router: { normalizeRequestPath: true } }); - var request = new Request(normServer, _req, _res); + var request = new Request(normServer, _req, _res, {}); var url = 'http://localhost' + rawPath + '?param1=something'; request._setUrl(url); @@ -203,17 +203,4 @@ describe('Request', function () { done(); }); }); - - describe('#_reply', function () { - - it('returns if already replied with callback', function (done) { - - var request = new Request(server, _req, _res); - request._isReplied = true; - request._reply(null, function () { - - done(); - }); - }); - }); }); \ No newline at end of file diff --git a/test/unit/route.js b/test/unit/route.js index f457e8ee1..9b0b0751c 100755 --- a/test/unit/route.js +++ b/test/unit/route.js @@ -132,7 +132,7 @@ describe('Route', function () { }(); }); - describe('#_generateRegex', function () { + describe('#_parsePath', function () { var testFingerprints = function () { @@ -143,7 +143,7 @@ describe('Route', function () { '/path/to/somewhere': '/path/to/somewhere', '/{param}': '/?', '/{param?}': '/?', - '/{param*}': '/*', + '/{param*}': '/#', '/{param*5}': '/?/?/?/?/?', '/path/{param}': '/path/?', '/path/{param}/to': '/path/?/to', @@ -151,7 +151,7 @@ describe('Route', function () { '/path/{param}/to/{some}': '/path/?/to/?', '/path/{param}/to/{some?}': '/path/?/to/?', '/path/{param*2}/to': '/path/?/?/to', - '/path/{param*}': '/path/*', + '/path/{param*}': '/path/#', '/path/{param*10}/to': '/path/?/?/?/?/?/?/?/?/?/?/to', '/path/{param*2}': '/path/?/?', '/%20path/': '/%20path/' diff --git a/test/unit/views.js b/test/unit/views.js index b9e669b8b..fa0d472bc 100755 --- a/test/unit/views.js +++ b/test/unit/views.js @@ -54,7 +54,7 @@ describe('Views', function () { } }); - views.render('valid/test', { title: 'test', message: 'Hapi' }, function (err, rendered, config) { + views.render('valid/test', { title: 'test', message: 'Hapi' }, null, function (err, rendered, config) { expect(rendered).to.exist; expect(rendered.length).above(1); @@ -79,7 +79,7 @@ describe('Views', function () { } }); - views.render('valid/test', { title: 'test', message: 'Hapi' }, function (err, rendered, config) { + views.render('valid/test', { title: 'test', message: 'Hapi' }, null, function (err, rendered, config) { expect(err).to.exist; expect(err.message).to.equal('Bad bad view'); @@ -89,7 +89,7 @@ describe('Views', function () { it('should work and not throw with valid (no layouts)', function (done) { - testView.render('valid/test', { title: 'test', message: 'Hapi' }, function (err, rendered, config) { + testView.render('valid/test', { title: 'test', message: 'Hapi' }, null, function (err, rendered, config) { expect(rendered).to.exist; expect(rendered.length).above(1); @@ -99,7 +99,7 @@ describe('Views', function () { it('should work and not throw with valid (with layouts)', function (done) { - testViewWithLayouts.render('valid/test', { title: 'test', message: 'Hapi' }, function (err, rendered, config) { + testViewWithLayouts.render('valid/test', { title: 'test', message: 'Hapi' }, null, function (err, rendered, config) { expect(rendered).to.exist; expect(rendered.length).above(1); @@ -121,7 +121,7 @@ describe('Views', function () { it('should return error when referencing non existant partial (with layouts)', function (done) { - testViewWithLayouts.render('invalid/test', { title: 'test', message: 'Hapi' }, function (err, rendered, config) { + testViewWithLayouts.render('invalid/test', { title: 'test', message: 'Hapi' }, null, function (err, rendered, config) { expect(err).to.exist; done(); @@ -130,7 +130,7 @@ describe('Views', function () { it('should return error when referencing non existant partial (no layouts)', function (done) { - testView.render('invalid/test', { title: 'test', message: 'Hapi' }, function (err, rendered, config) { + testView.render('invalid/test', { title: 'test', message: 'Hapi' }, null, function (err, rendered, config) { expect(err).to.exist; done(); @@ -141,7 +141,7 @@ describe('Views', function () { it('should return error if context uses layoutKeyword as a key', function (done) { var opts = { title: 'test', message: 'Hapi', content: 1 }; - testViewWithLayouts.render('valid/test', opts, function (err, rendered, config) { + testViewWithLayouts.render('valid/test', opts, null, function (err, rendered, config) { expect(err).to.exist; done(); @@ -150,7 +150,7 @@ describe('Views', function () { it('should return error on compile error (invalid template code)', function (done) { - testView.render('invalid/badmustache', { title: 'test', message: 'Hapi' }, function (err, rendered, config) { + testView.render('invalid/badmustache', { title: 'test', message: 'Hapi' }, null, function (err, rendered, config) { expect(err instanceof Error).to.equal(true); done(); @@ -165,7 +165,7 @@ describe('Views', function () { partialsPath: viewsPath + '/valid/partials' }); - tempView.render('testPartials', {}, function (err, rendered, config) { + tempView.render('testPartials', {}, null, function (err, rendered, config) { expect(rendered).to.equal('Nav:|'); done(); @@ -180,7 +180,7 @@ describe('Views', function () { partialsPath: viewsPath + '/valid/partials/' }); - tempView.render('testPartials', {}, function (err, rendered, config) { + tempView.render('testPartials', {}, null, function (err, rendered, config) { expect(rendered).to.exist; expect(rendered.length).above(1); @@ -197,7 +197,7 @@ describe('Views', function () { engines: { 'html': 'jade' } }); - tempView.render('testPartials', {}, function (err, rendered, config) { + tempView.render('testPartials', {}, null, function (err, rendered, config) { expect(rendered).to.exist; expect(rendered.length).above(1); @@ -213,7 +213,7 @@ describe('Views', function () { helpersPath: viewsPath + '/valid/helpers' }); - tempView.render('testHelpers', { something: 'uppercase' }, function (err, rendered, config) { + tempView.render('testHelpers', { something: 'uppercase' }, null, function (err, rendered, config) { expect(rendered).to.equal('

This is all UPPERCASE and this is how we like it!

'); done();