Skip to content

Commit 5c2ef14

Browse files
committed
http: optimize short path validation
PR-URL: #10654 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Fedor Indutny <[email protected]>
1 parent 6782577 commit 5c2ef14

File tree

1 file changed

+42
-8
lines changed

1 file changed

+42
-8
lines changed

lib/_http_client.js

+42-8
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,34 @@ const Agent = require('_http_agent');
1515
const Buffer = require('buffer').Buffer;
1616
const urlToOptions = require('internal/url').urlToOptions;
1717

18+
// The actual list of disallowed characters in regexp form is more like:
19+
// /[^A-Za-z0-9\-._~!$&'()*+,;=/:@]/
20+
// with an additional rule for ignoring percentage-escaped characters, but
21+
// that's a) hard to capture in a regular expression that performs well, and
22+
// b) possibly too restrictive for real-world usage. So instead we restrict the
23+
// filter to just control characters and spaces.
24+
//
25+
// This function is used in the case of small paths, where manual character code
26+
// checks can greatly outperform the equivalent regexp (tested in V8 5.4).
27+
function isInvalidPath(s) {
28+
var i = 0;
29+
if (s.charCodeAt(0) <= 32) return true;
30+
if (++i >= s.length) return false;
31+
if (s.charCodeAt(1) <= 32) return true;
32+
if (++i >= s.length) return false;
33+
if (s.charCodeAt(2) <= 32) return true;
34+
if (++i >= s.length) return false;
35+
if (s.charCodeAt(3) <= 32) return true;
36+
if (++i >= s.length) return false;
37+
if (s.charCodeAt(4) <= 32) return true;
38+
if (++i >= s.length) return false;
39+
if (s.charCodeAt(5) <= 32) return true;
40+
++i;
41+
for (; i < s.length; ++i)
42+
if (s.charCodeAt(i) <= 32) return true;
43+
return false;
44+
}
45+
1846
function ClientRequest(options, cb) {
1947
var self = this;
2048
OutgoingMessage.call(self);
@@ -45,14 +73,20 @@ function ClientRequest(options, cb) {
4573
if (self.agent && self.agent.protocol)
4674
expectedProtocol = self.agent.protocol;
4775

48-
if (options.path && /[\u0000-\u0020]/.test(options.path)) {
49-
// The actual regex is more like /[^A-Za-z0-9\-._~!$&'()*+,;=/:@]/
50-
// with an additional rule for ignoring percentage-escaped characters
51-
// but that's a) hard to capture in a regular expression that performs
52-
// well, and b) possibly too restrictive for real-world usage.
53-
// Restrict the filter to control characters and spaces.
54-
throw new TypeError('Request path contains unescaped characters');
55-
} else if (protocol !== expectedProtocol) {
76+
var path;
77+
if (options.path) {
78+
path = '' + options.path;
79+
var invalidPath;
80+
if (path.length <= 39) { // Determined experimentally in V8 5.4
81+
invalidPath = isInvalidPath(path);
82+
} else {
83+
invalidPath = /[\u0000-\u0020]/.test(path);
84+
}
85+
if (invalidPath)
86+
throw new TypeError('Request path contains unescaped characters');
87+
}
88+
89+
if (protocol !== expectedProtocol) {
5690
throw new Error('Protocol "' + protocol + '" not supported. ' +
5791
'Expected "' + expectedProtocol + '"');
5892
}

0 commit comments

Comments
 (0)