Remove trailing whitespace from header values.

Bug: https://github.com/dart-lang/sdk/issues/53005
Bug: https://github.com/dart-lang/sdk/issues/51532
Change-Id: I8a2fc04f48d50103819d655ccd300e73d59fbecc
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/319903
Reviewed-by: Alexander Aprelev <aam@google.com>
Commit-Queue: Brian Quinlan <bquinlan@google.com>
This commit is contained in:
Brian Quinlan 2023-08-15 16:58:12 +00:00 committed by Commit Queue
parent aa74293d3a
commit ce0d051c9c
3 changed files with 62 additions and 11 deletions

View file

@ -18,6 +18,14 @@
### Libraries
#### `dart:io`
- **Breaking change** [#53005][]: The headers returned by
`HttpClientResponse.headers` and `HttpRequest.headers` no longer include
trailing whitespace in their values.
[#52334]: https://dartbug.com/53005
#### `dart:js_interop`
- **JSNumber.toDart and Object.toJS**:

View file

@ -683,6 +683,9 @@ class _HttpParser extends Stream<_HttpIncoming> {
_state = _State.HEADER_VALUE_START;
} else {
String headerField = String.fromCharCodes(_headerField);
// The field value does not include any leading or trailing whitespace.
// See https://www.rfc-editor.org/rfc/rfc7230#section-3.2.4
_removeTrailingSpaces(_headerValue);
String headerValue = String.fromCharCodes(_headerValue);
const errorIfBothText = "Both Content-Length and Transfer-Encoding "
"are specified, at most one is allowed";
@ -1002,6 +1005,17 @@ class _HttpParser extends Stream<_HttpIncoming> {
return (byte > 31 && byte < 128) || (byte == _CharCode.HT);
}
static void _removeTrailingSpaces(List<int> value) {
var length = value.length;
while (length > 0 &&
(value[length - 1] == _CharCode.SP ||
value[length - 1] == _CharCode.HT)) {
--length;
}
value.length = length;
}
static List<String> _tokenizeFieldValue(String headerValue) {
List<String> tokens = <String>[];
int start = 0;

View file

@ -381,13 +381,24 @@ POST /test HTTP/1.1\r
request = """
POST /test HTTP/1.1\r
Header-A: AAA\r
X-Header-B: bbb\r
Header-A: AAA aaa\r
X-Header-B: bbb BBB\r
\r
""";
headers = new Map();
headers["header-a"] = "AAA";
headers["x-header-b"] = "bbb";
headers["header-a"] = "AAA aaa";
headers["x-header-b"] = "bbb BBB";
_testParseRequestLean(request, "POST", "/test", expectedHeaders: headers);
request = """
POST /test HTTP/1.1\r
Header-A: \t AAA aaa \t \r
X-Header-B: \t bbb BBB \t \r
\r
""";
headers = new Map();
headers["header-a"] = "AAA aaa";
headers["x-header-b"] = "bbb BBB";
_testParseRequestLean(request, "POST", "/test", expectedHeaders: headers);
request = """
@ -404,22 +415,23 @@ Empty-Header-2:\r
request = """
POST /test HTTP/1.1\r
Header-A: AAA\r
X-Header-B:\t \t bbb\r
Empty-Header-1:\t \t \r
Empty-Header-2:\t \t \r
\r
\r
""";
headers = new Map();
headers["header-a"] = "AAA";
headers["x-header-b"] = "bbb";
headers["empty-header-1"] = "";
headers["empty-header-2"] = "";
_testParseRequestLean(request, "POST", "/test", expectedHeaders: headers);
request = """
POST /test HTTP/1.1\r
Header-A: AA\r
A\r
Header-A: \t AA\r
A \t \r
X-Header-B: b\r
b\r
\t b\r
\t b \t \r
\r
""";
@ -461,6 +473,19 @@ Transfer-Encoding: chunked\r
01234\r
5\r
56789\r
0\r\n\r\n""";
_testParseRequest(request, "POST", "/test",
expectedTransferLength: -1, expectedBytesReceived: 10, chunked: true);
// Test LWS around chunked encoding header value.
request = """
POST /test HTTP/1.1\r
Transfer-Encoding: \t chunked \t \r
\r
5\r
01234\r
5\r
56789\r
0\r\n\r\n""";
_testParseRequest(request, "POST", "/test",
expectedTransferLength: -1, expectedBytesReceived: 10, chunked: true);
@ -569,6 +594,10 @@ Sec-WebSocket-Version: 13\r
_testParseResponse(response, 100, "Continue",
expectedTransferLength: 10, expectedBytesReceived: 0);
response = "HTTP/1.1 100 Continue\r\nContent-Length: \t 10 \t \r\n\r\n";
_testParseResponse(response, 100, "Continue",
expectedTransferLength: 10, expectedBytesReceived: 0);
response = "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n"
"Connection: Close\r\n\r\n";
_testParseResponse(response, 200, "OK", connectionClose: true);