Skip to content

Commit f6ac341

Browse files
authored
Merge branch 'master' into feat/cancellation
2 parents 6f7e849 + 4289e8b commit f6ac341

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+5333
-803
lines changed

.github/workflows/java.yml

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
name: package:java_http CI
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- master
8+
paths:
9+
- 'pkgs/java_http/**'
10+
- 'pkgs/http_client_conformance_tests/**'
11+
- '.github/workflows/java.yml'
12+
pull_request:
13+
paths:
14+
- 'pkgs/java_http/**'
15+
- 'pkgs/http_client_conformance_tests/**'
16+
- '.github/workflows/java.yml'
17+
schedule:
18+
# Runs every Sunday at midnight (00:00 UTC).
19+
- cron: "0 0 * * 0"
20+
21+
env:
22+
PUB_ENVIRONMENT: bot.github
23+
24+
jobs:
25+
analyze:
26+
name: Lint and static analysis
27+
runs-on: ubuntu-latest
28+
defaults:
29+
run:
30+
working-directory: pkgs/java_http
31+
steps:
32+
- uses: actions/checkout@v3
33+
- uses: subosito/flutter-action@v2
34+
with:
35+
channel: 'stable'
36+
37+
- id: install
38+
name: Install dependencies
39+
run: dart pub get
40+
41+
- name: Check formatting
42+
run: dart format --output=none --set-exit-if-changed .
43+
if: always() && steps.install.outcome == 'success'
44+
45+
- name: Analyze code
46+
run: dart analyze --fatal-infos
47+
if: always() && steps.install.outcome == 'success'
48+
49+
test:
50+
needs: analyze
51+
name: Build and test
52+
runs-on: ubuntu-latest
53+
defaults:
54+
run:
55+
working-directory: pkgs/java_http
56+
57+
steps:
58+
- uses: actions/checkout@v3
59+
- uses: subosito/flutter-action@v2
60+
with:
61+
channel: 'stable'
62+
63+
- name: Build jni dynamic libraries
64+
run: dart run jni:setup
65+
66+
- name: Run tests
67+
run: dart test

.github/workflows/pull_request_label.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
pull-requests: write
1717
runs-on: ubuntu-latest
1818
steps:
19-
- uses: actions/labeler@0776a679364a9a16110aac8d0f40f5e11009e327
19+
- uses: actions/labeler@0967ca812e7fdc8f5f71402a1b486d5bd061fe20
2020
with:
2121
repo-token: "${{ secrets.GITHUB_TOKEN }}"
2222
sync-labels: true

pkgs/cronet_http/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
## 0.2.2
22

33
* Require Dart 3.0
4+
* Throw `ClientException` when the `'Content-Length'` header is invalid.
45

56
## 0.2.1
67

pkgs/cronet_http/lib/src/cronet_client.dart

+14-4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import 'package:http/http.dart';
2121
import 'messages.dart' as messages;
2222

2323
final _api = messages.HttpApi();
24+
final _digitRegex = RegExp(r'^\d+$');
2425

2526
final Finalizer<String> _cronetEngineFinalizer = Finalizer(_api.freeEngine);
2627

@@ -266,11 +267,20 @@ class CronetClient extends BaseClient {
266267
.cast<String, List<Object?>>()
267268
.map((key, value) => MapEntry(key.toLowerCase(), value.join(',')));
268269

269-
final contentLengthHeader = responseHeaders['content-length'];
270+
int? contentLength;
271+
switch (responseHeaders['content-length']) {
272+
case final contentLengthHeader?
273+
when !_digitRegex.hasMatch(contentLengthHeader):
274+
throw ClientException(
275+
'Invalid content-length header [$contentLengthHeader].',
276+
request.url,
277+
);
278+
case final contentLengthHeader?:
279+
contentLength = int.parse(contentLengthHeader);
280+
}
281+
270282
return StreamedResponse(responseDataController.stream, result.statusCode,
271-
contentLength: contentLengthHeader == null
272-
? null
273-
: int.tryParse(contentLengthHeader),
283+
contentLength: contentLength,
274284
reasonPhrase: result.statusText,
275285
request: request,
276286
isRedirect: result.isRedirect,

pkgs/cronet_http/pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: cronet_http
22
description: >
33
An Android Flutter plugin that provides access to the Cronet HTTP client.
4-
version: 0.2.1
4+
version: 0.2.2
55
repository: https://github.com/dart-lang/http/tree/master/pkgs/cronet_http
66

77
environment:

pkgs/cupertino_http/CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
## 1.1.0
22

33
* Add websocket support to `cupertino_api`.
4+
* Add streaming upload support, i.e., if `CupertinoClient.send()` is called
5+
with a `StreamedRequest` then the data will be sent to the server
6+
incrementally.
7+
* Deprecate `Data.fromUint8List` in favor of `Data.fromList`, which accepts
8+
any `List<int>`.
9+
* Disable additional analyses for generated Objective-C bindings to prevent
10+
errors from `dart analyze`.
11+
* Throw `ClientException` when the `'Content-Length'` header is invalid.
412

513
## 1.0.1
614

pkgs/cupertino_http/analysis_options.yaml

-4
This file was deleted.

pkgs/cupertino_http/example/integration_test/client_conformance_test.dart

+2-3
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,11 @@ void main() {
1111
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
1212

1313
group('defaultSessionConfiguration', () {
14-
testAll(CupertinoClient.defaultSessionConfiguration,
15-
canStreamRequestBody: false);
14+
testAll(CupertinoClient.defaultSessionConfiguration);
1615
});
1716
group('fromSessionConfiguration', () {
1817
final config = URLSessionConfiguration.ephemeralSessionConfiguration();
1918
testAll(() => CupertinoClient.fromSessionConfiguration(config),
20-
canStreamRequestBody: false, canWorkInIsolates: false);
19+
canWorkInIsolates: false);
2120
});
2221
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:io';
6+
import 'dart:typed_data';
7+
8+
import 'package:convert/convert.dart';
9+
import 'package:crypto/crypto.dart';
10+
import 'package:cupertino_http/cupertino_http.dart';
11+
import 'package:flutter_test/flutter_test.dart';
12+
import 'package:http/http.dart';
13+
import 'package:integration_test/integration_test.dart';
14+
15+
void testClient(Client client) {
16+
group('client tests', () {
17+
late HttpServer server;
18+
late Uri uri;
19+
late List<int> serverHash;
20+
21+
setUp(() async {
22+
server = (await HttpServer.bind('localhost', 0))
23+
..listen((request) async {
24+
var hashSink = AccumulatorSink<Digest>();
25+
final hashConverter = sha1.startChunkedConversion(hashSink);
26+
await request.listen(hashConverter.add).asFuture<void>();
27+
hashConverter.close();
28+
serverHash = hashSink.events.single.bytes;
29+
await request.response.close();
30+
});
31+
uri = Uri.http('localhost:${server.port}');
32+
});
33+
tearDown(() {
34+
server.close();
35+
});
36+
37+
test('large single item stream', () async {
38+
// This tests that `CUPHTTPStreamToNSInputStreamAdapter` correctly
39+
// handles calls to `read:maxLength:` where the maximum length
40+
// is smaller than the amount of data in the buffer.
41+
final size = (Platform.isIOS ? 10 : 100) * 1024 * 1024;
42+
final data = Uint8List(size);
43+
for (var i = 0; i < data.length; ++i) {
44+
data[i] = i % 256;
45+
}
46+
final request = StreamedRequest('POST', uri);
47+
request.sink.add(data);
48+
request.sink.close();
49+
await client.send(request);
50+
expect(serverHash, sha1.convert(data).bytes);
51+
});
52+
});
53+
}
54+
55+
void main() {
56+
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
57+
58+
group('defaultSessionConfiguration', () {
59+
testClient(CupertinoClient.defaultSessionConfiguration());
60+
});
61+
group('fromSessionConfiguration', () {
62+
final config = URLSessionConfiguration.ephemeralSessionConfiguration();
63+
testClient(CupertinoClient.fromSessionConfiguration(config));
64+
});
65+
}

pkgs/cupertino_http/example/integration_test/data_test.dart

+5-5
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ void main() {
1212
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
1313

1414
group('empty data', () {
15-
final data = Data.fromUint8List(Uint8List(0));
15+
final data = Data.fromList(<int>[]);
1616

1717
test('length', () => expect(data.length, 0));
1818
test('bytes', () => expect(data.bytes, Uint8List(0)));
1919
test('toString', data.toString); // Just verify that there is no crash.
2020
});
2121

2222
group('non-empty data', () {
23-
final data = Data.fromUint8List(Uint8List.fromList([1, 2, 3, 4, 5]));
23+
final data = Data.fromList([1, 2, 3, 4, 5]);
2424
test('length', () => expect(data.length, 5));
2525
test(
2626
'bytes', () => expect(data.bytes, Uint8List.fromList([1, 2, 3, 4, 5])));
@@ -29,12 +29,12 @@ void main() {
2929

3030
group('Data.fromData', () {
3131
test('from empty', () {
32-
final from = Data.fromUint8List(Uint8List(0));
33-
expect(Data.fromData(from).bytes, Uint8List.fromList([]));
32+
final from = Data.fromList(<int>[]);
33+
expect(Data.fromData(from).bytes, Uint8List(0));
3434
});
3535

3636
test('from non-empty', () {
37-
final from = Data.fromUint8List(Uint8List.fromList([1, 2, 3, 4, 5]));
37+
final from = Data.fromList([1, 2, 3, 4, 5]);
3838
expect(Data.fromData(from).bytes, Uint8List.fromList([1, 2, 3, 4, 5]));
3939
});
4040
});

pkgs/cupertino_http/example/integration_test/error_test.dart

+27-5
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,37 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
@Skip('Error tests cannot currently be written. See comments in this file.')
6-
library;
7-
5+
import 'package:cupertino_http/cupertino_http.dart';
86
import 'package:integration_test/integration_test.dart';
97
import 'package:test/test.dart';
108

119
void main() {
1210
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
1311

14-
// TODO(https://github.com/dart-lang/ffigen/issues/386): Implement tests
15-
// when an initializer is callable.
12+
group('error', () {
13+
test('simple', () {
14+
final e = Error.fromCustomDomain('test domain', 123);
15+
expect(e.code, 123);
16+
expect(e.domain, 'test domain');
17+
expect(e.localizedDescription,
18+
allOf(contains('test domain'), contains('123')));
19+
expect(e.localizedFailureReason, null);
20+
expect(e.localizedRecoverySuggestion, null);
21+
expect(e.toString(), allOf(contains('test domain'), contains('123')));
22+
});
23+
24+
test('localized description', () {
25+
final e = Error.fromCustomDomain('test domain', 123,
26+
localizedDescription: 'This is a description');
27+
expect(e.code, 123);
28+
expect(e.domain, 'test domain');
29+
expect(e.localizedDescription, 'This is a description');
30+
expect(e.localizedFailureReason, null);
31+
expect(e.localizedRecoverySuggestion, null);
32+
expect(
33+
e.toString(),
34+
allOf(contains('test domain'), contains('123'),
35+
contains('This is a description')));
36+
});
37+
});
1638
}

pkgs/cupertino_http/example/integration_test/main.dart

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'package:integration_test/integration_test.dart';
66

77
import 'client_conformance_test.dart' as client_conformance_test;
8+
import 'client_test.dart' as client_test;
89
import 'data_test.dart' as data_test;
910
import 'error_test.dart' as error_test;
1011
import 'http_url_response_test.dart' as http_url_response_test;
@@ -27,6 +28,7 @@ void main() {
2728
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
2829

2930
client_conformance_test.main();
31+
client_test.main();
3032
data_test.main();
3133
error_test.main();
3234
http_url_response_test.main();

pkgs/cupertino_http/example/integration_test/mutable_data_test.dart

+3-4
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,15 @@ void main() {
2121

2222
group('appendBytes', () {
2323
test('append no bytes', () {
24-
final data = MutableData.empty()..appendBytes(Uint8List(0));
24+
final data = MutableData.empty()..appendBytes(<int>[]);
2525
expect(data.bytes, Uint8List(0));
2626
data.toString(); // Just verify that there is no crash.
2727
});
2828

2929
test('append some bytes', () {
30-
final data = MutableData.empty()
31-
..appendBytes(Uint8List.fromList([1, 2, 3, 4, 5]));
30+
final data = MutableData.empty()..appendBytes([1, 2, 3, 4, 5]);
3231
expect(data.bytes, Uint8List.fromList([1, 2, 3, 4, 5]));
33-
data.appendBytes(Uint8List.fromList([6, 7, 8, 9, 10]));
32+
data.appendBytes([6, 7, 8, 9, 10]);
3433
expect(data.bytes, Uint8List.fromList([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]));
3534
data.toString(); // Just verify that there is no crash.
3635
});

pkgs/cupertino_http/example/integration_test/mutable_url_request_test.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,13 @@ void main() {
4747

4848
test('empty', () => expect(request.httpBody, null));
4949
test('set', () {
50-
request.httpBody = Data.fromUint8List(Uint8List.fromList([1, 2, 3]));
50+
request.httpBody = Data.fromList([1, 2, 3]);
5151
expect(request.httpBody!.bytes, Uint8List.fromList([1, 2, 3]));
5252
request.toString(); // Just verify that there is no crash.
5353
});
5454
test('set to null', () {
5555
request
56-
..httpBody = Data.fromUint8List(Uint8List.fromList([1, 2, 3]))
56+
..httpBody = Data.fromList([1, 2, 3])
5757
..httpBody = null;
5858
expect(request.httpBody, null);
5959
});

pkgs/cupertino_http/example/integration_test/url_session_task_test.dart

+4-6
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import 'dart:io';
66

77
import 'package:cupertino_http/cupertino_http.dart';
8-
import 'package:flutter/foundation.dart';
98
import 'package:integration_test/integration_test.dart';
109
import 'package:test/test.dart';
1110

@@ -63,8 +62,7 @@ void testWebSocketTask() {
6362
await task
6463
.sendMessage(URLSessionWebSocketMessage.fromString('Hello World!'));
6564
await task.receiveMessage();
66-
task.cancelWithCloseCode(
67-
4998, Data.fromUint8List(Uint8List.fromList('Bye'.codeUnits)));
65+
task.cancelWithCloseCode(4998, Data.fromList('Bye'.codeUnits));
6866

6967
// Allow the server to run and save the close code.
7068
while (lastCloseCode == null) {
@@ -96,8 +94,8 @@ void testWebSocketTask() {
9694
final task = session.webSocketTaskWithRequest(
9795
URLRequest.fromUrl(Uri.parse('ws://localhost:${server.port}')))
9896
..resume();
99-
await task.sendMessage(URLSessionWebSocketMessage.fromData(
100-
Data.fromUint8List(Uint8List.fromList([1, 2, 3]))));
97+
await task.sendMessage(
98+
URLSessionWebSocketMessage.fromData(Data.fromList([1, 2, 3])));
10199
final receivedMessage = await task.receiveMessage();
102100
expect(receivedMessage.type,
103101
URLSessionWebSocketMessageType.urlSessionWebSocketMessageTypeData);
@@ -221,7 +219,7 @@ void testURLSessionTaskCommon(
221219
MutableURLRequest.fromUrl(
222220
Uri.parse('http://localhost:${server.port}/mypath'))
223221
..httpMethod = 'POST'
224-
..httpBody = Data.fromUint8List(Uint8List.fromList([1, 2, 3])))
222+
..httpBody = Data.fromList([1, 2, 3]))
225223
..prefersIncrementalDelivery = false
226224
..priority = 0.2
227225
..taskDescription = 'my task description'

0 commit comments

Comments
 (0)