|
|
|
|
|
var Hoek = require('hoek'); |
|
var Sntp = require('sntp'); |
|
var Boom = require('boom'); |
|
|
|
|
|
|
|
|
|
var internals = {}; |
|
|
|
|
|
|
|
|
|
internals.import = function () { |
|
|
|
for (var i in Hoek) { |
|
if (Hoek.hasOwnProperty(i)) { |
|
exports[i] = Hoek[i]; |
|
} |
|
} |
|
}; |
|
|
|
internals.import(); |
|
|
|
|
|
|
|
|
|
exports.version = function () { |
|
|
|
return exports.loadPackage(__dirname + '/..').version; |
|
}; |
|
|
|
|
|
|
|
|
|
exports.parseHost = function (req, hostHeaderName) { |
|
|
|
hostHeaderName = (hostHeaderName ? hostHeaderName.toLowerCase() : 'host'); |
|
var hostHeader = req.headers[hostHeaderName]; |
|
if (!hostHeader) { |
|
return null; |
|
} |
|
|
|
var hostHeaderRegex; |
|
if (hostHeader[0] === '[') { |
|
hostHeaderRegex = /^(?:(?:\r\n)?\s)*(\[[^\]]+\])(?::(\d+))?(?:(?:\r\n)?\s)*$/; |
|
} |
|
else { |
|
hostHeaderRegex = /^(?:(?:\r\n)?\s)*([^:]+)(?::(\d+))?(?:(?:\r\n)?\s)*$/; |
|
} |
|
|
|
var hostParts = hostHeader.match(hostHeaderRegex); |
|
|
|
if (!hostParts || |
|
hostParts.length !== 3 || |
|
!hostParts[1]) { |
|
|
|
return null; |
|
} |
|
|
|
return { |
|
name: hostParts[1], |
|
port: (hostParts[2] ? hostParts[2] : (req.connection && req.connection.encrypted ? 443 : 80)) |
|
}; |
|
}; |
|
|
|
|
|
|
|
|
|
exports.parseContentType = function (header) { |
|
|
|
if (!header) { |
|
return ''; |
|
} |
|
|
|
return header.split(';')[0].trim().toLowerCase(); |
|
}; |
|
|
|
|
|
|
|
|
|
exports.parseRequest = function (req, options) { |
|
|
|
if (!req.headers) { |
|
return req; |
|
} |
|
|
|
|
|
|
|
if (!options.host || !options.port) { |
|
var host = exports.parseHost(req, options.hostHeaderName); |
|
if (!host) { |
|
return new Error('Invalid Host header'); |
|
} |
|
} |
|
|
|
var request = { |
|
method: req.method, |
|
url: req.url, |
|
host: options.host || host.name, |
|
port: options.port || host.port, |
|
authorization: req.headers.authorization, |
|
contentType: req.headers['content-type'] || '' |
|
}; |
|
|
|
return request; |
|
}; |
|
|
|
|
|
exports.now = function () { |
|
|
|
return Sntp.now(); |
|
}; |
|
|
|
|
|
|
|
|
|
exports.parseAuthorizationHeader = function (header, keys) { |
|
|
|
keys = keys || ['id', 'ts', 'nonce', 'hash', 'ext', 'mac', 'app', 'dlg']; |
|
|
|
if (!header) { |
|
return Boom.unauthorized(null, 'Hawk'); |
|
} |
|
|
|
var headerParts = header.match(/^(\w+)(?:\s+(.*))?$/); |
|
if (!headerParts) { |
|
return Boom.badRequest('Invalid header syntax'); |
|
} |
|
|
|
var scheme = headerParts[1]; |
|
if (scheme.toLowerCase() !== 'hawk') { |
|
return Boom.unauthorized(null, 'Hawk'); |
|
} |
|
|
|
var attributesString = headerParts[2]; |
|
if (!attributesString) { |
|
return Boom.badRequest('Invalid header syntax'); |
|
} |
|
|
|
var attributes = {}; |
|
var errorMessage = ''; |
|
var verify = attributesString.replace(/(\w+)="([^"\\]*)"\s*(?:,\s*|$)/g, function ($0, $1, $2) { |
|
|
|
|
|
|
|
if (keys.indexOf($1) === -1) { |
|
errorMessage = 'Unknown attribute: ' + $1; |
|
return; |
|
} |
|
|
|
|
|
|
|
if ($2.match(/^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~]+$/) === null) { |
|
errorMessage = 'Bad attribute value: ' + $1; |
|
return; |
|
} |
|
|
|
|
|
|
|
if (attributes.hasOwnProperty($1)) { |
|
errorMessage = 'Duplicate attribute: ' + $1; |
|
return; |
|
} |
|
|
|
attributes[$1] = $2; |
|
return ''; |
|
}); |
|
|
|
if (verify !== '') { |
|
return Boom.badRequest(errorMessage || 'Bad header format'); |
|
} |
|
|
|
return attributes; |
|
}; |
|
|
|
|
|
exports.unauthorized = function (message) { |
|
|
|
return Boom.unauthorized(message, 'Hawk'); |
|
}; |
|
|
|
|