Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81146 views
1
// Load modules
2
3
var Sntp = require('sntp');
4
var Boom = require('boom');
5
6
7
// Declare internals
8
9
var internals = {};
10
11
12
exports.version = function () {
13
14
return require('../package.json').version;
15
};
16
17
18
// Extract host and port from request
19
20
// $1 $2
21
internals.hostHeaderRegex = /^(?:(?:\r\n)?\s)*((?:[^:]+)|(?:\[[^\]]+\]))(?::(\d+))?(?:(?:\r\n)?\s)*$/; // (IPv4, hostname)|(IPv6)
22
23
24
exports.parseHost = function (req, hostHeaderName) {
25
26
hostHeaderName = (hostHeaderName ? hostHeaderName.toLowerCase() : 'host');
27
var hostHeader = req.headers[hostHeaderName];
28
if (!hostHeader) {
29
return null;
30
}
31
32
var hostParts = hostHeader.match(internals.hostHeaderRegex);
33
if (!hostParts) {
34
return null;
35
}
36
37
return {
38
name: hostParts[1],
39
port: (hostParts[2] ? hostParts[2] : (req.connection && req.connection.encrypted ? 443 : 80))
40
};
41
};
42
43
44
// Parse Content-Type header content
45
46
exports.parseContentType = function (header) {
47
48
if (!header) {
49
return '';
50
}
51
52
return header.split(';')[0].trim().toLowerCase();
53
};
54
55
56
// Convert node's to request configuration object
57
58
exports.parseRequest = function (req, options) {
59
60
if (!req.headers) {
61
return req;
62
}
63
64
// Obtain host and port information
65
66
if (!options.host || !options.port) {
67
var host = exports.parseHost(req, options.hostHeaderName);
68
if (!host) {
69
return new Error('Invalid Host header');
70
}
71
}
72
73
var request = {
74
method: req.method,
75
url: req.url,
76
host: options.host || host.name,
77
port: options.port || host.port,
78
authorization: req.headers.authorization,
79
contentType: req.headers['content-type'] || ''
80
};
81
82
return request;
83
};
84
85
86
exports.now = function (localtimeOffsetMsec) {
87
88
return Sntp.now() + (localtimeOffsetMsec || 0);
89
};
90
91
92
exports.nowSecs = function (localtimeOffsetMsec) {
93
94
return Math.floor(exports.now(localtimeOffsetMsec) / 1000);
95
};
96
97
98
// Parse Hawk HTTP Authorization header
99
100
exports.parseAuthorizationHeader = function (header, keys) {
101
102
keys = keys || ['id', 'ts', 'nonce', 'hash', 'ext', 'mac', 'app', 'dlg'];
103
104
if (!header) {
105
return Boom.unauthorized(null, 'Hawk');
106
}
107
108
var headerParts = header.match(/^(\w+)(?:\s+(.*))?$/); // Header: scheme[ something]
109
if (!headerParts) {
110
return Boom.badRequest('Invalid header syntax');
111
}
112
113
var scheme = headerParts[1];
114
if (scheme.toLowerCase() !== 'hawk') {
115
return Boom.unauthorized(null, 'Hawk');
116
}
117
118
var attributesString = headerParts[2];
119
if (!attributesString) {
120
return Boom.badRequest('Invalid header syntax');
121
}
122
123
var attributes = {};
124
var errorMessage = '';
125
var verify = attributesString.replace(/(\w+)="([^"\\]*)"\s*(?:,\s*|$)/g, function ($0, $1, $2) {
126
127
// Check valid attribute names
128
129
if (keys.indexOf($1) === -1) {
130
errorMessage = 'Unknown attribute: ' + $1;
131
return;
132
}
133
134
// Allowed attribute value characters: !#$%&'()*+,-./:;<=>?@[]^_`{|}~ and space, a-z, A-Z, 0-9
135
136
if ($2.match(/^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~]+$/) === null) {
137
errorMessage = 'Bad attribute value: ' + $1;
138
return;
139
}
140
141
// Check for duplicates
142
143
if (attributes.hasOwnProperty($1)) {
144
errorMessage = 'Duplicate attribute: ' + $1;
145
return;
146
}
147
148
attributes[$1] = $2;
149
return '';
150
});
151
152
if (verify !== '') {
153
return Boom.badRequest(errorMessage || 'Bad header format');
154
}
155
156
return attributes;
157
};
158
159
160
exports.unauthorized = function (message) {
161
162
return Boom.unauthorized(message, 'Hawk');
163
};
164
165
166