mirror of
https://github.com/dart-lang/sdk
synced 2024-10-02 23:39:19 +00:00
[dart:io] Loosen the HTTP header size limit
The `HttpClient` and `HttpServer` clasess now have a 1 MiB limit for the total size of the HTTP headers when parsing a request or response, instead of the former 8 KiB limit for each header name and value. This limit cannot be configured at this time. Bug: https://github.com/flutter/flutter/issues/56580 Change-Id: I5f094df32a93ec3e6645a0d69d8cf8263082775a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/150500 Commit-Queue: Zichang Guo <zichangguo@google.com> Reviewed-by: Jonas Termansen <sortie@google.com>
This commit is contained in:
parent
d9b874c4b5
commit
a6db069971
|
@ -8,6 +8,10 @@
|
|||
to cancel outgoing HTTP requests and stop following IO operations.
|
||||
* A validation check is added to `path` of class `Cookie`. Having characters
|
||||
ranging from 0x00 to 0x1f and 0x3b (";") will lead to a `FormatException`.
|
||||
* The `HttpClient` and `HttpServer` clasess now have a 1 MiB limit for the
|
||||
total size of the HTTP headers when parsing a request or response, instead
|
||||
of the former 8 KiB limit for each header name and value. This limit cannot
|
||||
be configured at this time.
|
||||
|
||||
#### `dart:typed_data`
|
||||
|
||||
|
|
|
@ -236,6 +236,7 @@ class _HttpParser extends Stream<_HttpIncoming> {
|
|||
Uint8List? _buffer;
|
||||
int _index = -1;
|
||||
|
||||
// Whether a HTTP request is being parsed (as opposed to a response).
|
||||
final bool _requestParser;
|
||||
int _state = _State.START;
|
||||
int? _httpVersionIndex;
|
||||
|
@ -246,8 +247,8 @@ class _HttpParser extends Stream<_HttpIncoming> {
|
|||
final List<int> _uriOrReasonPhrase = [];
|
||||
final List<int> _headerField = [];
|
||||
final List<int> _headerValue = [];
|
||||
// The limit for method, uriOrReasonPhrase, header field and value
|
||||
int _headerSizeLimit = 8 * 1024;
|
||||
static const _headerTotalSizeLimit = 1024 * 1024;
|
||||
int _headersReceivedSize = 0;
|
||||
|
||||
int _httpVersion = _HttpVersion.UNDETERMINED;
|
||||
int _transferLength = -1;
|
||||
|
@ -946,6 +947,7 @@ class _HttpParser extends Stream<_HttpIncoming> {
|
|||
_messageType = _MessageType.UNDETERMINED;
|
||||
_headerField.clear();
|
||||
_headerValue.clear();
|
||||
_headersReceivedSize = 0;
|
||||
_method.clear();
|
||||
_uriOrReasonPhrase.clear();
|
||||
|
||||
|
@ -977,8 +979,7 @@ class _HttpParser extends Stream<_HttpIncoming> {
|
|||
}
|
||||
|
||||
static bool _isValueChar(int byte) {
|
||||
return (byte > 31 && byte < 128) ||
|
||||
(byte == _CharCode.HT);
|
||||
return (byte > 31 && byte < 128) || (byte == _CharCode.HT);
|
||||
}
|
||||
|
||||
static List<String> _tokenizeFieldValue(String headerValue) {
|
||||
|
@ -1036,7 +1037,8 @@ class _HttpParser extends Stream<_HttpIncoming> {
|
|||
}
|
||||
|
||||
void _addWithValidation(List<int> list, int byte) {
|
||||
if (list.length < _headerSizeLimit) {
|
||||
_headersReceivedSize++;
|
||||
if (_headersReceivedSize < _headerTotalSizeLimit) {
|
||||
list.add(byte);
|
||||
} else {
|
||||
_reportSizeLimitError();
|
||||
|
@ -1074,7 +1076,8 @@ class _HttpParser extends Stream<_HttpIncoming> {
|
|||
throw UnsupportedError("Unexpected state: $_state");
|
||||
break;
|
||||
}
|
||||
throw HttpException("$method exceeds the $_headerSizeLimit size limit");
|
||||
throw HttpException(
|
||||
"$method exceeds the $_headerTotalSizeLimit size limit");
|
||||
}
|
||||
|
||||
_HttpIncoming _createIncoming(int transferLength) {
|
||||
|
|
81
tests/standalone/io/http_big_header_test.dart
Normal file
81
tests/standalone/io/http_big_header_test.dart
Normal file
|
@ -0,0 +1,81 @@
|
|||
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
// This test checks whether a big header is accepted.
|
||||
|
||||
Future<void> testClient(int limit) async {
|
||||
final server = await HttpServer.bind('127.0.0.1', 0);
|
||||
final str = 'a' * (1000);
|
||||
int size = 0;
|
||||
server.listen((request) async {
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
request.response.headers.add('dummy', str);
|
||||
size += 1000;
|
||||
if (size > limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
await request.response.close();
|
||||
server.close();
|
||||
});
|
||||
|
||||
final client = HttpClient();
|
||||
final request = await client.get('127.0.0.1', server.port, '/');
|
||||
await request.close();
|
||||
}
|
||||
|
||||
Future<void> client() async {
|
||||
int i = 64;
|
||||
try {
|
||||
for (; i < 101 * 1024 * 1024; i *= 100) {
|
||||
await testClient(i);
|
||||
}
|
||||
} on HttpException catch (e) {
|
||||
Expect.isTrue(e.toString().contains('size limit'));
|
||||
Expect.isTrue(i > 1024 * 1024);
|
||||
return;
|
||||
}
|
||||
Expect.fail('An exception is expected');
|
||||
}
|
||||
|
||||
Future<void> testServer(int limit, int port) async {
|
||||
final str = 'a' * (1000);
|
||||
final client = HttpClient();
|
||||
final request = await client.get('127.0.0.1', port, '/');
|
||||
for (int size = 0; size < limit; size += 1000) {
|
||||
request.headers.add('dummy', str);
|
||||
}
|
||||
await request.close();
|
||||
}
|
||||
|
||||
Future<void> server() async {
|
||||
final server = await HttpServer.bind('127.0.0.1', 0);
|
||||
int i = 64;
|
||||
try {
|
||||
server.listen((request) async {
|
||||
await request.response.close();
|
||||
});
|
||||
for (; i < 101 * 1024 * 1024; i *= 100) {
|
||||
print(i);
|
||||
await testServer(i, server.port);
|
||||
}
|
||||
} on SocketException catch (_) {
|
||||
// Server will close on error and writing to the socket will be blocked due
|
||||
// to broken pipe.
|
||||
Expect.isTrue(i > 1024 * 1024);
|
||||
server.close();
|
||||
return;
|
||||
}
|
||||
server.close();
|
||||
Expect.fail('An exception is expected');
|
||||
}
|
||||
|
||||
Future<void> main() async {
|
||||
await client();
|
||||
await server();
|
||||
}
|
81
tests/standalone_2/io/http_big_header_test.dart
Normal file
81
tests/standalone_2/io/http_big_header_test.dart
Normal file
|
@ -0,0 +1,81 @@
|
|||
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
// This test checks whether a big header is accepted.
|
||||
|
||||
Future<void> testClient(int limit) async {
|
||||
final server = await HttpServer.bind('127.0.0.1', 0);
|
||||
final str = 'a' * (1000);
|
||||
int size = 0;
|
||||
server.listen((request) async {
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
request.response.headers.add('dummy', str);
|
||||
size += 1000;
|
||||
if (size > limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
await request.response.close();
|
||||
server.close();
|
||||
});
|
||||
|
||||
final client = HttpClient();
|
||||
final request = await client.get('127.0.0.1', server.port, '/');
|
||||
await request.close();
|
||||
}
|
||||
|
||||
Future<void> client() async {
|
||||
int i = 64;
|
||||
try {
|
||||
for (; i < 101 * 1024 * 1024; i *= 100) {
|
||||
await testClient(i);
|
||||
}
|
||||
} on HttpException catch (e) {
|
||||
Expect.isTrue(e.toString().contains('size limit'));
|
||||
Expect.isTrue(i > 1024 * 1024);
|
||||
return;
|
||||
}
|
||||
Expect.fail('An exception is expected');
|
||||
}
|
||||
|
||||
Future<void> testServer(int limit, int port) async {
|
||||
final str = 'a' * (1000);
|
||||
final client = HttpClient();
|
||||
final request = await client.get('127.0.0.1', port, '/');
|
||||
for (int size = 0; size < limit; size += 1000) {
|
||||
request.headers.add('dummy', str);
|
||||
}
|
||||
await request.close();
|
||||
}
|
||||
|
||||
Future<void> server() async {
|
||||
final server = await HttpServer.bind('127.0.0.1', 0);
|
||||
int i = 64;
|
||||
try {
|
||||
server.listen((request) async {
|
||||
await request.response.close();
|
||||
});
|
||||
for (; i < 101 * 1024 * 1024; i *= 100) {
|
||||
print(i);
|
||||
await testServer(i, server.port);
|
||||
}
|
||||
} on SocketException catch (_) {
|
||||
// Server will close on error and writing to the socket will be blocked due
|
||||
// to broken pipe.
|
||||
Expect.isTrue(i > 1024 * 1024);
|
||||
server.close();
|
||||
return;
|
||||
}
|
||||
server.close();
|
||||
Expect.fail('An exception is expected');
|
||||
}
|
||||
|
||||
Future<void> main() async {
|
||||
await client();
|
||||
await server();
|
||||
}
|
Loading…
Reference in a new issue