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

Migrate Essentials v0.0.1: Implement Client with openUrl method #2

Merged
merged 42 commits into from
Jul 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
d96f235
adding dependencies, generated_bindings and android support
unsuitable001 Jun 11, 2021
4d3bb12
refactor: third_party APIs (cronet) seperated from wrapper.h
unsuitable001 Jun 12, 2021
e3ca8a8
initial implementation: essential APIs of http client, request & resp…
unsuitable001 Jun 12, 2021
b2253a3
refactor: comments & readme
unsuitable001 Jun 13, 2021
30cfbe6
refactor: docs, analysis, license, tests
unsuitable001 Jun 13, 2021
1ba712d
api change: registerCallback's future resolves `bool` and readDataCal…
unsuitable001 Jun 14, 2021
a218ac4
tests & bug fix: http response callback api
unsuitable001 Jun 15, 2021
d99c7c3
tests: automatic port selection
unsuitable001 Jun 15, 2021
c3da375
tests: add license and enforce 80char
unsuitable001 Jun 16, 2021
ea156b1
remove: flutter support boilerplate
unsuitable001 Jun 17, 2021
382be13
hint user to get binaries and relocate prepare_cronet
unsuitable001 Jun 17, 2021
82b296a
refactor: native code lib/src/native -> src and 3rd_party -> third_party
unsuitable001 Jun 18, 2021
a667deb
rewrite build.sh in dart
unsuitable001 Jun 18, 2021
7df5522
add build_cronet executable for user customization
unsuitable001 Jun 18, 2021
9e7836a
tar -> package:archive and cronet binary detection improvements
unsuitable001 Jun 19, 2021
5b921cf
info: display command to get cronet library when wrapper can't load it
unsuitable001 Jun 19, 2021
0303b71
test: remove unused imports
unsuitable001 Jun 19, 2021
5d4c698
refactor: wrapper.cc & improve: destroying unused requests
unsuitable001 Jun 20, 2021
088b9fb
include: cronet c++ sample
unsuitable001 Jun 21, 2021
c88867e
refactoring, added new tests and api comparisons
unsuitable001 Jun 23, 2021
f6ae319
move find_resource.dart -> third_party and fix readme
unsuitable001 Jun 24, 2021
5eba348
remove: alternate callback based API
unsuitable001 Jun 24, 2021
dd2490f
refactor: remove part directives
unsuitable001 Jun 24, 2021
f4cbd24
merge quic & http2 options into protocol using enum HttpProtocol
unsuitable001 Jun 25, 2021
12499c3
initial splitting of cronet and wrapper bindings
unsuitable001 Jun 25, 2021
783a6ce
spliting wrapper and cronet bindings
unsuitable001 Jun 27, 2021
96636e8
locate binaries to .dart_tool
unsuitable001 Jun 27, 2021
d3e4d84
formatting and sanity checks
unsuitable001 Jun 28, 2021
5958aad
add licence & minor refactors
unsuitable001 Jun 29, 2021
7533e4b
void* -> real type def for cronet fn & test: add regex
unsuitable001 Jun 29, 2021
f9a1e1e
setup script, cmake based build & set dylibs as global
unsuitable001 Jun 30, 2021
1d51146
analyzer: removed unused show from tool/build_wrapper.dart
unsuitable001 Jun 30, 2021
2f93cd3
ci: change ci setup command
unsuitable001 Jun 30, 2021
31de14b
CronetNativeException -> Error and enum to string mapping
unsuitable001 Jun 30, 2021
2ce6957
add verify option to cronet:setup, fix setup halting
unsuitable001 Jun 30, 2021
0beef32
C API for SampleExecutor
unsuitable001 Jul 1, 2021
4452dbe
tool: update tarballs. setup verify: delete verifier after done.
unsuitable001 Jul 1, 2021
1dcb370
readme: setup command, add: ffigen issue reference
unsuitable001 Jul 2, 2021
c50d7b3
readme: typo fix
unsuitable001 Jul 2, 2021
f222aa4
readme: supported platforms
unsuitable001 Jul 2, 2021
3de3374
readme
unsuitable001 Jul 2, 2021
ec2efb0
update: binary download url
unsuitable001 Jul 2, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/test-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ jobs:
- id: install
name: Install dependencies
run: dart pub get
- id: get_binaries
name: Download Cronet Binaries
run: dart run cronet:setup
- name: Run VM tests
run: dart test --platform vm
if: always() && steps.install.outcome == 'success'
35 changes: 35 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
build/
pubspec.lock

# cmake build directories
out

# Directory created by dartdoc.
doc/api/

Expand Down Expand Up @@ -33,3 +36,35 @@ compile_commands.json

# Mac
.DS_Store

# Cronet pre-built binaries
cronet_binaries/

# Generated shared libraries.
*.so
*.so.*
*.dylib
*.dll
*.lib

# Compiled executables.
*.o
*.out

# AOT compiled files.
*.exe

*.jar
*.cxx

*.flutter-plugins
*.flutter-plugins-dependencies

*.idea

# Directory for quick experiments.
experiments/

# Files generated by tests for debugging purposes.
test/debug_generated/*
!test/debug_generated/readme.md
10 changes: 10 additions & 0 deletions .metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.

version:
revision: 1d9032c7e1d867f071f2277eb1673e8f9b0274e3
channel: stable

project_type: plugin
2 changes: 2 additions & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@
# Name/Organization <email address>

Google LLC

Soumyadip Mondal <[email protected]>
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## 0.0.1

* HttpClient with QUIC, HTTP2, brotli support.
* HttpClient with a customizable user agent string.
* HttpClient close method (without force close).
* Implemented open, openUrl & other associated methods.
* Response is consumable using 2 styles of APIs. dart:io style and callback based style.
* Different types of Exceptions are implemented.

**Breaking Changes:**

* Custom `SecurityContext` is no longer handled by the client. Users have to handle it in other ways. (To be documented later).
* `userAgent` property is now read-only. Custom userAgent should be passed as a constructor argument.
71 changes: 71 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,74 @@
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.

This is a [GSoC 2021 project](https://summerofcode.withgoogle.com/projects/#4757095741652992).

## Supported Platforms

Currently, 64 bit Linux and Windows systems are supported.

## Usage

1. Add package as a dependency in your `pubspec.yaml`.

2. Run this from the `root` of your project.

**Dart CLI**

```bash
dart pub get
dart run cronet:setup # Downloads the cronet binaries.
```

3. Import

```dart
import 'package:cronet/cronet.dart';
```

**Note:** Internet connection is required to download cronet binaries.

## Example

```dart
final client = HttpClient();
client
.getUrl(Uri.parse('http://info.cern.ch/'))
.then((HttpClientRequest request) {
return request.close();
}).then((HttpClientResponse response) {
response.transform(utf8.decoder).listen((contents) {
print(contents);
},
onDone: () => print(
'Done!'));
});
```

[See the API comparison with `dart:io`.](dart_io_comparison.md)

## Run Example

```bash
cd example_dart
dart run cronet:setup # Downloads the cronet binaries.
dart run
```

## Run Tests

```bash
dart pub get
dart run cronet:setup # Downloads the cronet binaries.
dart test --platform vm
```

You can also verify your cronet binaries using `dart run cronet:setup verify`.
Make sure to have `cmake 3.15`.

## Building Your Own

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`.

2. Run `dart run cronet:setup build` from the root of your project.

**Note for Windows:** Run `step 2` from `x64 Native Tools Command Prompt for VS 2019` shell.
9 changes: 9 additions & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,12 @@
# BSD-style license that can be found in the LICENSE file.

include: package:lints/recommended.yaml

analyzer:
strong-mode:
implicit-casts: false
implicit-dynamic: false
linter:
rules:
- directives_ordering
- lines_longer_than_80_chars
192 changes: 192 additions & 0 deletions bin/setup.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
// Copyright (c) 2021, 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:archive/archive.dart';
import 'package:cli_util/cli_logging.dart' show Ansi, Logger;
import 'package:cronet/src/constants.dart';
import 'package:cronet/src/third_party/ffigen/find_resource.dart';

// Extracts a tar.gz file.
void extract(String fileName, [String dir = '']) {
final tarGzFile = File(fileName).readAsBytesSync();
final archive = GZipDecoder().decodeBytes(tarGzFile, verify: true);
final tarData = TarDecoder().decodeBytes(archive, verify: true);
for (final file in tarData) {
final filename = file.name;
if (file.isFile) {
final data = file.content as List<int>;
File(dir + filename)
..createSync(recursive: true)
..writeAsBytesSync(data);
} else {
Directory(dir + filename).createSync(recursive: true);
}
}
}

/// Places downloaded binaries to proper location.
void placeBinaries(String platform, String fileName) {
final logger = Logger.standard();
final ansi = Ansi(Ansi.terminalSupportsAnsi);
logger.stdout('${ansi.yellow}Extracting Cronet for $platform${ansi.none}');
Directory(binaryStorageDir).createSync(recursive: true);
extract(fileName, binaryStorageDir);
logger.stdout('Done! Cleaning up...');

File(fileName).deleteSync();
logger.stdout(
'${ansi.green}Done! Cronet support for $platform is now available!'
'${ansi.none}');
}

/// Download `cronet` library from Github Releases.
Future<void> downloadCronetBinaries(String platform) async {
final logger = Logger.standard();
final ansi = Ansi(Ansi.terminalSupportsAnsi);
if (!isCronetAvailable(platform)) {
final fileName = platform + '.tar.gz';
logger.stdout('Downloading Cronet for $platform');
final downloadUrl = cronetBinaryUrl + fileName;
logger.stdout(downloadUrl);
try {
final httpClient = HttpClient();
final request = await httpClient.getUrl(Uri.parse(downloadUrl));
final response = await request.close();
final fileSink = File(fileName).openWrite();
await response.pipe(fileSink);
await fileSink.flush();
await fileSink.close();
httpClient.close();
} catch (error) {
Exception("Can't download. Check your network connection!");
}

placeBinaries(platform, fileName);
} else {
logger.stdout('${ansi.yellow}Cronet $platform is already available.'
' No need to download.${ansi.none}');
}
}

/// Builds wrapper from source for the current platform.
void buildWrapper() {
final logger = Logger.standard();
final ansi = Ansi(Ansi.terminalSupportsAnsi);
final wrapperPath = wrapperSourcePath();
final pwd = Directory.current;
Directory.current = Directory(wrapperPath);
logger.stdout('Building Wrapper...');
try {
final result = Process.runSync(
'cmake', ['CMakeLists.txt', '-B', 'out/${Platform.operatingSystem}']);
print(result.stdout);
print(result.stderr);
} catch (error) {
Directory.current = pwd;
logger.stdout("${ansi.red}Build failed.${ansi.none}");
if (Platform.isWindows) {
logger.stdout(
'Open ${ansi.yellow}x64 Native Tools Command Prompt for VS 2019.'
'${ansi.none} Then run:\n'
'cd ${pwd.path}\ndart run cronet:setup build');
}
return;
}
var result =
Process.runSync('cmake', ['--build', 'out/${Platform.operatingSystem}']);
print(result.stdout);
print(result.stderr);
if (result.exitCode != 0) return;
Directory.current = pwd;
final moveLocation = '$binaryStorageDir${Platform.operatingSystem}64';
Directory(moveLocation).createSync(recursive: true);
final buildOutputPath = Platform.isLinux
? '$wrapperPath/out/${Platform.operatingSystem}/${getWrapperName()}'
: '$wrapperPath\\out\\${Platform.operatingSystem}\\Debug\\${getWrapperName()}';
File(buildOutputPath).copySync('$moveLocation/${getWrapperName()}');
logger.stdout(
'${ansi.green}Wrapper moved to $moveLocation. Success!${ansi.none}');
return;
}

/// Verify if cronet binary is working correctly.
void verifyCronetBinary() {
final logger = Logger.standard();
final ansi = Ansi(Ansi.terminalSupportsAnsi);
final sampleSource = findPackageRoot()
.resolve('third_party/cronet_sample')
.toFilePath(windows: Platform.isWindows);
final buildName = Platform.isLinux ? 'cronet_sample' : 'cronet_sample.exe';
final pwd = Directory.current;
if (!isCronetAvailable('${Platform.operatingSystem}64')) {
logger.stderr('${ansi.red}Cronet binaries are not available.${ansi.none}');
logger.stdout('Get the cronet binaries by running: dart run cronet:setup');
return;
}

logger.stdout('Building Sample...');
var result = Process.runSync('cmake', [
'$sampleSource/CMakeLists.txt',
'-B',
'$sampleSource/out/${Platform.operatingSystem}'
], environment: {
'CURRENTDIR': pwd.path
});
print(result.stdout);
print(result.stderr);
result = Process.runSync(
'cmake', ['--build', '$sampleSource/out/${Platform.operatingSystem}'],
environment: {'CURRENTDIR': pwd.path});
print(result.stdout);
print(result.stderr);
final buildOutputPath = Platform.isLinux
? '$sampleSource/out/${Platform.operatingSystem}/$buildName'
: '$sampleSource\\out\\${Platform.operatingSystem}\\Debug\\$buildName';

logger.stdout('Copying...');
final sample = File(buildOutputPath)
.copySync('.dart_tool/cronet/${Platform.operatingSystem}64/$buildName');

logger.stdout('Verifying...');
result = Process.runSync(
'.dart_tool/cronet/${Platform.operatingSystem}64/$buildName', []);
if (result.exitCode == 0) {
logger.stdout('${ansi.green}Verified! Cronet is working fine.${ansi.none}');
} else {
logger.stderr('${ansi.red}Verification failed!${ansi.none}');
}
sample.deleteSync();
}

Future<void> main(List<String> args) async {
const docStr = """
dart run cronet:setup [option]
Downloads the cronet binaries.\n
clean\tClean downloaded or built binaries.
build\tBuilds the wrapper. Requires cmake.
verify\tVerifies the cronet binary.
""";
final logger = Logger.standard();
if (args.length > 1) {
logger.stderr('Expected 1 argument only.');
logger.stdout(docStr);
} else if (args.contains('-h')) {
logger.stdout(docStr);
} else if (args.contains('clean')) {
logger.stdout('cleaning...');
Directory(binaryStorageDir).deleteSync(recursive: true);
logger.stdout('Done!');
} else if (args.contains('build')) {
buildWrapper();
} else if (args.contains('verify')) {
verifyCronetBinary();
} else {
// Targeting only 64bit OS. (At least for the time being.)
if (validPlatforms.contains('${Platform.operatingSystem}64')) {
await downloadCronetBinaries('${Platform.operatingSystem}64');
}
}
}
18 changes: 18 additions & 0 deletions dart_io_comparison.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Comparing APIs with dart:io

## userAgent

### dart:io

```dart
final client = HttpClient();
client.userAgent = 'myUA/1.0'; // using custom UA string.
print(client.userAgent); // Will print myUA/1.0.
```

### Cronet

```dart
final client = HttpClient(userAgent: 'myUA/1.0'); // using custom UA string.
print(client.userAgent); // Will print myUA/1.0.
```
Loading