-
Notifications
You must be signed in to change notification settings - Fork 30k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
http: create an option for setting a maximum size for uri parsing #26553
Changes from all commits
45fa652
3218828
197d264
e7ade8b
78dfcf7
710e830
38307dc
93e384a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1959,6 +1959,17 @@ added: v11.6.0 | |
Read-only property specifying the maximum allowed size of HTTP headers in bytes. | ||
Defaults to 8KB. Configurable using the [`--max-http-header-size`][] CLI option. | ||
|
||
## http.maxUriSize | ||
<!-- YAML | ||
added: REPLACEME | ||
--> | ||
|
||
* {number} | ||
|
||
Read-only property specifying the maximum allowed size of HTTP request uri | ||
in bytes. Defaults to 7KB. Configurable using the | ||
[`--max-http-uri-size`][] CLI option. | ||
BridgeAR marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing bottom references. Will this option be documented later? |
||
|
||
## http.request(options[, callback]) | ||
## http.request(url[, options][, callback]) | ||
<!-- YAML | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -426,6 +426,10 @@ PerProcessOptionsParser::PerProcessOptionsParser( | |
"set the maximum size of HTTP headers (default: 8KB)", | ||
&PerProcessOptions::max_http_header_size, | ||
kAllowedInEnvironment); | ||
AddOption("--max-http-uri-size", | ||
"set the maximum size of HTTP request uri (default: 7KB)", | ||
&PerProcessOptions::max_http_uri_size, | ||
kAllowedInEnvironment); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fwiw, this should not be a per-process but rather a per-Environment option. |
||
AddOption("--v8-pool-size", | ||
"set V8's thread pool size", | ||
&PerProcessOptions::v8_thread_pool_size, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,34 @@ | ||
// Flags: --expose-internals | ||
'use strict'; | ||
const { expectsError, mustCall } = require('../common'); | ||
const assert = require('assert'); | ||
const { createServer, maxHeaderSize } = require('http'); | ||
const { createConnection } = require('net'); | ||
const { expectsError, mustCall } = require('../common'); | ||
|
||
const { getOptionValue } = require('internal/options'); | ||
|
||
const currentParser = getOptionValue('--http-parser'); | ||
|
||
const sumOfLengths = (strings) => strings | ||
.map((string) => string.length) | ||
.reduce((a, b) => a + b); | ||
|
||
const getBreakingLength = () => { | ||
if (currentParser === 'llhttp') { | ||
return maxHeaderSize - HEADER_NAME.length + 1; | ||
} else { | ||
return maxHeaderSize - HEADER_NAME.length - HEADER_SEPARATOR - | ||
(2 * CRLF.length) + 1; | ||
} | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add some comments in this function? |
||
|
||
const CRLF = '\r\n'; | ||
const DUMMY_HEADER_NAME = 'Cookie: '; | ||
const DUMMY_HEADER_VALUE = 'a'.repeat( | ||
// Plus one is to make it 1 byte too big | ||
maxHeaderSize - DUMMY_HEADER_NAME.length - (2 * CRLF.length) + 1 | ||
); | ||
const HEADER_NAME = 'Cookie'; | ||
const HEADER_SEPARATOR = ': '; | ||
const HEADER_VALUE = 'a'.repeat(getBreakingLength()); | ||
const PAYLOAD_GET = 'GET /blah HTTP/1.1'; | ||
const PAYLOAD = PAYLOAD_GET + CRLF + | ||
DUMMY_HEADER_NAME + DUMMY_HEADER_VALUE + CRLF.repeat(2); | ||
HEADER_NAME + HEADER_SEPARATOR + HEADER_VALUE + CRLF.repeat(2); | ||
|
||
const server = createServer(); | ||
|
||
|
@@ -21,7 +37,13 @@ server.on('connection', mustCall((socket) => { | |
type: Error, | ||
message: 'Parse Error', | ||
code: 'HPE_HEADER_OVERFLOW', | ||
bytesParsed: maxHeaderSize + PAYLOAD_GET.length, | ||
bytesParsed: sumOfLengths([ | ||
PAYLOAD_GET, | ||
CRLF, | ||
HEADER_NAME, | ||
HEADER_SEPARATOR, | ||
HEADER_VALUE | ||
]) + 1, | ||
rawPacket: Buffer.from(PAYLOAD) | ||
})); | ||
})); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
'use strict'; | ||
const { expectsError, mustCall } = require('../common'); | ||
const assert = require('assert'); | ||
const { createServer, maxUriSize } = require('http'); | ||
const { createConnection } = require('net'); | ||
|
||
const CRLF = '\r\n'; | ||
const REQUEST_METHOD_AND_SPACE = 'GET '; | ||
const DUMMY_URI = '/' + 'a'.repeat( | ||
maxUriSize | ||
); // The slash makes it just 1 byte too long. | ||
|
||
const PAYLOAD = REQUEST_METHOD_AND_SPACE + DUMMY_URI + CRLF; | ||
|
||
const server = createServer(); | ||
|
||
server.on('connection', mustCall((socket) => { | ||
socket.on('error', expectsError({ | ||
type: Error, | ||
message: 'Parse Error', | ||
code: 'HPE_URI_OVERFLOW', | ||
bytesParsed: REQUEST_METHOD_AND_SPACE.length + DUMMY_URI.length, | ||
rawPacket: Buffer.from(PAYLOAD) | ||
})); | ||
})); | ||
|
||
server.listen(0, mustCall(() => { | ||
const c = createConnection(server.address().port); | ||
let received = ''; | ||
|
||
c.on('connect', mustCall(() => { | ||
c.write(PAYLOAD); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is highly possible that the payload comes out of the other side of the socket as a single Buffer. Can you please add a test where the URL is split into two or more chunks? |
||
})); | ||
c.on('data', mustCall((data) => { | ||
received += data.toString(); | ||
})); | ||
c.on('end', mustCall(() => { | ||
assert.strictEqual( | ||
received, | ||
'HTTP/1.1 414 URI Too Long\r\n' + | ||
'Connection: close\r\n\r\n' | ||
); | ||
c.end(); | ||
})); | ||
c.on('close', mustCall(() => server.close())); | ||
})); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -122,8 +122,8 @@ const test2 = common.mustCall(() => { | |
'Agent: nod2\r\n' + | ||
'X-CRASH: '; | ||
|
||
// /, Host, localhost, Agent, node, X-CRASH, a... | ||
const currentSize = 1 + 4 + 9 + 5 + 4 + 7; | ||
// Host, localhost, Agent, node, X-CRASH, a... | ||
const currentSize = 4 + 9 + 5 + 4 + 7; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you please explain this change? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. currentSize is being used by |
||
headers = fillHeaders(headers, currentSize); | ||
|
||
const server = http.createServer(common.mustNotCall()); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.