Skip to content

Commit

Permalink
Remove trailing whitespace from header values.
Browse files Browse the repository at this point in the history
Bug: #53005
Bug: #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>
  • Loading branch information
brianquinlan authored and Commit Queue committed Aug 15, 2023
1 parent aa74293 commit ce0d051
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 11 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**:
Expand Down
14 changes: 14 additions & 0 deletions sdk/lib/_http/http_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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;
Expand Down
51 changes: 40 additions & 11 deletions tests/standalone/io/http_parser_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 = """
Expand All @@ -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
""";

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit ce0d051

Please sign in to comment.