Skip to content
This repository was archived by the owner on Feb 11, 2024. It is now read-only.

Commit c9ef18a

Browse files
Migrate Essentials v0.0.1: Implement Client with openUrl method (#2)
1 parent 1b02c1e commit c9ef18a

Some content is hidden

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

65 files changed

+27054
-15
lines changed

.github/workflows/test-package.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ jobs:
6060
- id: install
6161
name: Install dependencies
6262
run: dart pub get
63+
- id: get_binaries
64+
name: Download Cronet Binaries
65+
run: dart run cronet:setup
6366
- name: Run VM tests
6467
run: dart test --platform vm
6568
if: always() && steps.install.outcome == 'success'

.gitignore

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
build/
77
pubspec.lock
88

9+
# cmake build directories
10+
out
11+
912
# Directory created by dartdoc.
1013
doc/api/
1114

@@ -33,3 +36,35 @@ compile_commands.json
3336

3437
# Mac
3538
.DS_Store
39+
40+
# Cronet pre-built binaries
41+
cronet_binaries/
42+
43+
# Generated shared libraries.
44+
*.so
45+
*.so.*
46+
*.dylib
47+
*.dll
48+
*.lib
49+
50+
# Compiled executables.
51+
*.o
52+
*.out
53+
54+
# AOT compiled files.
55+
*.exe
56+
57+
*.jar
58+
*.cxx
59+
60+
*.flutter-plugins
61+
*.flutter-plugins-dependencies
62+
63+
*.idea
64+
65+
# Directory for quick experiments.
66+
experiments/
67+
68+
# Files generated by tests for debugging purposes.
69+
test/debug_generated/*
70+
!test/debug_generated/readme.md

.metadata

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# This file tracks properties of this Flutter project.
2+
# Used by Flutter tool to assess capabilities and perform upgrades etc.
3+
#
4+
# This file should be version controlled and should not be manually edited.
5+
6+
version:
7+
revision: 1d9032c7e1d867f071f2277eb1673e8f9b0274e3
8+
channel: stable
9+
10+
project_type: plugin

AUTHORS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@
44
# Name/Organization <email address>
55

66
Google LLC
7+
8+
Soumyadip Mondal <[email protected]>

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
## 0.0.1
2+
3+
* HttpClient with QUIC, HTTP2, brotli support.
4+
* HttpClient with a customizable user agent string.
5+
* HttpClient close method (without force close).
6+
* Implemented open, openUrl & other associated methods.
7+
* Response is consumable using 2 styles of APIs. dart:io style and callback based style.
8+
* Different types of Exceptions are implemented.
9+
10+
**Breaking Changes:**
11+
12+
* Custom `SecurityContext` is no longer handled by the client. Users have to handle it in other ways. (To be documented later).
13+
* `userAgent` property is now read-only. Custom userAgent should be passed as a constructor argument.

README.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,74 @@
33
This package binds to Cronet's [native API](https://chromium.googlesource.com/chromium/src/+/master/components/cronet/native/test_instructions.md) to expose them in Dart.
44

55
This is a [GSoC 2021 project](https://summerofcode.withgoogle.com/projects/#4757095741652992).
6+
7+
## Supported Platforms
8+
9+
Currently, 64 bit Linux and Windows systems are supported.
10+
11+
## Usage
12+
13+
1. Add package as a dependency in your `pubspec.yaml`.
14+
15+
2. Run this from the `root` of your project.
16+
17+
**Dart CLI**
18+
19+
```bash
20+
dart pub get
21+
dart run cronet:setup # Downloads the cronet binaries.
22+
```
23+
24+
3. Import
25+
26+
```dart
27+
import 'package:cronet/cronet.dart';
28+
```
29+
30+
**Note:** Internet connection is required to download cronet binaries.
31+
32+
## Example
33+
34+
```dart
35+
final client = HttpClient();
36+
client
37+
.getUrl(Uri.parse('http://info.cern.ch/'))
38+
.then((HttpClientRequest request) {
39+
return request.close();
40+
}).then((HttpClientResponse response) {
41+
response.transform(utf8.decoder).listen((contents) {
42+
print(contents);
43+
},
44+
onDone: () => print(
45+
'Done!'));
46+
});
47+
```
48+
49+
[See the API comparison with `dart:io`.](dart_io_comparison.md)
50+
51+
## Run Example
52+
53+
```bash
54+
cd example_dart
55+
dart run cronet:setup # Downloads the cronet binaries.
56+
dart run
57+
```
58+
59+
## Run Tests
60+
61+
```bash
62+
dart pub get
63+
dart run cronet:setup # Downloads the cronet binaries.
64+
dart test --platform vm
65+
```
66+
67+
You can also verify your cronet binaries using `dart run cronet:setup verify`.
68+
Make sure to have `cmake 3.15`.
69+
70+
## Building Your Own
71+
72+
1. Make sure you've downloaded your custom version of cronet shared library and filename follows the pattern `cronet.86.0.4240.198.<extension>` with a prefix `lib` if on `linux`. Else, you can build cronet from [source](https://www.chromium.org/developers/how-tos/get-the-code) using the [provided instuctions](https://chromium.googlesource.com/chromium/src/+/master/components/cronet/build_instructions.md). Then copy the library to the designated folder. For linux, the files are under `.dart_tool/cronet/linux64`.
73+
74+
2. Run `dart run cronet:setup build` from the root of your project.
75+
76+
**Note for Windows:** Run `step 2` from `x64 Native Tools Command Prompt for VS 2019` shell.

analysis_options.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,12 @@
33
# BSD-style license that can be found in the LICENSE file.
44

55
include: package:lints/recommended.yaml
6+
7+
analyzer:
8+
strong-mode:
9+
implicit-casts: false
10+
implicit-dynamic: false
11+
linter:
12+
rules:
13+
- directives_ordering
14+
- lines_longer_than_80_chars

bin/setup.dart

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
// Copyright (c) 2021, 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+
7+
import 'package:archive/archive.dart';
8+
import 'package:cli_util/cli_logging.dart' show Ansi, Logger;
9+
import 'package:cronet/src/constants.dart';
10+
import 'package:cronet/src/third_party/ffigen/find_resource.dart';
11+
12+
// Extracts a tar.gz file.
13+
void extract(String fileName, [String dir = '']) {
14+
final tarGzFile = File(fileName).readAsBytesSync();
15+
final archive = GZipDecoder().decodeBytes(tarGzFile, verify: true);
16+
final tarData = TarDecoder().decodeBytes(archive, verify: true);
17+
for (final file in tarData) {
18+
final filename = file.name;
19+
if (file.isFile) {
20+
final data = file.content as List<int>;
21+
File(dir + filename)
22+
..createSync(recursive: true)
23+
..writeAsBytesSync(data);
24+
} else {
25+
Directory(dir + filename).createSync(recursive: true);
26+
}
27+
}
28+
}
29+
30+
/// Places downloaded binaries to proper location.
31+
void placeBinaries(String platform, String fileName) {
32+
final logger = Logger.standard();
33+
final ansi = Ansi(Ansi.terminalSupportsAnsi);
34+
logger.stdout('${ansi.yellow}Extracting Cronet for $platform${ansi.none}');
35+
Directory(binaryStorageDir).createSync(recursive: true);
36+
extract(fileName, binaryStorageDir);
37+
logger.stdout('Done! Cleaning up...');
38+
39+
File(fileName).deleteSync();
40+
logger.stdout(
41+
'${ansi.green}Done! Cronet support for $platform is now available!'
42+
'${ansi.none}');
43+
}
44+
45+
/// Download `cronet` library from Github Releases.
46+
Future<void> downloadCronetBinaries(String platform) async {
47+
final logger = Logger.standard();
48+
final ansi = Ansi(Ansi.terminalSupportsAnsi);
49+
if (!isCronetAvailable(platform)) {
50+
final fileName = platform + '.tar.gz';
51+
logger.stdout('Downloading Cronet for $platform');
52+
final downloadUrl = cronetBinaryUrl + fileName;
53+
logger.stdout(downloadUrl);
54+
try {
55+
final httpClient = HttpClient();
56+
final request = await httpClient.getUrl(Uri.parse(downloadUrl));
57+
final response = await request.close();
58+
final fileSink = File(fileName).openWrite();
59+
await response.pipe(fileSink);
60+
await fileSink.flush();
61+
await fileSink.close();
62+
httpClient.close();
63+
} catch (error) {
64+
Exception("Can't download. Check your network connection!");
65+
}
66+
67+
placeBinaries(platform, fileName);
68+
} else {
69+
logger.stdout('${ansi.yellow}Cronet $platform is already available.'
70+
' No need to download.${ansi.none}');
71+
}
72+
}
73+
74+
/// Builds wrapper from source for the current platform.
75+
void buildWrapper() {
76+
final logger = Logger.standard();
77+
final ansi = Ansi(Ansi.terminalSupportsAnsi);
78+
final wrapperPath = wrapperSourcePath();
79+
final pwd = Directory.current;
80+
Directory.current = Directory(wrapperPath);
81+
logger.stdout('Building Wrapper...');
82+
try {
83+
final result = Process.runSync(
84+
'cmake', ['CMakeLists.txt', '-B', 'out/${Platform.operatingSystem}']);
85+
print(result.stdout);
86+
print(result.stderr);
87+
} catch (error) {
88+
Directory.current = pwd;
89+
logger.stdout("${ansi.red}Build failed.${ansi.none}");
90+
if (Platform.isWindows) {
91+
logger.stdout(
92+
'Open ${ansi.yellow}x64 Native Tools Command Prompt for VS 2019.'
93+
'${ansi.none} Then run:\n'
94+
'cd ${pwd.path}\ndart run cronet:setup build');
95+
}
96+
return;
97+
}
98+
var result =
99+
Process.runSync('cmake', ['--build', 'out/${Platform.operatingSystem}']);
100+
print(result.stdout);
101+
print(result.stderr);
102+
if (result.exitCode != 0) return;
103+
Directory.current = pwd;
104+
final moveLocation = '$binaryStorageDir${Platform.operatingSystem}64';
105+
Directory(moveLocation).createSync(recursive: true);
106+
final buildOutputPath = Platform.isLinux
107+
? '$wrapperPath/out/${Platform.operatingSystem}/${getWrapperName()}'
108+
: '$wrapperPath\\out\\${Platform.operatingSystem}\\Debug\\${getWrapperName()}';
109+
File(buildOutputPath).copySync('$moveLocation/${getWrapperName()}');
110+
logger.stdout(
111+
'${ansi.green}Wrapper moved to $moveLocation. Success!${ansi.none}');
112+
return;
113+
}
114+
115+
/// Verify if cronet binary is working correctly.
116+
void verifyCronetBinary() {
117+
final logger = Logger.standard();
118+
final ansi = Ansi(Ansi.terminalSupportsAnsi);
119+
final sampleSource = findPackageRoot()
120+
.resolve('third_party/cronet_sample')
121+
.toFilePath(windows: Platform.isWindows);
122+
final buildName = Platform.isLinux ? 'cronet_sample' : 'cronet_sample.exe';
123+
final pwd = Directory.current;
124+
if (!isCronetAvailable('${Platform.operatingSystem}64')) {
125+
logger.stderr('${ansi.red}Cronet binaries are not available.${ansi.none}');
126+
logger.stdout('Get the cronet binaries by running: dart run cronet:setup');
127+
return;
128+
}
129+
130+
logger.stdout('Building Sample...');
131+
var result = Process.runSync('cmake', [
132+
'$sampleSource/CMakeLists.txt',
133+
'-B',
134+
'$sampleSource/out/${Platform.operatingSystem}'
135+
], environment: {
136+
'CURRENTDIR': pwd.path
137+
});
138+
print(result.stdout);
139+
print(result.stderr);
140+
result = Process.runSync(
141+
'cmake', ['--build', '$sampleSource/out/${Platform.operatingSystem}'],
142+
environment: {'CURRENTDIR': pwd.path});
143+
print(result.stdout);
144+
print(result.stderr);
145+
final buildOutputPath = Platform.isLinux
146+
? '$sampleSource/out/${Platform.operatingSystem}/$buildName'
147+
: '$sampleSource\\out\\${Platform.operatingSystem}\\Debug\\$buildName';
148+
149+
logger.stdout('Copying...');
150+
final sample = File(buildOutputPath)
151+
.copySync('.dart_tool/cronet/${Platform.operatingSystem}64/$buildName');
152+
153+
logger.stdout('Verifying...');
154+
result = Process.runSync(
155+
'.dart_tool/cronet/${Platform.operatingSystem}64/$buildName', []);
156+
if (result.exitCode == 0) {
157+
logger.stdout('${ansi.green}Verified! Cronet is working fine.${ansi.none}');
158+
} else {
159+
logger.stderr('${ansi.red}Verification failed!${ansi.none}');
160+
}
161+
sample.deleteSync();
162+
}
163+
164+
Future<void> main(List<String> args) async {
165+
const docStr = """
166+
dart run cronet:setup [option]
167+
Downloads the cronet binaries.\n
168+
clean\tClean downloaded or built binaries.
169+
build\tBuilds the wrapper. Requires cmake.
170+
verify\tVerifies the cronet binary.
171+
""";
172+
final logger = Logger.standard();
173+
if (args.length > 1) {
174+
logger.stderr('Expected 1 argument only.');
175+
logger.stdout(docStr);
176+
} else if (args.contains('-h')) {
177+
logger.stdout(docStr);
178+
} else if (args.contains('clean')) {
179+
logger.stdout('cleaning...');
180+
Directory(binaryStorageDir).deleteSync(recursive: true);
181+
logger.stdout('Done!');
182+
} else if (args.contains('build')) {
183+
buildWrapper();
184+
} else if (args.contains('verify')) {
185+
verifyCronetBinary();
186+
} else {
187+
// Targeting only 64bit OS. (At least for the time being.)
188+
if (validPlatforms.contains('${Platform.operatingSystem}64')) {
189+
await downloadCronetBinaries('${Platform.operatingSystem}64');
190+
}
191+
}
192+
}

dart_io_comparison.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Comparing APIs with dart:io
2+
3+
## userAgent
4+
5+
### dart:io
6+
7+
```dart
8+
final client = HttpClient();
9+
client.userAgent = 'myUA/1.0'; // using custom UA string.
10+
print(client.userAgent); // Will print myUA/1.0.
11+
```
12+
13+
### Cronet
14+
15+
```dart
16+
final client = HttpClient(userAgent: 'myUA/1.0'); // using custom UA string.
17+
print(client.userAgent); // Will print myUA/1.0.
18+
```

0 commit comments

Comments
 (0)