diff --git a/.github/labeler.yml b/.github/labeler.yml index 7ac52ce..b49eaf2 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml
@@ -1,6 +1,6 @@ -# Configuration for .github/workflows/pull_request_label.yml. +# Configuration for .github/workflows/pull_request_label.yml. -'infra': +'type-infra': - '.github/**' 'package:cronet_http':
diff --git a/.github/workflows/cronet.yml b/.github/workflows/cronet.yml index 94fcf1c..0976a75 100644 --- a/.github/workflows/cronet.yml +++ b/.github/workflows/cronet.yml
@@ -6,10 +6,12 @@ - main - master paths: + - '.github/workflows/cronet.yaml' - 'pkgs/cronet_http/**' - 'pkgs/http_client_conformance_tests/**' pull_request: paths: + - '.github/workflows/cronet.yaml' - 'pkgs/cronet_http/**' - 'pkgs/http_client_conformance_tests/**' schedule: @@ -19,48 +21,48 @@ PUB_ENVIRONMENT: bot.github jobs: - analyze: - name: Lint and static analysis - runs-on: ubuntu-latest - defaults: - run: - working-directory: pkgs/cronet_http - steps: - - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v2 - with: - # TODO: Change to 'stable' when a release version of flutter - # pins version 1.21.1 or later of 'package:test' - channel: 'master' - - id: install - name: Install dependencies - run: flutter pub get - - name: Check formatting - run: dart format --output=none --set-exit-if-changed . - if: always() && steps.install.outcome == 'success' - - name: Analyze code - run: flutter analyze --fatal-infos - if: always() && steps.install.outcome == 'success' - - test: - # Test package:cupertino_http use flutter integration tests. - needs: analyze - name: "Build and test" + verify: + name: Format & Analyze & Test runs-on: macos-latest + strategy: + matrix: + package: ['cronet_http', 'cronet_http_embedded'] steps: - uses: actions/checkout@v4 - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: '17' - uses: subosito/flutter-action@v2 with: channel: 'stable' + - name: Make cronet_http_embedded copy + if: ${{ matrix.package == 'cronet_http_embedded' }} + run: | + cp -r pkgs/cronet_http pkgs/cronet_http_embedded + cd pkgs/cronet_http_embedded + flutter pub get && dart tool/prepare_for_embedded.dart + - id: install + name: Install dependencies + working-directory: 'pkgs/${{ matrix.package }}' + run: flutter pub get + - name: Check formatting + working-directory: 'pkgs/${{ matrix.package }}' + run: dart format --output=none --set-exit-if-changed . + if: always() && steps.install.outcome == 'success' + - name: Analyze code + working-directory: 'pkgs/${{ matrix.package }}' + run: flutter analyze --fatal-infos + if: always() && steps.install.outcome == 'success' - name: Run tests uses: reactivecircus/android-emulator-runner@v2 + if: always() && steps.install.outcome == 'success' with: + # api-level/minSdkVersion should be help in sync in: + # - .github/workflows/cronet.yml + # - pkgs/cronet_http/android/build.gradle + # - pkgs/cronet_http/example/android/app/build.gradle api-level: 28 - target: playstore - arch: x86_64 + target: ${{ matrix.package == 'cronet_http_embedded' && 'google_apis' || 'playstore' }} profile: pixel - script: cd ./pkgs/cronet_http/example && flutter test --timeout=1200s integration_test/ + script: cd 'pkgs/${{ matrix.package }}/example' && flutter test --timeout=1200s integration_test/
diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index 35af167..3cb8d19 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml
@@ -1,4 +1,4 @@ -# Created with package:mono_repo v6.6.0 +# Created with package:mono_repo v6.6.1 name: Dart CI on: push: @@ -36,27 +36,27 @@ name: Checkout repository uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 - name: mono_repo self validate - run: dart pub global activate mono_repo 6.6.0 + run: dart pub global activate mono_repo 6.6.1 - name: mono_repo self validate run: dart pub global run mono_repo generate --validate job_002: - name: "analyze_and_format; linux; Dart 2.19.0; PKG: pkgs/http_client_conformance_tests; `dart analyze --fatal-infos`" + name: "analyze_and_format; linux; Dart 3.0.0; PKGS: pkgs/http_client_conformance_tests, pkgs/http_profile; `dart analyze --fatal-infos`" runs-on: ubuntu-latest steps: - name: Cache Pub hosted dependencies uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:2.19.0;packages:pkgs/http_client_conformance_tests;commands:analyze_1" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.0.0;packages:pkgs/http_client_conformance_tests-pkgs/http_profile;commands:analyze_1" restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:2.19.0;packages:pkgs/http_client_conformance_tests - os:ubuntu-latest;pub-cache-hosted;sdk:2.19.0 + os:ubuntu-latest;pub-cache-hosted;sdk:3.0.0;packages:pkgs/http_client_conformance_tests-pkgs/http_profile + os:ubuntu-latest;pub-cache-hosted;sdk:3.0.0 os:ubuntu-latest;pub-cache-hosted os:ubuntu-latest - name: Setup Dart SDK uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d with: - sdk: "2.19.0" + sdk: "3.0.0" - id: checkout name: Checkout repository uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 @@ -69,24 +69,33 @@ run: dart analyze --fatal-infos if: "always() && steps.pkgs_http_client_conformance_tests_pub_upgrade.conclusion == 'success'" working-directory: pkgs/http_client_conformance_tests + - id: pkgs_http_profile_pub_upgrade + name: pkgs/http_profile; dart pub upgrade + run: dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/http_profile + - name: "pkgs/http_profile; dart analyze --fatal-infos" + run: dart analyze --fatal-infos + if: "always() && steps.pkgs_http_profile_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/http_profile job_003: - name: "analyze_and_format; linux; Dart 3.0.0; PKGS: pkgs/http, pkgs/http_profile; `dart analyze --fatal-infos`" + name: "analyze_and_format; linux; Dart 3.2.0; PKG: pkgs/http; `dart analyze --fatal-infos`" runs-on: ubuntu-latest steps: - name: Cache Pub hosted dependencies uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.0.0;packages:pkgs/http-pkgs/http_profile;commands:analyze_1" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.2.0;packages:pkgs/http;commands:analyze_1" restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:3.0.0;packages:pkgs/http-pkgs/http_profile - os:ubuntu-latest;pub-cache-hosted;sdk:3.0.0 + os:ubuntu-latest;pub-cache-hosted;sdk:3.2.0;packages:pkgs/http + os:ubuntu-latest;pub-cache-hosted;sdk:3.2.0 os:ubuntu-latest;pub-cache-hosted os:ubuntu-latest - name: Setup Dart SDK uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d with: - sdk: "3.0.0" + sdk: "3.2.0" - id: checkout name: Checkout repository uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 @@ -99,15 +108,6 @@ run: dart analyze --fatal-infos if: "always() && steps.pkgs_http_pub_upgrade.conclusion == 'success'" working-directory: pkgs/http - - id: pkgs_http_profile_pub_upgrade - name: pkgs/http_profile; dart pub upgrade - run: dart pub upgrade - if: "always() && steps.checkout.conclusion == 'success'" - working-directory: pkgs/http_profile - - name: "pkgs/http_profile; dart analyze --fatal-infos" - run: dart analyze --fatal-infos - if: "always() && steps.pkgs_http_profile_pub_upgrade.conclusion == 'success'" - working-directory: pkgs/http_profile job_004: name: "analyze_and_format; linux; Dart dev; PKGS: pkgs/http, pkgs/http_client_conformance_tests, pkgs/http_profile; `dart analyze --fatal-infos`" runs-on: ubuntu-latest @@ -265,16 +265,16 @@ if: "always() && steps.pkgs_flutter_http_example_pub_upgrade.conclusion == 'success'" working-directory: pkgs/flutter_http_example job_008: - name: "unit_test; linux; Dart 3.0.0; PKG: pkgs/http; `dart run --define=no_default_http_client=true test/no_default_http_client_test.dart`" + name: "unit_test; linux; Dart 3.0.0; PKG: pkgs/http_profile; `dart test --platform vm`" runs-on: ubuntu-latest steps: - name: Cache Pub hosted dependencies uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.0.0;packages:pkgs/http;commands:command_1" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.0.0;packages:pkgs/http_profile;commands:test_2" restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:3.0.0;packages:pkgs/http + os:ubuntu-latest;pub-cache-hosted;sdk:3.0.0;packages:pkgs/http_profile os:ubuntu-latest;pub-cache-hosted;sdk:3.0.0 os:ubuntu-latest;pub-cache-hosted os:ubuntu-latest @@ -285,6 +285,44 @@ - id: checkout name: Checkout repository uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 + - id: pkgs_http_profile_pub_upgrade + name: pkgs/http_profile; dart pub upgrade + run: dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/http_profile + - name: "pkgs/http_profile; dart test --platform vm" + run: dart test --platform vm + if: "always() && steps.pkgs_http_profile_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/http_profile + needs: + - job_001 + - job_002 + - job_003 + - job_004 + - job_005 + - job_006 + - job_007 + job_009: + name: "unit_test; linux; Dart 3.2.0; PKG: pkgs/http; `dart run --define=no_default_http_client=true test/no_default_http_client_test.dart`" + runs-on: ubuntu-latest + steps: + - name: Cache Pub hosted dependencies + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 + with: + path: "~/.pub-cache/hosted" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.2.0;packages:pkgs/http;commands:command_1" + restore-keys: | + os:ubuntu-latest;pub-cache-hosted;sdk:3.2.0;packages:pkgs/http + os:ubuntu-latest;pub-cache-hosted;sdk:3.2.0 + os:ubuntu-latest;pub-cache-hosted + os:ubuntu-latest + - name: Setup Dart SDK + uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d + with: + sdk: "3.2.0" + - id: checkout + name: Checkout repository + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 - id: pkgs_http_pub_upgrade name: pkgs/http; dart pub upgrade run: dart pub upgrade @@ -302,24 +340,24 @@ - job_005 - job_006 - job_007 - job_009: - name: "unit_test; linux; Dart 3.0.0; PKG: pkgs/http; `dart test --platform chrome`" + job_010: + name: "unit_test; linux; Dart 3.2.0; PKG: pkgs/http; `dart test --platform chrome`" runs-on: ubuntu-latest steps: - name: Cache Pub hosted dependencies uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.0.0;packages:pkgs/http;commands:test_3" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.2.0;packages:pkgs/http;commands:test_3" restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:3.0.0;packages:pkgs/http - os:ubuntu-latest;pub-cache-hosted;sdk:3.0.0 + os:ubuntu-latest;pub-cache-hosted;sdk:3.2.0;packages:pkgs/http + os:ubuntu-latest;pub-cache-hosted;sdk:3.2.0 os:ubuntu-latest;pub-cache-hosted os:ubuntu-latest - name: Setup Dart SDK uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d with: - sdk: "3.0.0" + sdk: "3.2.0" - id: checkout name: Checkout repository uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 @@ -340,24 +378,24 @@ - job_005 - job_006 - job_007 - job_010: - name: "unit_test; linux; Dart 3.0.0; PKGS: pkgs/http, pkgs/http_profile; `dart test --platform vm`" + job_011: + name: "unit_test; linux; Dart 3.2.0; PKG: pkgs/http; `dart test --platform vm`" runs-on: ubuntu-latest steps: - name: Cache Pub hosted dependencies uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.0.0;packages:pkgs/http-pkgs/http_profile;commands:test_2" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.2.0;packages:pkgs/http;commands:test_2" restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:3.0.0;packages:pkgs/http-pkgs/http_profile - os:ubuntu-latest;pub-cache-hosted;sdk:3.0.0 + os:ubuntu-latest;pub-cache-hosted;sdk:3.2.0;packages:pkgs/http + os:ubuntu-latest;pub-cache-hosted;sdk:3.2.0 os:ubuntu-latest;pub-cache-hosted os:ubuntu-latest - name: Setup Dart SDK uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d with: - sdk: "3.0.0" + sdk: "3.2.0" - id: checkout name: Checkout repository uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 @@ -370,15 +408,6 @@ run: dart test --platform vm if: "always() && steps.pkgs_http_pub_upgrade.conclusion == 'success'" working-directory: pkgs/http - - id: pkgs_http_profile_pub_upgrade - name: pkgs/http_profile; dart pub upgrade - run: dart pub upgrade - if: "always() && steps.checkout.conclusion == 'success'" - working-directory: pkgs/http_profile - - name: "pkgs/http_profile; dart test --platform vm" - run: dart test --platform vm - if: "always() && steps.pkgs_http_profile_pub_upgrade.conclusion == 'success'" - working-directory: pkgs/http_profile needs: - job_001 - job_002 @@ -387,7 +416,7 @@ - job_005 - job_006 - job_007 - job_011: + job_012: name: "unit_test; linux; Dart dev; PKG: pkgs/http; `dart run --define=no_default_http_client=true test/no_default_http_client_test.dart`" runs-on: ubuntu-latest steps: @@ -425,7 +454,7 @@ - job_005 - job_006 - job_007 - job_012: + job_013: name: "unit_test; linux; Dart dev; PKG: pkgs/http; `dart test --platform chrome`" runs-on: ubuntu-latest steps: @@ -463,7 +492,7 @@ - job_005 - job_006 - job_007 - job_013: + job_014: name: "unit_test; linux; Dart dev; PKGS: pkgs/http, pkgs/http_profile; `dart test --platform vm`" runs-on: ubuntu-latest steps: @@ -510,7 +539,45 @@ - job_005 - job_006 - job_007 - job_014: + job_015: + name: "unit_test; linux; Dart dev; PKG: pkgs/http; `dart test --test-randomize-ordering-seed=random -p chrome -c dart2wasm`" + runs-on: ubuntu-latest + steps: + - name: Cache Pub hosted dependencies + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 + with: + path: "~/.pub-cache/hosted" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/http;commands:test_4" + restore-keys: | + os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/http + os:ubuntu-latest;pub-cache-hosted;sdk:dev + os:ubuntu-latest;pub-cache-hosted + os:ubuntu-latest + - name: Setup Dart SDK + uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d + with: + sdk: dev + - id: checkout + name: Checkout repository + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 + - id: pkgs_http_pub_upgrade + name: pkgs/http; dart pub upgrade + run: dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/http + - name: "pkgs/http; dart test --test-randomize-ordering-seed=random -p chrome -c dart2wasm" + run: "dart test --test-randomize-ordering-seed=random -p chrome -c dart2wasm" + if: "always() && steps.pkgs_http_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/http + needs: + - job_001 + - job_002 + - job_003 + - job_004 + - job_005 + - job_006 + - job_007 + job_016: name: "unit_test; linux; Flutter stable; PKG: pkgs/flutter_http_example; `flutter test --platform chrome`" runs-on: ubuntu-latest steps: @@ -548,7 +615,7 @@ - job_005 - job_006 - job_007 - job_015: + job_017: name: "unit_test; linux; Flutter stable; PKG: pkgs/flutter_http_example; `flutter test`" runs-on: ubuntu-latest steps: @@ -586,7 +653,7 @@ - job_005 - job_006 - job_007 - job_016: + job_018: name: "unit_test; macos; Flutter stable; PKG: pkgs/flutter_http_example; `flutter test`" runs-on: macos-latest steps: @@ -624,7 +691,7 @@ - job_005 - job_006 - job_007 - job_017: + job_019: name: "unit_test; windows; Flutter stable; PKG: pkgs/flutter_http_example; `flutter test`" runs-on: windows-latest steps:
diff --git a/.github/workflows/pull_request_label.yml b/.github/workflows/pull_request_label.yml index 9933aad..54e3df5 100644 --- a/.github/workflows/pull_request_label.yml +++ b/.github/workflows/pull_request_label.yml
@@ -16,7 +16,7 @@ pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@ac9175f8a1f3625fd0d4fb234536d26811351594 + - uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" sync-labels: true
diff --git a/analysis_options.yaml b/analysis_options.yaml index 7d741bd..e536739 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml
@@ -14,15 +14,10 @@ - avoid_returning_this - avoid_unused_constructor_parameters - cascade_invocations - - comment_references - join_return_with_assignment - missing_whitespace_between_adjacent_strings - no_adjacent_strings_in_list - no_runtimeType_toString - - prefer_const_constructors - prefer_const_declarations - prefer_expression_function_bodies - - prefer_relative_imports - - test_types_in_equals - use_string_buffers - - use_super_parameters
diff --git a/pkgs/cronet_http/CHANGELOG.md b/pkgs/cronet_http/CHANGELOG.md index 83e2701..cf1fcbc 100644 --- a/pkgs/cronet_http/CHANGELOG.md +++ b/pkgs/cronet_http/CHANGELOG.md
@@ -1,6 +1,11 @@ +## 1.0.0 + +* No functional changes. + ## 0.4.2 * Require `package:jni >= 0.7.2` to remove a potential buffer overflow. +* Fix a bug where incorrect HTTP request methods were sent. ## 0.4.1
diff --git a/pkgs/cronet_http/README.md b/pkgs/cronet_http/README.md index 0238eb6..49acf33 100644 --- a/pkgs/cronet_http/README.md +++ b/pkgs/cronet_http/README.md
@@ -1,31 +1,61 @@ +[![pub package](https://img.shields.io/pub/v/cronet_http.svg)](https://pub.dev/packages/cronet_http) +[![package publisher](https://img.shields.io/pub/publisher/cronet_http.svg)](https://pub.dev/packages/cronet_http/publisher) + An Android Flutter plugin that provides access to the -[Cronet](https://developer.android.com/guide/topics/connectivity/cronet/reference/org/chromium/net/package-summary) +[Cronet][] HTTP client. Cronet is available as part of -[Google Play Services](https://developers.google.com/android/guides/overview). +[Google Play Services][]. -This package depends on -[Google Play Services](https://developers.google.com/android/guides/overview) -for its Cronet implementation. +This package depends on [Google Play Services][] for its [Cronet][] +implementation. [`package:cronet_http_embedded`](https://pub.dev/packages/cronet_http_embedded) -is functionally identical to this package but embeds Cronet directly instead -of relying on -[Google Play Services](https://developers.google.com/android/guides/overview). +is functionally identical to this package but embeds [Cronet][] directly +instead of relying on [Google Play Services][]. -## Status: Experimental +## Motivation -**NOTE**: This package is currently experimental and published under the -[labs.dart.dev](https://dart.dev/dart-team-packages) pub publisher in order to -solicit feedback. +Using [Cronet][], rather than the socket-based [dart:io HttpClient][] +implemententation, has several advantages: -For packages in the labs.dart.dev publisher we generally plan to either graduate -the package into a supported publisher (dart.dev, tools.dart.dev) after a period -of feedback and iteration, or discontinue the package. These packages have a -much higher expected rate of API and breaking changes. +1. It automatically supports Android platform features such as HTTP proxies. +2. It supports configurable caching. +3. It supports more HTTP features such as HTTP/3. -Your feedback is valuable and will help us evolve this package. -For general feedback and suggestions please comment in the -[feedback issue](https://github.com/dart-lang/http/issues/764). -For bugs, please file an issue in the -[bug tracker](https://github.com/dart-lang/http/issues). +## Using + +The easiest way to use this library is via the the high-level interface +defined by [package:http Client][]. + +This approach allows the same HTTP code to be used on all platforms, while +still allowing platform-specific setup. + +```dart +import 'package:cronet_http/cronet_http.dart'; +import 'package:http/http.dart'; +import 'package:http/io_client.dart'; + +void main() async { + late Client httpClient; + if (Platform.isAndroid) { + final engine = CronetEngine.build( + cacheMode: CacheMode.memory, + cacheMaxSize: 2 * 1024 * 1024, + userAgent: 'Book Agent'); + httpClient = CronetClient.fromCronetEngine(engine); + } else { + httpClient = IOClient(HttpClient()..userAgent = 'Book Agent'); + } + + final response = await client.get(Uri.https( + 'www.googleapis.com', + '/books/v1/volumes', + {'q': 'HTTP', 'maxResults': '40', 'printType': 'books'})); +} +``` + +[Cronet]: https://developer.android.com/guide/topics/connectivity/cronet/reference/org/chromium/net/package-summary +[dart:io HttpClient]: https://api.dart.dev/stable/dart-io/HttpClient-class.html +[Google Play Services]: https://developers.google.com/android/guides/overview +[package:http Client]: https://pub.dev/documentation/http/latest/http/Client-class.html
diff --git a/pkgs/cronet_http/android/build.gradle b/pkgs/cronet_http/android/build.gradle index 3a91d8a..96bb197 100644 --- a/pkgs/cronet_http/android/build.gradle +++ b/pkgs/cronet_http/android/build.gradle
@@ -2,14 +2,14 @@ version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.6.10' + ext.kotlin_version = '1.7.21' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.1.2' + classpath 'com.android.tools.build:gradle:7.4.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -25,6 +25,11 @@ apply plugin: 'kotlin-android' android { + // Conditional for compatibility with AGP <4.2. + if (project.android.hasProperty("namespace")) { + namespace 'io.flutter.plugins.cronet_http' + } + compileSdkVersion 31 compileOptions { @@ -41,7 +46,11 @@ } defaultConfig { - minSdkVersion 16 + // api-level/minSdkVersion should be help in sync in: + // - .github/workflows/cronet.yml + // - pkgs/cronet_http/android/build.gradle + // - pkgs/cronet_http/example/android/app/build.gradle + minSdkVersion 28 } defaultConfig {
diff --git a/pkgs/cronet_http/example/android/app/build.gradle b/pkgs/cronet_http/example/android/app/build.gradle index 88b3356..1f7cd94 100644 --- a/pkgs/cronet_http/example/android/app/build.gradle +++ b/pkgs/cronet_http/example/android/app/build.gradle
@@ -46,7 +46,11 @@ applicationId "io.flutter.cronet_http_example" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. - minSdkVersion flutter.minSdkVersion + // api-level/minSdkVersion should be help in sync in: + // - .github/workflows/cronet.yml + // - pkgs/cronet_http/android/build.gradle + // - pkgs/cronet_http/example/android/app/build.gradle + minSdkVersion 28 targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName
diff --git a/pkgs/cronet_http/example/android/app/src/debug/AndroidManifest.xml b/pkgs/cronet_http/example/android/app/src/debug/AndroidManifest.xml index b17be9f..6170951 100644 --- a/pkgs/cronet_http/example/android/app/src/debug/AndroidManifest.xml +++ b/pkgs/cronet_http/example/android/app/src/debug/AndroidManifest.xml
@@ -1,4 +1,4 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.example.cronet_http_example"> + package="io.flutter.cronet_http_example"> <uses-permission android:name="android.permission.INTERNET"/> </manifest>
diff --git a/pkgs/cronet_http/example/android/app/src/main/AndroidManifest.xml b/pkgs/cronet_http/example/android/app/src/main/AndroidManifest.xml index e8d5f3f..254760d 100644 --- a/pkgs/cronet_http/example/android/app/src/main/AndroidManifest.xml +++ b/pkgs/cronet_http/example/android/app/src/main/AndroidManifest.xml
@@ -1,5 +1,5 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.example.cronet_http_example"> + package="io.flutter.cronet_http_example"> <uses-permission android:name="android.permission.INTERNET"/> <application android:label="cronet_http_example"
diff --git a/pkgs/cronet_http/example/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java b/pkgs/cronet_http/example/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java index 90015fd..3772f02 100644 --- a/pkgs/cronet_http/example/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java +++ b/pkgs/cronet_http/example/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java
@@ -16,14 +16,24 @@ private static final String TAG = "GeneratedPluginRegistrant"; public static void registerWith(@NonNull FlutterEngine flutterEngine) { try { - flutterEngine.getPlugins().add(new io.flutter.plugins.cronet_http.CronetHttpPlugin()); - } catch(Exception e) { - Log.e(TAG, "Error registering plugin cronet_http, io.flutter.plugins.cronet_http.CronetHttpPlugin", e); + flutterEngine.getPlugins().add(new dev.flutter.plugins.integration_test.IntegrationTestPlugin()); + } catch (Exception e) { + Log.e(TAG, "Error registering plugin integration_test, dev.flutter.plugins.integration_test.IntegrationTestPlugin", e); } try { - flutterEngine.getPlugins().add(new dev.flutter.plugins.integration_test.IntegrationTestPlugin()); - } catch(Exception e) { - Log.e(TAG, "Error registering plugin integration_test, dev.flutter.plugins.integration_test.IntegrationTestPlugin", e); + flutterEngine.getPlugins().add(new com.github.dart_lang.jni.JniPlugin()); + } catch (Exception e) { + Log.e(TAG, "Error registering plugin jni, com.github.dart_lang.jni.JniPlugin", e); + } + try { + flutterEngine.getPlugins().add(new io.flutter.plugins.pathprovider.PathProviderPlugin()); + } catch (Exception e) { + Log.e(TAG, "Error registering plugin path_provider_android, io.flutter.plugins.pathprovider.PathProviderPlugin", e); + } + try { + flutterEngine.getPlugins().add(new com.tekartik.sqflite.SqflitePlugin()); + } catch (Exception e) { + Log.e(TAG, "Error registering plugin sqflite, com.tekartik.sqflite.SqflitePlugin", e); } } }
diff --git a/pkgs/cronet_http/example/android/app/src/main/kotlin/com/example/cronet_http_example/MainActivity.kt b/pkgs/cronet_http/example/android/app/src/main/kotlin/io/flutter/cronet_http_example/MainActivity.kt similarity index 70% rename from pkgs/cronet_http/example/android/app/src/main/kotlin/com/example/cronet_http_example/MainActivity.kt rename to pkgs/cronet_http/example/android/app/src/main/kotlin/io/flutter/cronet_http_example/MainActivity.kt index 2688c93..97b07c8 100644 --- a/pkgs/cronet_http/example/android/app/src/main/kotlin/com/example/cronet_http_example/MainActivity.kt +++ b/pkgs/cronet_http/example/android/app/src/main/kotlin/io/flutter/cronet_http_example/MainActivity.kt
@@ -1,4 +1,4 @@ -package com.example.cronet_http_example +package io.flutter.cronet_http_example import io.flutter.embedding.android.FlutterActivity
diff --git a/pkgs/cronet_http/example/android/app/src/profile/AndroidManifest.xml b/pkgs/cronet_http/example/android/app/src/profile/AndroidManifest.xml index b17be9f..6170951 100644 --- a/pkgs/cronet_http/example/android/app/src/profile/AndroidManifest.xml +++ b/pkgs/cronet_http/example/android/app/src/profile/AndroidManifest.xml
@@ -1,4 +1,4 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.example.cronet_http_example"> + package="io.flutter.cronet_http_example"> <uses-permission android:name="android.permission.INTERNET"/> </manifest>
diff --git a/pkgs/cronet_http/example/android/build.gradle b/pkgs/cronet_http/example/android/build.gradle index 83ae220..954fa1c 100644 --- a/pkgs/cronet_http/example/android/build.gradle +++ b/pkgs/cronet_http/example/android/build.gradle
@@ -1,12 +1,12 @@ buildscript { - ext.kotlin_version = '1.6.10' + ext.kotlin_version = '1.7.21' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.1.2' + classpath 'com.android.tools.build:gradle:7.4.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -26,6 +26,6 @@ project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir }
diff --git a/pkgs/cronet_http/example/android/gradle/wrapper/gradle-wrapper.properties b/pkgs/cronet_http/example/android/gradle/wrapper/gradle-wrapper.properties index cc5527d..cfe88f6 100644 --- a/pkgs/cronet_http/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/pkgs/cronet_http/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
diff --git a/pkgs/cronet_http/example/pubspec.yaml b/pkgs/cronet_http/example/pubspec.yaml index 568cef9..5bb8027 100644 --- a/pkgs/cronet_http/example/pubspec.yaml +++ b/pkgs/cronet_http/example/pubspec.yaml
@@ -4,7 +4,7 @@ publish_to: 'none' environment: - sdk: "^3.0.0" + sdk: ^3.0.0 dependencies: cached_network_image: ^3.2.3 @@ -13,10 +13,10 @@ cupertino_icons: ^1.0.2 flutter: sdk: flutter - http: ^0.13.5 + http: ^1.0.0 dev_dependencies: - dart_flutter_team_lints: ^1.0.0 + dart_flutter_team_lints: ^2.0.0 flutter_test: sdk: flutter http_client_conformance_tests:
diff --git a/pkgs/cronet_http/lib/src/cronet_client.dart b/pkgs/cronet_http/lib/src/cronet_client.dart index c4d0d19..272b602 100644 --- a/pkgs/cronet_http/lib/src/cronet_client.dart +++ b/pkgs/cronet_http/lib/src/cronet_client.dart
@@ -317,7 +317,7 @@ jb.UrlRequestCallbackProxy.new1( _urlRequestCallbacks(request, responseCompleter)), _executor, - ); + )..setHttpMethod(request.method.toJString()); var headers = request.headers; if (body.isNotEmpty &&
diff --git a/pkgs/cronet_http/pubspec.yaml b/pkgs/cronet_http/pubspec.yaml index 82390c8..5f4229e 100644 --- a/pkgs/cronet_http/pubspec.yaml +++ b/pkgs/cronet_http/pubspec.yaml
@@ -1,7 +1,7 @@ name: cronet_http -description: > +version: 1.0.0 +description: >- An Android Flutter plugin that provides access to the Cronet HTTP client. -version: 0.4.2 repository: https://github.com/dart-lang/http/tree/master/pkgs/cronet_http environment: @@ -15,7 +15,7 @@ jni: ^0.7.2 dev_dependencies: - dart_flutter_team_lints: ^1.0.0 + dart_flutter_team_lints: ^2.0.0 jnigen: ^0.7.0 xml: ^6.1.0 yaml_edit: ^2.0.3 @@ -25,4 +25,3 @@ platforms: android: ffiPlugin: true -
diff --git a/pkgs/cronet_http/tool/prepare_for_embedded.dart b/pkgs/cronet_http/tool/prepare_for_embedded.dart index e0f99d7..3442bc6 100644 --- a/pkgs/cronet_http/tool/prepare_for_embedded.dart +++ b/pkgs/cronet_http/tool/prepare_for_embedded.dart
@@ -13,6 +13,9 @@ /// 1. Modifying the Gradle build file to reference the embedded Cronet. /// 2. Modifying the *name* and *description* in `pubspec.yaml`. /// 3. Replacing `README.md` with `README_EMBEDDED.md`. +/// 4. Change the name of `cronet_http.dart` to `cronet_http_embedded.dart`. +/// 5. Update all the imports from `package:cronet_http/cronet_http.dart` to +/// `package:cronet_http_embedded/cronet_http_embedded.dart` /// /// After running this script, `flutter pub publish` /// can be run to update package:cronet_http_embedded. @@ -40,7 +43,7 @@ 'android/maven2/org/chromium/net/group-index.xml', ); -void main() async { +void main(List<String> args) async { if (Directory.current.path.endsWith('tool')) { _packageDirectory = Directory.current.parent; } else { @@ -51,6 +54,8 @@ updateCronetDependency(latestVersion); updatePubSpec(); updateReadme(); + updateLibraryName(); + updateImports(); } Future<String> _getLatestCronetVersion() async { @@ -69,7 +74,7 @@ return versions.last; } -/// Update android/build.gradle +/// Update android/build.gradle. void updateCronetDependency(String latestVersion) { final fBuildGradle = File('${_packageDirectory.path}/android/build.gradle'); final gradleContent = fBuildGradle.readAsStringSync(); @@ -88,18 +93,48 @@ fBuildGradle.writeAsStringSync(newGradleContent); } -/// Update pubspec.yaml +/// Update pubspec.yaml and example/pubspec.yaml. void updatePubSpec() { + print('Updating pubspec.yaml'); final fPubspec = File('${_packageDirectory.path}/pubspec.yaml'); final yamlEditor = YamlEditor(fPubspec.readAsStringSync()) ..update(['name'], _packageName) ..update(['description'], _packageDescription); fPubspec.writeAsStringSync(yamlEditor.toString()); + print('Updating example/pubspec.yaml'); + final examplePubspec = File('${_packageDirectory.path}/example/pubspec.yaml'); + final replaced = examplePubspec + .readAsStringSync() + .replaceAll('cronet_http:', 'cronet_http_embedded:'); + examplePubspec.writeAsStringSync(replaced); } -/// Move README_EMBEDDED.md to replace README.md +/// Move README_EMBEDDED.md to replace README.md. void updateReadme() { + print('Updating README.md from README_EMBEDDED.md'); File('${_packageDirectory.path}/README.md').deleteSync(); File('${_packageDirectory.path}/README_EMBEDDED.md') .renameSync('${_packageDirectory.path}/README.md'); } + +void updateImports() { + print('Updating imports in Dart files'); + for (final file in _packageDirectory.listSync(recursive: true)) { + if (file is File && file.path.endsWith('.dart')) { + final updatedSource = file.readAsStringSync().replaceAll( + 'package:cronet_http/cronet_http.dart', + 'package:cronet_http_embedded/cronet_http_embedded.dart', + ); + file.writeAsStringSync(updatedSource); + } + } +} + +void updateLibraryName() { + print('Renaming cronet_http.dart to cronet_http_embedded.dart'); + File( + '${_packageDirectory.path}/lib/cronet_http.dart', + ).renameSync( + '${_packageDirectory.path}/lib/cronet_http_embedded.dart', + ); +}
diff --git a/pkgs/cupertino_http/CHANGELOG.md b/pkgs/cupertino_http/CHANGELOG.md index 3a69c49..d768f7a 100644 --- a/pkgs/cupertino_http/CHANGELOG.md +++ b/pkgs/cupertino_http/CHANGELOG.md
@@ -1,3 +1,8 @@ +## 1.2.0 + +* Add support for setting additional http headers in + `URLSessionConfiguration`. + ## 1.1.0 * Add websocket support to `cupertino_api`.
diff --git a/pkgs/cupertino_http/example/integration_test/client_test.dart b/pkgs/cupertino_http/example/integration_test/client_test.dart index 9247783..1becd5c 100644 --- a/pkgs/cupertino_http/example/integration_test/client_test.dart +++ b/pkgs/cupertino_http/example/integration_test/client_test.dart
@@ -2,6 +2,7 @@ // 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:async'; import 'dart:io'; import 'dart:typed_data'; @@ -45,7 +46,7 @@ } final request = StreamedRequest('POST', uri); request.sink.add(data); - request.sink.close(); + unawaited(request.sink.close()); await client.send(request); expect(serverHash, sha1.convert(data).bytes); });
diff --git a/pkgs/cupertino_http/example/integration_test/url_session_configuration_test.dart b/pkgs/cupertino_http/example/integration_test/url_session_configuration_test.dart index 85386bc..ab117c3 100644 --- a/pkgs/cupertino_http/example/integration_test/url_session_configuration_test.dart +++ b/pkgs/cupertino_http/example/integration_test/url_session_configuration_test.dart
@@ -2,10 +2,38 @@ // 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:cupertino_http/cupertino_http.dart'; import 'package:integration_test/integration_test.dart'; import 'package:test/test.dart'; +/// Make a HTTP request using the given configuration and return the headers +/// received by the server. +Future<Map<String, List<String>>> sentHeaders( + URLSessionConfiguration config) async { + final session = URLSession.sessionWithConfiguration(config); + final headers = <String, List<String>>{}; + final server = (await HttpServer.bind('localhost', 0)) + ..listen((request) async { + request.headers.forEach((k, v) => headers[k] = v); + await request.drain<void>(); + request.response.headers.set('Content-Type', 'text/plain'); + request.response.write('Hello World'); + await request.response.close(); + }); + + final task = session.dataTaskWithRequest(URLRequest.fromUrl( + Uri(scheme: 'http', host: 'localhost', port: server.port))) + ..resume(); + while (task.state != URLSessionTaskState.urlSessionTaskStateCompleted) { + await pumpEventQueue(); + } + + await server.close(); + return headers; +} + void testProperties(URLSessionConfiguration config) { group('properties', () { test('allowsCellularAccess', () { @@ -32,6 +60,22 @@ config.discretionary = false; expect(config.discretionary, false); }); + test('httpAdditionalHeaders', () async { + expect(config.httpAdditionalHeaders, isNull); + + config.httpAdditionalHeaders = { + 'User-Agent': 'My Client', + 'MyHeader': 'myvalue' + }; + expect(config.httpAdditionalHeaders, + {'User-Agent': 'My Client', 'MyHeader': 'myvalue'}); + final headers = await sentHeaders(config); + expect(headers, containsPair('user-agent', ['My Client'])); + expect(headers, containsPair('myheader', ['myvalue'])); + + config.httpAdditionalHeaders = null; + expect(config.httpAdditionalHeaders, isNull); + }); test('httpCookieAcceptPolicy', () { config.httpCookieAcceptPolicy = HTTPCookieAcceptPolicy.httpCookieAcceptPolicyAlways;
diff --git a/pkgs/cupertino_http/example/lib/main.dart b/pkgs/cupertino_http/example/lib/main.dart index edfceac..32c2b08 100644 --- a/pkgs/cupertino_http/example/lib/main.dart +++ b/pkgs/cupertino_http/example/lib/main.dart
@@ -15,7 +15,10 @@ void main() { var clientFactory = Client.new; // The default Client. if (Platform.isIOS || Platform.isMacOS) { - clientFactory = CupertinoClient.defaultSessionConfiguration.call; + final config = URLSessionConfiguration.ephemeralSessionConfiguration() + ..cache = URLCache.withCapacity(memoryCapacity: 2 * 1024 * 1024) + ..httpAdditionalHeaders = {'User-Agent': 'Book Agent'}; + clientFactory = () => CupertinoClient.fromSessionConfiguration(config); } runWithClient(() => runApp(const BookSearchApp()), clientFactory); }
diff --git a/pkgs/cupertino_http/example/pubspec.yaml b/pkgs/cupertino_http/example/pubspec.yaml index be72731..bae184b 100644 --- a/pkgs/cupertino_http/example/pubspec.yaml +++ b/pkgs/cupertino_http/example/pubspec.yaml
@@ -16,12 +16,12 @@ cupertino_icons: ^1.0.2 flutter: sdk: flutter - http: ^0.13.5 + http: ^1.0.0 dev_dependencies: convert: ^3.1.1 crypto: ^3.0.3 - dart_flutter_team_lints: ^1.0.0 + dart_flutter_team_lints: ^2.0.0 ffi: ^2.0.1 flutter_test: sdk: flutter
diff --git a/pkgs/cupertino_http/lib/src/cupertino_api.dart b/pkgs/cupertino_http/lib/src/cupertino_api.dart index 01c4853..5cebd7e 100644 --- a/pkgs/cupertino_http/lib/src/cupertino_api.dart +++ b/pkgs/cupertino_http/lib/src/cupertino_api.dart
@@ -344,6 +344,32 @@ bool get discretionary => _nsObject.discretionary; set discretionary(bool value) => _nsObject.discretionary = value; + /// Additional headers to send with each request. + /// + /// Note that the getter for this field returns a **copy** of the headers. + /// + /// See [NSURLSessionConfiguration.HTTPAdditionalHeaders](https://developer.apple.com/documentation/foundation/nsurlsessionconfiguration/1411532-httpadditionalheaders) + Map<String, String>? get httpAdditionalHeaders { + if (_nsObject.HTTPAdditionalHeaders case var additionalHeaders?) { + final headers = ncb.NSDictionary.castFrom(additionalHeaders); + return stringDictToMap(headers); + } + return null; + } + + set httpAdditionalHeaders(Map<String, String>? headers) { + if (headers == null) { + _nsObject.HTTPAdditionalHeaders = null; + return; + } + final d = ncb.NSMutableDictionary.alloc(linkedLibs).init(); + headers.forEach((key, value) { + d.setObject_forKey_( + value.toNSString(linkedLibs), key.toNSString(linkedLibs)); + }); + _nsObject.HTTPAdditionalHeaders = d; + } + /// What policy to use when deciding whether to accept cookies. /// /// See [NSURLSessionConfiguration.HTTPCookieAcceptPolicy](https://developer.apple.com/documentation/foundation/nsurlsessionconfiguration/1408933-httpcookieacceptpolicy). @@ -441,6 +467,7 @@ 'allowsConstrainedNetworkAccess=$allowsConstrainedNetworkAccess ' 'allowsExpensiveNetworkAccess=$allowsExpensiveNetworkAccess ' 'discretionary=$discretionary ' + 'httpAdditionalHeaders=$httpAdditionalHeaders ' 'httpCookieAcceptPolicy=$httpCookieAcceptPolicy ' 'httpShouldSetCookies=$httpShouldSetCookies ' 'httpMaximumConnectionsPerHost=$httpMaximumConnectionsPerHost '
diff --git a/pkgs/cupertino_http/pubspec.yaml b/pkgs/cupertino_http/pubspec.yaml index 9d639a8..0a97bdd 100644 --- a/pkgs/cupertino_http/pubspec.yaml +++ b/pkgs/cupertino_http/pubspec.yaml
@@ -1,8 +1,8 @@ name: cupertino_http -description: > +version: 1.2.0 +description: >- A macOS/iOS Flutter plugin that provides access to the Foundation URL Loading System. -version: 1.1.0 repository: https://github.com/dart-lang/http/tree/master/pkgs/cupertino_http environment: @@ -17,7 +17,7 @@ http: '>=0.13.4 <2.0.0' dev_dependencies: - dart_flutter_team_lints: ^1.0.0 + dart_flutter_team_lints: ^2.0.0 ffigen: ^9.0.1 flutter:
diff --git a/pkgs/flutter_http_example/lib/main.dart b/pkgs/flutter_http_example/lib/main.dart index 70f003c..406b9e6 100644 --- a/pkgs/flutter_http_example/lib/main.dart +++ b/pkgs/flutter_http_example/lib/main.dart
@@ -11,7 +11,7 @@ import 'book.dart'; import 'http_client_factory.dart' - if (dart.library.html) 'http_client_factory_web.dart' as http_factory; + if (dart.library.js_interop) 'http_client_factory_web.dart' as http_factory; void main() { // `runWithClient` is used to control which `package:http` `Client` is used
diff --git a/pkgs/flutter_http_example/pubspec.yaml b/pkgs/flutter_http_example/pubspec.yaml index 6f3d1ba..0331490 100644 --- a/pkgs/flutter_http_example/pubspec.yaml +++ b/pkgs/flutter_http_example/pubspec.yaml
@@ -1,10 +1,9 @@ name: flutter_http_example +version: 1.0.0 description: Demonstrates how to use package:http in a Flutter app. publish_to: 'none' -version: 1.0.0 - environment: sdk: ^3.0.0 flutter: '>=3.10.0' @@ -17,11 +16,11 @@ fetch_client: ^1.0.2 flutter: sdk: flutter - http: ^0.13.5 + http: ^1.0.0 provider: ^6.0.5 dev_dependencies: - dart_flutter_team_lints: ^1.0.0 + dart_flutter_team_lints: ^2.0.0 flutter_test: sdk: flutter integration_test:
diff --git a/pkgs/http/CHANGELOG.md b/pkgs/http/CHANGELOG.md index 6d39b10..7b8dec4 100644 --- a/pkgs/http/CHANGELOG.md +++ b/pkgs/http/CHANGELOG.md
@@ -1,8 +1,18 @@ +## 1.1.3-wip + +* Add `MockClient.pngResponse`, which makes it easier to fake image responses. + +## 1.1.2 + +* Allow `web: '>=0.3.0 <0.5.0'`. + ## 1.1.1 * `BrowserClient` throws `ClientException` when the `'Content-Length'` header is invalid. * `IOClient` trims trailing whitespace on header values. +* Require Dart 3.2 +* Browser: support Wasm by using `package:web`. ## 1.1.0
diff --git a/pkgs/http/README.md b/pkgs/http/README.md index c889f2b..642c238 100644 --- a/pkgs/http/README.md +++ b/pkgs/http/README.md
@@ -4,8 +4,8 @@ A composable, Future-based library for making HTTP requests. This package contains a set of high-level functions and classes that make it -easy to consume HTTP resources. It's multi-platform, and supports mobile, desktop, -and the browser. +easy to consume HTTP resources. It's multi-platform (mobile, desktop, and +browser) and supports multiple implementations. ## Using @@ -23,6 +23,11 @@ print(await http.read(Uri.https('example.com', 'foobar.txt'))); ``` +> [!NOTE] +> Flutter applications may require +> [additional configuration](https://docs.flutter.dev/data-and-backend/networking#platform-notes) +> to make HTTP requests. + If you're making multiple requests to the same server, you can keep open a persistent connection by using a [Client][] rather than making one-off requests. If you do this, make sure to close the client when you're done: @@ -41,6 +46,12 @@ } ``` +> [!TIP] +> For detailed background information and practical usage examples, see: +> - [Dart Development: Fetch data from the internet](https://dart.dev/tutorials/server/fetch-data) +> - [Flutter Cookbook: Fetch data from the internet](https://docs.flutter.dev/cookbook/networking/fetch-data) +> - [The Flutter HTTP example application][flutterhttpexample] + You can also exert more fine-grained control over your requests and responses by creating [Request][] or [StreamedRequest][] objects yourself and passing them to [Client.send][]. @@ -100,3 +111,181 @@ the [`RetryClient()`][new RetryClient] constructor. [new RetryClient]: https://pub.dev/documentation/http/latest/retry/RetryClient/RetryClient.html + +## Choosing an implementation + +There are multiple implementations of the `package:http` [`Client`][client] interface. By default, `package:http` uses [`BrowserClient`][browserclient] on the web and [`IOClient`][ioclient] on all other platforms. You an choose a different [`Client`][client] implementation based on the needs of your application. + +You can change implementations without changing your application code, except +for a few lines of [configuration](#2-configure-the-http-client). + +Some well supported implementations are: + +| Implementation | Supported Platforms | SDK | Caching | HTTP3/QUIC | Platform Native | +| -------------- | ------------------- | ----| ------- | ---------- | --------------- | +| `package:http` — [`IOClient`][ioclient] | Android, iOS, Linux, macOS, Windows | Dart, Flutter | ❌ | ❌ | ❌ | +| `package:http` — [`BrowserClient`][browserclient] | Web | Dart, Flutter | ― | ✅︎ | ✅︎ | Dart, Flutter | +| [`package:cupertino_http`][cupertinohttp] — [`CupertinoClient`][cupertinoclient] | iOS, macOS | Flutter | ✅︎ | ✅︎ | ✅︎ | +| [`package:cronet_http`][cronethttp] — [`CronetClient`][cronetclient] | Android | Flutter | ✅︎ | ✅︎ | ― | +| [`package:fetch_client`][fetch] — [`FetchClient`][fetchclient] | Web | Dart, Flutter | ✅︎ | ✅︎ | ✅︎ | + +> [!TIP] +> If you are writing a Dart package or Flutter pluggin that uses +> `package:http`, you should not depend on a particular [`Client`][client] +> implementation. Let the application author decide what implementation is +> best for their project. You can make that easier by accepting an explicit +> [`Client`][client] argument. For example: +> +> ```dart +> Future<Album> fetchAlbum({Client? client}) async { +> client ??= Client(); +> ... +> } +> ``` + +## Configuration + +To use a HTTP client implementation other than the default, you must: +1. Add the HTTP client as a dependency. +2. Configure the HTTP client. +3. Connect the HTTP client to the code that uses it. + +### 1. Add the HTTP client as a dependency. + +To add a package compatible with the Dart SDK to your project, use `dart pub add`. + +For example: + +```terminal +# Replace "fetch_client" with the package that you want to use. +dart pub add fetch_client +``` + +To add a package that requires the Flutter SDK, use `flutter pub add`. + +For example: + +```terminal +# Replace "cupertino_http" with the package that you want to use. +flutter pub add cupertino_http +``` + +### 2. Configure the HTTP client. + +Different `package:http` [`Client`][client] implementations may require +different configuration options. + +Add a function that returns a correctly configured [`Client`][client]. You can +return a different [`Client`][client] on different platforms. + +For example: + +```dart +Client httpClient() { + if (Platform.isAndroid) { + final engine = CronetEngine.build( + cacheMode: CacheMode.memory, + cacheMaxSize: 1000000); + return CronetClient.fromCronetEngine(engine); + } + if (Platform.isIOS || Platform.isMacOS) { + final config = URLSessionConfiguration.ephemeralSessionConfiguration() + ..cache = URLCache.withCapacity(memoryCapacity: 1000000); + return CupertinoClient.fromSessionConfiguration(config); + } + return IOClient(); +} +``` + +> [!TIP] +> [The Flutter HTTP example application][flutterhttpexample] demonstrates +> configuration best practices. + +#### Supporting browser and native + +If your application can be run in the browser and natively, you must put your +browser and native configurations in seperate files and import the correct file +based on the platform. + +For example: + +```dart +// -- http_client_factory.dart +Client httpClient() { + if (Platform.isAndroid) { + return CronetClient.defaultCronetEngine(); + } + if (Platform.isIOS || Platform.isMacOS) { + return CupertinoClient.defaultSessionConfiguration(); + } + return IOClient(); +} +``` + +```dart +// -- http_client_factory_web.dart +Client httpClient() => FetchClient(); +``` + +```dart +// -- main.dart +import 'http_client_factory.dart' + if (dart.library.js_interop) 'http_client_factory_web.dart' + +// The correct `httpClient` will be available. +``` + +### 3. Connect the HTTP client to the code that uses it. + +The best way to pass [`Client`][client] to the places that use it is +explicitly through arguments. + +For example: + +```dart +void main() { + final client = httpClient(); + fetchAlbum(client, ...); +} +``` + +In Flutter, you can use a one of many +[state mangement approaches][flutterstatemanagement]. + +If you depend on code that uses top-level functions (e.g. `http.post`) or +calls the [`Client()`][clientconstructor] constructor, then you can use +[`runWithClient`](runwithclient) to ensure that the correct +`Client` is used. When an [Isolate][isolate] is spawned, it does not inherit +any variables from the calling Zone, so `runWithClient` needs to be used in +each Isolate that uses `package:http`. + +You can ensure that only the `Client` that you have explicitly configured is +used by defining `no_default_http_client=true` in the environment. This will +also allow the default `Client` implementation to be removed, resulting in +a reduced application size. + +```terminal +$ flutter build appbundle --dart-define=no_default_http_client=true ... +$ dart compile exe --define=no_default_http_client=true ... +``` + +> [!TIP] +> [The Flutter HTTP example application][flutterhttpexample] demonstrates +> how to make the configured [`Client`][client] available using +> [`package:provider`][provider] and [`runWithClient`](runwithclient). + +[browserclient]: https://pub.dev/documentation/http/latest/browser_client/BrowserClient-class.html +[client]: https://pub.dev/documentation/http/latest/http/Client-class.html +[clientconstructor]: https://pub.dev/documentation/http/latest/http/Client/Client.html +[cupertinohttp]: https://pub.dev/packages/cupertino_http +[cupertinoclient]: https://pub.dev/documentation/cupertino_http/latest/cupertino_http/CupertinoClient-class.html +[cronethttp]: https://pub.dev/packages/cronet_http +[cronetclient]: https://pub.dev/documentation/cronet_http/latest/cronet_http/CronetClient-class.html +[fetch]: https://pub.dev/packages/fetch_client +[fetchclient]: https://pub.dev/documentation/fetch_client/latest/fetch_client/FetchClient-class.html +[flutterhttpexample]: https://github.com/dart-lang/http/tree/master/pkgs/flutter_http_example +[ioclient]: https://pub.dev/documentation/http/latest/io_client/IOClient-class.html +[isolate]: https://dart.dev/language/concurrency#how-isolates-work +[flutterstatemanagement]: https://docs.flutter.dev/data-and-backend/state-mgmt/options +[provider]: https://pub.dev/packages/provider +[runwithclient]: https://pub.dev/documentation/http/latest/http/runWithClient.html
diff --git a/pkgs/http/lib/src/browser_client.dart b/pkgs/http/lib/src/browser_client.dart index 9345be0..80db8b1 100644 --- a/pkgs/http/lib/src/browser_client.dart +++ b/pkgs/http/lib/src/browser_client.dart
@@ -3,8 +3,9 @@ // BSD-style license that can be found in the LICENSE file. import 'dart:async'; -import 'dart:html'; -import 'dart:typed_data'; +import 'dart:js_interop'; + +import 'package:web/helpers.dart'; import 'base_client.dart'; import 'base_request.dart'; @@ -25,8 +26,8 @@ return BrowserClient(); } -/// A `dart:html`-based HTTP client that runs in the browser and is backed by -/// XMLHttpRequests. +/// A `package:web`-based HTTP client that runs in the browser and is backed by +/// [XMLHttpRequest]. /// /// This client inherits some of the limitations of XMLHttpRequest. It ignores /// the [BaseRequest.contentLength], [BaseRequest.persistentConnection], @@ -37,7 +38,7 @@ /// The currently active XHRs. /// /// These are aborted if the client is closed. - final _xhrs = <HttpRequest>{}; + final _xhrs = <XMLHttpRequest>{}; /// Whether to send credentials such as cookies or authorization headers for /// cross-site requests. @@ -55,28 +56,31 @@ 'HTTP request failed. Client is already closed.', request.url); } var bytes = await request.finalize().toBytes(); - var xhr = HttpRequest(); + var xhr = XMLHttpRequest(); _xhrs.add(xhr); xhr - ..open(request.method, '${request.url}', async: true) + ..open(request.method, '${request.url}', true) ..responseType = 'arraybuffer' ..withCredentials = withCredentials; - request.headers.forEach(xhr.setRequestHeader); + for (var header in request.headers.entries) { + xhr.setRequestHeader(header.key, header.value); + } var completer = Completer<StreamedResponse>(); unawaited(xhr.onLoad.first.then((_) { - if (xhr.responseHeaders['content-length'] case final contentLengthHeader? - when !_digitRegex.hasMatch(contentLengthHeader)) { + if (xhr.responseHeaders['content-length'] case final contentLengthHeader + when contentLengthHeader != null && + !_digitRegex.hasMatch(contentLengthHeader)) { completer.completeError(ClientException( 'Invalid content-length header [$contentLengthHeader].', request.url, )); return; } - var body = (xhr.response as ByteBuffer).asUint8List(); + var body = (xhr.response as JSArrayBuffer).toDart.asUint8List(); completer.complete(StreamedResponse( - ByteStream.fromBytes(body), xhr.status!, + ByteStream.fromBytes(body), xhr.status, contentLength: body.length, request: request, headers: xhr.responseHeaders, @@ -91,7 +95,7 @@ StackTrace.current); })); - xhr.send(bytes); + xhr.send(bytes.toJS); try { return await completer.future; @@ -112,3 +116,30 @@ _xhrs.clear(); } } + +extension on XMLHttpRequest { + Map<String, String> get responseHeaders { + // from Closure's goog.net.Xhrio.getResponseHeaders. + var headers = <String, String>{}; + var headersString = getAllResponseHeaders(); + var headersList = headersString.split('\r\n'); + for (var header in headersList) { + if (header.isEmpty) { + continue; + } + + var splitIdx = header.indexOf(': '); + if (splitIdx == -1) { + continue; + } + var key = header.substring(0, splitIdx).toLowerCase(); + var value = header.substring(splitIdx + 2); + if (headers.containsKey(key)) { + headers[key] = '${headers[key]}, $value'; + } else { + headers[key] = value; + } + } + return headers; + } +}
diff --git a/pkgs/http/lib/src/client.dart b/pkgs/http/lib/src/client.dart index 9bceb88..7429ca8 100644 --- a/pkgs/http/lib/src/client.dart +++ b/pkgs/http/lib/src/client.dart
@@ -12,7 +12,7 @@ import 'base_client.dart'; import 'base_request.dart'; import 'client_stub.dart' - if (dart.library.html) 'browser_client.dart' + if (dart.library.js_interop) 'browser_client.dart' if (dart.library.io) 'io_client.dart'; import 'exception.dart'; import 'response.dart'; @@ -37,7 +37,8 @@ /// Creates a new platform appropriate client. /// /// Creates an `IOClient` if `dart:io` is available and a `BrowserClient` if - /// `dart:html` is available, otherwise it will throw an unsupported error. + /// `dart:js_interop` is available, otherwise it will throw an unsupported + /// error. factory Client() => zoneClient ?? createClient(); /// Sends an HTTP HEAD request with the given headers to the given URL.
diff --git a/pkgs/http/lib/src/client_stub.dart b/pkgs/http/lib/src/client_stub.dart index 1a34d50..6384fd0 100644 --- a/pkgs/http/lib/src/client_stub.dart +++ b/pkgs/http/lib/src/client_stub.dart
@@ -6,4 +6,4 @@ /// Implemented in `browser_client.dart` and `io_client.dart`. BaseClient createClient() => throw UnsupportedError( - 'Cannot create a client without dart:html or dart:io.'); + 'Cannot create a client without dart:js_interop or dart:io.');
diff --git a/pkgs/http/lib/src/io_client.dart b/pkgs/http/lib/src/io_client.dart index 247cc8c..db66b02 100644 --- a/pkgs/http/lib/src/io_client.dart +++ b/pkgs/http/lib/src/io_client.dart
@@ -72,6 +72,18 @@ /// The underlying `dart:io` HTTP client. HttpClient? _inner; + /// Create a new `dart:io`-based HTTP [Client]. + /// + /// If [inner] is provided then it can be used to provide configuration + /// options for the client. + /// + /// For example: + /// ```dart + /// final httpClient = HttpClient() + /// ..userAgent = 'Book Agent' + /// ..idleTimeout = const Duration(seconds: 5); + /// final client = IOClient(httpClient); + /// ``` IOClient([HttpClient? inner]) : _inner = inner ?? HttpClient(); /// Sends an HTTP request and asynchronously returns the response.
diff --git a/pkgs/http/lib/src/mock_client.dart b/pkgs/http/lib/src/mock_client.dart index bf2df40..52f108a 100644 --- a/pkgs/http/lib/src/mock_client.dart +++ b/pkgs/http/lib/src/mock_client.dart
@@ -2,6 +2,8 @@ // 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:convert'; + import 'base_client.dart'; import 'base_request.dart'; import 'byte_stream.dart'; @@ -10,6 +12,11 @@ import 'streamed_request.dart'; import 'streamed_response.dart'; +final _pngImageData = base64Decode( + 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDw' + 'AEhQGAhKmMIQAAAABJRU5ErkJggg==', +); + // TODO(nweiz): once Dart has some sort of Rack- or WSGI-like standard for // server APIs, MockClient should conform to it. @@ -69,6 +76,17 @@ var bodyStream = request.finalize(); return await _handler(request, bodyStream); } + + /// Return a response containing a PNG image. + static Response pngResponse({BaseRequest? request}) { + final headers = { + 'content-type': 'image/png', + 'content-length': '${_pngImageData.length}' + }; + + return Response.bytes(_pngImageData, 200, + request: request, headers: headers, reasonPhrase: 'OK'); + } } /// A handler function that receives [StreamedRequest]s and sends
diff --git a/pkgs/http/mono_pkg.yaml b/pkgs/http/mono_pkg.yaml index 0e2f9d8..06f79d9 100644 --- a/pkgs/http/mono_pkg.yaml +++ b/pkgs/http/mono_pkg.yaml
@@ -18,3 +18,5 @@ - command: dart run --define=no_default_http_client=true test/no_default_http_client_test.dart os: - linux + - test: --test-randomize-ordering-seed=random -p chrome -c dart2wasm + sdk: dev
diff --git a/pkgs/http/pubspec.yaml b/pkgs/http/pubspec.yaml index ec23e2d..1645f96 100644 --- a/pkgs/http/pubspec.yaml +++ b/pkgs/http/pubspec.yaml
@@ -1,18 +1,19 @@ name: http -version: 1.1.1-wip +version: 1.1.3-wip description: A composable, multi-platform, Future-based API for HTTP requests. repository: https://github.com/dart-lang/http/tree/master/pkgs/http environment: - sdk: ^3.0.0 + sdk: ^3.2.0 dependencies: async: ^2.5.0 http_parser: ^4.0.0 meta: ^1.3.0 + web: '>=0.3.0 <0.5.0' dev_dependencies: - dart_flutter_team_lints: ^1.0.0 + dart_flutter_team_lints: ^2.0.0 fake_async: ^1.2.0 http_client_conformance_tests: path: ../http_client_conformance_tests/
diff --git a/pkgs/http/test/html/utils.dart b/pkgs/http/test/html/utils.dart index abe5808..501c621 100644 --- a/pkgs/http/test/html/utils.dart +++ b/pkgs/http/test/html/utils.dart
@@ -2,7 +2,7 @@ // 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:html'; +import 'package:web/helpers.dart'; export '../utils.dart';
diff --git a/pkgs/http/test/io/client_conformance_test.dart b/pkgs/http/test/io/client_conformance_test.dart index 5d8f7f5..20bf39f 100644 --- a/pkgs/http/test/io/client_conformance_test.dart +++ b/pkgs/http/test/io/client_conformance_test.dart
@@ -10,5 +10,6 @@ import 'package:test/test.dart'; void main() { - testAll(IOClient.new); + testAll(IOClient.new, preservesMethodCase: false // https://dartbug.com/54187 + ); }
diff --git a/pkgs/http/test/mock_client_test.dart b/pkgs/http/test/mock_client_test.dart index db561c5..625285c 100644 --- a/pkgs/http/test/mock_client_test.dart +++ b/pkgs/http/test/mock_client_test.dart
@@ -5,6 +5,7 @@ import 'dart:convert'; import 'package:http/http.dart' as http; +import 'package:http/src/request.dart'; import 'package:http/testing.dart'; import 'package:test/test.dart'; @@ -43,4 +44,25 @@ expect(await client.read(Uri.http('example.com', '/foo')), equals('you did it')); }); + + test('pngResponse with default options', () { + final response = MockClient.pngResponse(); + expect(response.statusCode, 200); + expect(response.bodyBytes.take(8), + [137, 80, 78, 71, 13, 10, 26, 10] // PNG header + ); + expect(response.request, null); + expect(response.headers, containsPair('content-type', 'image/png')); + }); + + test('pngResponse with request', () { + final request = Request('GET', Uri.https('example.com')); + final response = MockClient.pngResponse(request: request); + expect(response.statusCode, 200); + expect(response.bodyBytes.take(8), + [137, 80, 78, 71, 13, 10, 26, 10] // PNG header + ); + expect(response.request, request); + expect(response.headers, containsPair('content-type', 'image/png')); + }); }
diff --git a/pkgs/http_client_conformance_tests/bin/generate_server_wrappers.dart b/pkgs/http_client_conformance_tests/bin/generate_server_wrappers.dart index 74f9d00..6e86737 100644 --- a/pkgs/http_client_conformance_tests/bin/generate_server_wrappers.dart +++ b/pkgs/http_client_conformance_tests/bin/generate_server_wrappers.dart
@@ -10,12 +10,17 @@ import 'package:dart_style/dart_style.dart'; -const vm = '''// Generated by generate_server_wrappers.dart. Do not edit. +const _export = '''export 'server_queue_helpers.dart' + show StreamQueueOfNullableObjectExtension;'''; + +const _vm = '''// Generated by generate_server_wrappers.dart. Do not edit. import 'package:stream_channel/stream_channel.dart'; import '<server_file_placeholder>'; +$_export + /// Starts the redirect test HTTP server in the same process. Future<StreamChannel<Object?>> startServer() async { final controller = StreamChannelController<Object?>(sync: true); @@ -24,11 +29,13 @@ } '''; -const web = '''// Generated by generate_server_wrappers.dart. Do not edit. +const _web = '''// Generated by generate_server_wrappers.dart. Do not edit. import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; +$_export + /// Starts the redirect test HTTP server out-of-process. Future<StreamChannel<Object?>> startServer() async => spawnHybridUri(Uri( scheme: 'package', @@ -41,11 +48,11 @@ files.where((file) => file.path.endsWith('_server.dart')).forEach((file) { final vmPath = file.path.replaceAll('_server.dart', '_server_vm.dart'); - File(vmPath).writeAsStringSync(formatter.format(vm.replaceAll( + File(vmPath).writeAsStringSync(formatter.format(_vm.replaceAll( '<server_file_placeholder>', file.uri.pathSegments.last))); final webPath = file.path.replaceAll('_server.dart', '_server_web.dart'); - File(webPath).writeAsStringSync(formatter.format(web.replaceAll( + File(webPath).writeAsStringSync(formatter.format(_web.replaceAll( '<server_file_placeholder>', file.uri.pathSegments.last))); }); }
diff --git a/pkgs/http_client_conformance_tests/lib/http_client_conformance_tests.dart b/pkgs/http_client_conformance_tests/lib/http_client_conformance_tests.dart index 3d00319..bd83c02 100644 --- a/pkgs/http_client_conformance_tests/lib/http_client_conformance_tests.dart +++ b/pkgs/http_client_conformance_tests/lib/http_client_conformance_tests.dart
@@ -12,6 +12,7 @@ import 'src/request_body_streamed_tests.dart'; import 'src/request_body_tests.dart'; import 'src/request_headers_tests.dart'; +import 'src/request_methods_tests.dart'; import 'src/response_body_streamed_test.dart'; import 'src/response_body_tests.dart'; import 'src/response_headers_tests.dart'; @@ -27,6 +28,7 @@ export 'src/request_body_streamed_tests.dart' show testRequestBodyStreamed; export 'src/request_body_tests.dart' show testRequestBody; export 'src/request_headers_tests.dart' show testRequestHeaders; +export 'src/request_methods_tests.dart' show testRequestMethods; export 'src/response_body_streamed_test.dart' show testResponseBodyStreamed; export 'src/response_body_tests.dart' show testResponseBody; export 'src/response_headers_tests.dart' show testResponseHeaders; @@ -49,14 +51,20 @@ /// If [canWorkInIsolates] is `false` then tests that require that the [Client] /// work in Isolates other than the main isolate will be skipped. /// +/// If [preservesMethodCase] is `false` then tests that assume that the +/// [Client] preserves custom request method casing will be skipped. +/// /// The tests are run against a series of HTTP servers that are started by the /// tests. If the tests are run in the browser, then the test servers are /// started in another process. Otherwise, the test servers are run in-process. -void testAll(Client Function() clientFactory, - {bool canStreamRequestBody = true, - bool canStreamResponseBody = true, - bool redirectAlwaysAllowed = false, - bool canWorkInIsolates = true}) { +void testAll( + Client Function() clientFactory, { + bool canStreamRequestBody = true, + bool canStreamResponseBody = true, + bool redirectAlwaysAllowed = false, + bool canWorkInIsolates = true, + bool preservesMethodCase = false, +}) { testRequestBody(clientFactory()); testRequestBodyStreamed(clientFactory(), canStreamRequestBody: canStreamRequestBody); @@ -65,6 +73,7 @@ testResponseBodyStreamed(clientFactory(), canStreamResponseBody: canStreamResponseBody); testRequestHeaders(clientFactory()); + testRequestMethods(clientFactory(), preservesMethodCase: preservesMethodCase); testResponseHeaders(clientFactory()); testResponseStatusLine(clientFactory()); testRedirect(clientFactory(), redirectAlwaysAllowed: redirectAlwaysAllowed);
diff --git a/pkgs/http_client_conformance_tests/lib/src/close_tests.dart b/pkgs/http_client_conformance_tests/lib/src/close_tests.dart index 040b338..39324ad 100644 --- a/pkgs/http_client_conformance_tests/lib/src/close_tests.dart +++ b/pkgs/http_client_conformance_tests/lib/src/close_tests.dart
@@ -8,7 +8,7 @@ import 'package:test/test.dart'; import 'request_body_server_vm.dart' - if (dart.library.html) 'request_body_server_web.dart'; + if (dart.library.js_interop) 'request_body_server_web.dart'; /// Tests that the [Client] correctly implements [Client.close]. void testClose(Client Function() clientFactory) { @@ -20,7 +20,7 @@ setUpAll(() async { httpServerChannel = await startServer(); httpServerQueue = StreamQueue(httpServerChannel.stream); - host = 'localhost:${await httpServerQueue.next}'; + host = 'localhost:${await httpServerQueue.nextAsInt}'; }); tearDownAll(() => httpServerChannel.sink.add(null));
diff --git a/pkgs/http_client_conformance_tests/lib/src/compressed_response_body_server_vm.dart b/pkgs/http_client_conformance_tests/lib/src/compressed_response_body_server_vm.dart index 2bb2c16..a5ae1e0 100644 --- a/pkgs/http_client_conformance_tests/lib/src/compressed_response_body_server_vm.dart +++ b/pkgs/http_client_conformance_tests/lib/src/compressed_response_body_server_vm.dart
@@ -4,6 +4,8 @@ import 'compressed_response_body_server.dart'; +export 'server_queue_helpers.dart' show StreamQueueOfNullableObjectExtension; + /// Starts the redirect test HTTP server in the same process. Future<StreamChannel<Object?>> startServer() async { final controller = StreamChannelController<Object?>(sync: true);
diff --git a/pkgs/http_client_conformance_tests/lib/src/compressed_response_body_server_web.dart b/pkgs/http_client_conformance_tests/lib/src/compressed_response_body_server_web.dart index f880799..7b1d1a6 100644 --- a/pkgs/http_client_conformance_tests/lib/src/compressed_response_body_server_web.dart +++ b/pkgs/http_client_conformance_tests/lib/src/compressed_response_body_server_web.dart
@@ -3,6 +3,8 @@ import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; +export 'server_queue_helpers.dart' show StreamQueueOfNullableObjectExtension; + /// Starts the redirect test HTTP server out-of-process. Future<StreamChannel<Object?>> startServer() async => spawnHybridUri(Uri( scheme: 'package',
diff --git a/pkgs/http_client_conformance_tests/lib/src/compressed_response_body_tests.dart b/pkgs/http_client_conformance_tests/lib/src/compressed_response_body_tests.dart index 3ce871b..538b3ba 100644 --- a/pkgs/http_client_conformance_tests/lib/src/compressed_response_body_tests.dart +++ b/pkgs/http_client_conformance_tests/lib/src/compressed_response_body_tests.dart
@@ -8,7 +8,7 @@ import 'package:test/test.dart'; import 'compressed_response_body_server_vm.dart' - if (dart.library.html) 'compressed_response_body_server_web.dart'; + if (dart.library.js_interop) 'compressed_response_body_server_web.dart'; /// Tests that the [Client] correctly implements HTTP responses with compressed /// bodies. @@ -32,7 +32,7 @@ setUpAll(() async { httpServerChannel = await startServer(); httpServerQueue = StreamQueue(httpServerChannel.stream); - host = 'localhost:${await httpServerQueue.next}'; + host = 'localhost:${await httpServerQueue.nextAsInt}'; }); tearDownAll(() => httpServerChannel.sink.add(null));
diff --git a/pkgs/http_client_conformance_tests/lib/src/isolate_test.dart b/pkgs/http_client_conformance_tests/lib/src/isolate_test.dart index b4ac8b2..1723ab5 100644 --- a/pkgs/http_client_conformance_tests/lib/src/isolate_test.dart +++ b/pkgs/http_client_conformance_tests/lib/src/isolate_test.dart
@@ -2,7 +2,7 @@ // 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:isolate' if (dart.library.html) 'dummy_isolate.dart'; +import 'dart:isolate' if (dart.library.js_interop) 'dummy_isolate.dart'; import 'package:async/async.dart'; import 'package:http/http.dart'; @@ -10,7 +10,7 @@ import 'package:test/test.dart'; import 'request_body_server_vm.dart' - if (dart.library.html) 'request_body_server_web.dart'; + if (dart.library.js_interop) 'request_body_server_web.dart'; Future<void> _testPost(Client Function() clientFactory, String host) async { await Isolate.run( @@ -31,7 +31,7 @@ setUpAll(() async { httpServerChannel = await startServer(); httpServerQueue = StreamQueue(httpServerChannel.stream); - host = 'localhost:${await httpServerQueue.next}'; + host = 'localhost:${await httpServerQueue.nextAsInt}'; }); tearDownAll(() => httpServerChannel.sink.add(null));
diff --git a/pkgs/http_client_conformance_tests/lib/src/multiple_clients_server_vm.dart b/pkgs/http_client_conformance_tests/lib/src/multiple_clients_server_vm.dart index c689212..f00f4ba 100644 --- a/pkgs/http_client_conformance_tests/lib/src/multiple_clients_server_vm.dart +++ b/pkgs/http_client_conformance_tests/lib/src/multiple_clients_server_vm.dart
@@ -4,6 +4,8 @@ import 'multiple_clients_server.dart'; +export 'server_queue_helpers.dart' show StreamQueueOfNullableObjectExtension; + /// Starts the redirect test HTTP server in the same process. Future<StreamChannel<Object?>> startServer() async { final controller = StreamChannelController<Object?>(sync: true);
diff --git a/pkgs/http_client_conformance_tests/lib/src/multiple_clients_server_web.dart b/pkgs/http_client_conformance_tests/lib/src/multiple_clients_server_web.dart index 91cfc76..3f71aa7 100644 --- a/pkgs/http_client_conformance_tests/lib/src/multiple_clients_server_web.dart +++ b/pkgs/http_client_conformance_tests/lib/src/multiple_clients_server_web.dart
@@ -3,6 +3,8 @@ import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; +export 'server_queue_helpers.dart' show StreamQueueOfNullableObjectExtension; + /// Starts the redirect test HTTP server out-of-process. Future<StreamChannel<Object?>> startServer() async => spawnHybridUri(Uri( scheme: 'package',
diff --git a/pkgs/http_client_conformance_tests/lib/src/multiple_clients_tests.dart b/pkgs/http_client_conformance_tests/lib/src/multiple_clients_tests.dart index be9c4d9..ad40d4a 100644 --- a/pkgs/http_client_conformance_tests/lib/src/multiple_clients_tests.dart +++ b/pkgs/http_client_conformance_tests/lib/src/multiple_clients_tests.dart
@@ -8,7 +8,7 @@ import 'package:test/test.dart'; import 'multiple_clients_server_vm.dart' - if (dart.library.html) 'multiple_clients_server_web.dart'; + if (dart.library.js_interop) 'multiple_clients_server_web.dart'; /// Tests that the [Client] works correctly if there are many used /// simultaneously. @@ -21,7 +21,7 @@ setUpAll(() async { httpServerChannel = await startServer(); httpServerQueue = StreamQueue(httpServerChannel.stream); - host = 'localhost:${await httpServerQueue.next}'; + host = 'localhost:${await httpServerQueue.nextAsInt}'; }); tearDownAll(() => httpServerChannel.sink.add(null));
diff --git a/pkgs/http_client_conformance_tests/lib/src/redirect_server_vm.dart b/pkgs/http_client_conformance_tests/lib/src/redirect_server_vm.dart index 7f9cf8c..4a9450a 100644 --- a/pkgs/http_client_conformance_tests/lib/src/redirect_server_vm.dart +++ b/pkgs/http_client_conformance_tests/lib/src/redirect_server_vm.dart
@@ -4,6 +4,8 @@ import 'redirect_server.dart'; +export 'server_queue_helpers.dart' show StreamQueueOfNullableObjectExtension; + /// Starts the redirect test HTTP server in the same process. Future<StreamChannel<Object?>> startServer() async { final controller = StreamChannelController<Object?>(sync: true);
diff --git a/pkgs/http_client_conformance_tests/lib/src/redirect_server_web.dart b/pkgs/http_client_conformance_tests/lib/src/redirect_server_web.dart index 0fbe8a3..a5fb0f2 100644 --- a/pkgs/http_client_conformance_tests/lib/src/redirect_server_web.dart +++ b/pkgs/http_client_conformance_tests/lib/src/redirect_server_web.dart
@@ -3,6 +3,8 @@ import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; +export 'server_queue_helpers.dart' show StreamQueueOfNullableObjectExtension; + /// Starts the redirect test HTTP server out-of-process. Future<StreamChannel<Object?>> startServer() async => spawnHybridUri(Uri( scheme: 'package',
diff --git a/pkgs/http_client_conformance_tests/lib/src/redirect_tests.dart b/pkgs/http_client_conformance_tests/lib/src/redirect_tests.dart index e21a5c2..47a77a7 100644 --- a/pkgs/http_client_conformance_tests/lib/src/redirect_tests.dart +++ b/pkgs/http_client_conformance_tests/lib/src/redirect_tests.dart
@@ -8,7 +8,7 @@ import 'package:test/test.dart'; import 'redirect_server_vm.dart' - if (dart.library.html) 'redirect_server_web.dart'; + if (dart.library.js_interop) 'redirect_server_web.dart'; /// Tests that the [Client] correctly implements HTTP redirect logic. /// @@ -23,7 +23,7 @@ setUpAll(() async { httpServerChannel = await startServer(); httpServerQueue = StreamQueue(httpServerChannel.stream); - host = 'localhost:${await httpServerQueue.next}'; + host = 'localhost:${await httpServerQueue.nextAsInt}'; }); tearDownAll(() => httpServerChannel.sink.add(null));
diff --git a/pkgs/http_client_conformance_tests/lib/src/request_body_server_vm.dart b/pkgs/http_client_conformance_tests/lib/src/request_body_server_vm.dart index 2260766..d2e1e4a 100644 --- a/pkgs/http_client_conformance_tests/lib/src/request_body_server_vm.dart +++ b/pkgs/http_client_conformance_tests/lib/src/request_body_server_vm.dart
@@ -4,6 +4,8 @@ import 'request_body_server.dart'; +export 'server_queue_helpers.dart' show StreamQueueOfNullableObjectExtension; + /// Starts the redirect test HTTP server in the same process. Future<StreamChannel<Object?>> startServer() async { final controller = StreamChannelController<Object?>(sync: true);
diff --git a/pkgs/http_client_conformance_tests/lib/src/request_body_server_web.dart b/pkgs/http_client_conformance_tests/lib/src/request_body_server_web.dart index 250bd52..6b6ab00 100644 --- a/pkgs/http_client_conformance_tests/lib/src/request_body_server_web.dart +++ b/pkgs/http_client_conformance_tests/lib/src/request_body_server_web.dart
@@ -3,6 +3,8 @@ import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; +export 'server_queue_helpers.dart' show StreamQueueOfNullableObjectExtension; + /// Starts the redirect test HTTP server out-of-process. Future<StreamChannel<Object?>> startServer() async => spawnHybridUri(Uri( scheme: 'package',
diff --git a/pkgs/http_client_conformance_tests/lib/src/request_body_streamed_server_vm.dart b/pkgs/http_client_conformance_tests/lib/src/request_body_streamed_server_vm.dart index 9f58119..c343d68 100644 --- a/pkgs/http_client_conformance_tests/lib/src/request_body_streamed_server_vm.dart +++ b/pkgs/http_client_conformance_tests/lib/src/request_body_streamed_server_vm.dart
@@ -4,6 +4,8 @@ import 'request_body_streamed_server.dart'; +export 'server_queue_helpers.dart' show StreamQueueOfNullableObjectExtension; + /// Starts the redirect test HTTP server in the same process. Future<StreamChannel<Object?>> startServer() async { final controller = StreamChannelController<Object?>(sync: true);
diff --git a/pkgs/http_client_conformance_tests/lib/src/request_body_streamed_server_web.dart b/pkgs/http_client_conformance_tests/lib/src/request_body_streamed_server_web.dart index 97e8fbc..41477ee 100644 --- a/pkgs/http_client_conformance_tests/lib/src/request_body_streamed_server_web.dart +++ b/pkgs/http_client_conformance_tests/lib/src/request_body_streamed_server_web.dart
@@ -3,6 +3,8 @@ import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; +export 'server_queue_helpers.dart' show StreamQueueOfNullableObjectExtension; + /// Starts the redirect test HTTP server out-of-process. Future<StreamChannel<Object?>> startServer() async => spawnHybridUri(Uri( scheme: 'package',
diff --git a/pkgs/http_client_conformance_tests/lib/src/request_body_streamed_tests.dart b/pkgs/http_client_conformance_tests/lib/src/request_body_streamed_tests.dart index 560858d..0f43505 100644 --- a/pkgs/http_client_conformance_tests/lib/src/request_body_streamed_tests.dart +++ b/pkgs/http_client_conformance_tests/lib/src/request_body_streamed_tests.dart
@@ -11,7 +11,7 @@ import 'package:test/test.dart'; import 'request_body_streamed_server_vm.dart' - if (dart.library.html) 'request_body_streamed_server_web.dart'; + if (dart.library.js_interop) 'request_body_streamed_server_web.dart'; /// Tests that the [Client] correctly implements streamed request body /// uploading. @@ -29,7 +29,7 @@ setUp(() async { httpServerChannel = await startServer(); httpServerQueue = StreamQueue(httpServerChannel.stream); - host = 'localhost:${await httpServerQueue.next}'; + host = 'localhost:${await httpServerQueue.nextAsInt}'; }); tearDown(() => httpServerChannel.sink.add(null));
diff --git a/pkgs/http_client_conformance_tests/lib/src/request_body_tests.dart b/pkgs/http_client_conformance_tests/lib/src/request_body_tests.dart index e211da5..901f7f7 100644 --- a/pkgs/http_client_conformance_tests/lib/src/request_body_tests.dart +++ b/pkgs/http_client_conformance_tests/lib/src/request_body_tests.dart
@@ -10,7 +10,7 @@ import 'package:test/test.dart'; import 'request_body_server_vm.dart' - if (dart.library.html) 'request_body_server_web.dart'; + if (dart.library.js_interop) 'request_body_server_web.dart'; class _Plus2Decoder extends Converter<List<int>, String> { @override @@ -47,7 +47,7 @@ setUp(() async { httpServerChannel = await startServer(); httpServerQueue = StreamQueue(httpServerChannel.stream); - host = 'localhost:${await httpServerQueue.next}'; + host = 'localhost:${await httpServerQueue.nextAsInt}'; }); tearDown(() => httpServerChannel.sink.add(null)); @@ -260,6 +260,47 @@ expect(serverReceivedBody.codeUnits, <int>[]); }); + test('client.send() with persistentConnection', () async { + // Do five requests to verify that the connection persistance logic is + // correct. + for (var i = 0; i < 5; ++i) { + final request = Request('POST', Uri.http(host, '')) + ..headers['Content-Type'] = 'text/plain; charset=utf-8' + ..persistentConnection = true + ..body = 'Hello World $i'; + + final response = await client.send(request); + expect(response.statusCode, 200); + + final serverReceivedContentType = await httpServerQueue.next; + final serverReceivedBody = await httpServerQueue.next as String; + + expect(serverReceivedContentType, ['text/plain; charset=utf-8']); + expect(serverReceivedBody, 'Hello World $i'); + } + }); + + test('client.send() with persistentConnection and body >64K', () async { + // 64KiB is special for the HTTP network API: + // https://fetch.spec.whatwg.org/#http-network-or-cache-fetch + // See https://github.com/dart-lang/http/issues/977 + final body = ''.padLeft(64 * 1024, 'XYZ'); + + final request = Request('POST', Uri.http(host, '')) + ..headers['Content-Type'] = 'text/plain; charset=utf-8' + ..persistentConnection = true + ..body = body; + + final response = await client.send(request); + expect(response.statusCode, 200); + + final serverReceivedContentType = await httpServerQueue.next; + final serverReceivedBody = await httpServerQueue.next as String; + + expect(serverReceivedContentType, ['text/plain; charset=utf-8']); + expect(serverReceivedBody, body); + }); + test('client.send() GET with non-empty stream', () async { final request = StreamedRequest('GET', Uri.http(host, '')); request.headers['Content-Type'] = 'image/png';
diff --git a/pkgs/http_client_conformance_tests/lib/src/request_headers_server_vm.dart b/pkgs/http_client_conformance_tests/lib/src/request_headers_server_vm.dart index 44e6565..dc930dc 100644 --- a/pkgs/http_client_conformance_tests/lib/src/request_headers_server_vm.dart +++ b/pkgs/http_client_conformance_tests/lib/src/request_headers_server_vm.dart
@@ -4,6 +4,8 @@ import 'request_headers_server.dart'; +export 'server_queue_helpers.dart' show StreamQueueOfNullableObjectExtension; + /// Starts the redirect test HTTP server in the same process. Future<StreamChannel<Object?>> startServer() async { final controller = StreamChannelController<Object?>(sync: true);
diff --git a/pkgs/http_client_conformance_tests/lib/src/request_headers_server_web.dart b/pkgs/http_client_conformance_tests/lib/src/request_headers_server_web.dart index 62e8d9e..a15b69b 100644 --- a/pkgs/http_client_conformance_tests/lib/src/request_headers_server_web.dart +++ b/pkgs/http_client_conformance_tests/lib/src/request_headers_server_web.dart
@@ -3,6 +3,8 @@ import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; +export 'server_queue_helpers.dart' show StreamQueueOfNullableObjectExtension; + /// Starts the redirect test HTTP server out-of-process. Future<StreamChannel<Object?>> startServer() async => spawnHybridUri(Uri( scheme: 'package',
diff --git a/pkgs/http_client_conformance_tests/lib/src/request_headers_tests.dart b/pkgs/http_client_conformance_tests/lib/src/request_headers_tests.dart index 8adf98c..24d94d8 100644 --- a/pkgs/http_client_conformance_tests/lib/src/request_headers_tests.dart +++ b/pkgs/http_client_conformance_tests/lib/src/request_headers_tests.dart
@@ -8,7 +8,7 @@ import 'package:test/test.dart'; import 'request_headers_server_vm.dart' - if (dart.library.html) 'request_headers_server_web.dart'; + if (dart.library.js_interop) 'request_headers_server_web.dart'; /// Tests that the [Client] correctly sends headers in the request. void testRequestHeaders(Client client) async { @@ -20,7 +20,7 @@ setUpAll(() async { httpServerChannel = await startServer(); httpServerQueue = StreamQueue(httpServerChannel.stream); - host = 'localhost:${await httpServerQueue.next}'; + host = 'localhost:${await httpServerQueue.nextAsInt}'; }); tearDownAll(() => httpServerChannel.sink.add(null));
diff --git a/pkgs/http_client_conformance_tests/lib/src/request_methods_server.dart b/pkgs/http_client_conformance_tests/lib/src/request_methods_server.dart new file mode 100644 index 0000000..bf05ec0 --- /dev/null +++ b/pkgs/http_client_conformance_tests/lib/src/request_methods_server.dart
@@ -0,0 +1,41 @@ +// Copyright (c) 2023, 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:async'; +import 'dart:io'; + +import 'package:stream_channel/stream_channel.dart'; + +/// Starts an HTTP server that captures the request headers. +/// +/// Channel protocol: +/// On Startup: +/// - send port +/// On Request Received: +/// - send the received request method (e.g. GET) as a String +/// When Receive Anything: +/// - exit +void hybridMain(StreamChannel<Object?> channel) async { + late HttpServer server; + + server = (await HttpServer.bind('localhost', 0)) + ..listen((request) async { + request.response.headers.set('Access-Control-Allow-Origin', '*'); + if (request.method == 'OPTIONS') { + // Handle a CORS preflight request: + // https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#preflighted_requests + request.response.headers + ..set('Access-Control-Allow-Methods', '*') + ..set('Access-Control-Allow-Headers', '*'); + } else { + channel.sink.add(request.method); + } + unawaited(request.response.close()); + }); + + channel.sink.add(server.port); + await channel + .stream.first; // Any writes indicates that the server should exit. + unawaited(server.close()); +}
diff --git a/pkgs/http_client_conformance_tests/lib/src/request_methods_server_vm.dart b/pkgs/http_client_conformance_tests/lib/src/request_methods_server_vm.dart new file mode 100644 index 0000000..fa25735 --- /dev/null +++ b/pkgs/http_client_conformance_tests/lib/src/request_methods_server_vm.dart
@@ -0,0 +1,14 @@ +// Generated by generate_server_wrappers.dart. Do not edit. + +import 'package:stream_channel/stream_channel.dart'; + +import 'request_methods_server.dart'; + +export 'server_queue_helpers.dart' show StreamQueueOfNullableObjectExtension; + +/// Starts the redirect test HTTP server in the same process. +Future<StreamChannel<Object?>> startServer() async { + final controller = StreamChannelController<Object?>(sync: true); + hybridMain(controller.foreign); + return controller.local; +}
diff --git a/pkgs/http_client_conformance_tests/lib/src/request_methods_server_web.dart b/pkgs/http_client_conformance_tests/lib/src/request_methods_server_web.dart new file mode 100644 index 0000000..f9c924e --- /dev/null +++ b/pkgs/http_client_conformance_tests/lib/src/request_methods_server_web.dart
@@ -0,0 +1,11 @@ +// Generated by generate_server_wrappers.dart. Do not edit. + +import 'package:stream_channel/stream_channel.dart'; +import 'package:test/test.dart'; + +export 'server_queue_helpers.dart' show StreamQueueOfNullableObjectExtension; + +/// Starts the redirect test HTTP server out-of-process. +Future<StreamChannel<Object?>> startServer() async => spawnHybridUri(Uri( + scheme: 'package', + path: 'http_client_conformance_tests/src/request_methods_server.dart'));
diff --git a/pkgs/http_client_conformance_tests/lib/src/request_methods_tests.dart b/pkgs/http_client_conformance_tests/lib/src/request_methods_tests.dart new file mode 100644 index 0000000..802f57e --- /dev/null +++ b/pkgs/http_client_conformance_tests/lib/src/request_methods_tests.dart
@@ -0,0 +1,88 @@ +// Copyright (c) 2023, 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 'package:async/async.dart'; +import 'package:http/http.dart'; +import 'package:stream_channel/stream_channel.dart'; +import 'package:test/test.dart'; + +import 'request_methods_server_vm.dart' + if (dart.library.js_interop) 'request_methods_server_web.dart'; + +/// Tests that the [Client] correctly sends HTTP request methods +/// (e.g. GET, HEAD). +/// +/// If [preservesMethodCase] is `false` then tests that assume that the +/// [Client] preserves custom request method casing will be skipped. +void testRequestMethods(Client client, + {bool preservesMethodCase = true}) async { + group('request methods', () { + late final String host; + late final StreamChannel<Object?> httpServerChannel; + late final StreamQueue<Object?> httpServerQueue; + + setUpAll(() async { + httpServerChannel = await startServer(); + httpServerQueue = StreamQueue(httpServerChannel.stream); + host = 'localhost:${await httpServerQueue.nextAsInt}'; + }); + tearDownAll(() => httpServerChannel.sink.add(null)); + + test('custom method - not case preserving', () async { + await client.send(Request( + 'CuStOm', + Uri.http(host, ''), + )); + final method = await httpServerQueue.next as String; + expect('CUSTOM', method.toUpperCase()); + }); + + test('custom method case preserving', () async { + await client.send(Request( + 'CuStOm', + Uri.http(host, ''), + )); + final method = await httpServerQueue.next as String; + expect('CuStOm', method); + }, + skip: preservesMethodCase + ? false + : 'does not preserve HTTP request method case'); + + test('delete', () async { + await client.delete(Uri.http(host, '')); + final method = await httpServerQueue.next as String; + expect('DELETE', method); + }); + + test('get', () async { + await client.get(Uri.http(host, '')); + final method = await httpServerQueue.next as String; + expect('GET', method); + }); + test('head', () async { + await client.head(Uri.http(host, '')); + final method = await httpServerQueue.next as String; + expect('HEAD', method); + }); + + test('patch', () async { + await client.patch(Uri.http(host, '')); + final method = await httpServerQueue.next as String; + expect('PATCH', method); + }); + + test('post', () async { + await client.post(Uri.http(host, '')); + final method = await httpServerQueue.next as String; + expect('POST', method); + }); + + test('put', () async { + await client.put(Uri.http(host, '')); + final method = await httpServerQueue.next as String; + expect('PUT', method); + }); + }); +}
diff --git a/pkgs/http_client_conformance_tests/lib/src/response_body_server_vm.dart b/pkgs/http_client_conformance_tests/lib/src/response_body_server_vm.dart index f88e065..a12b6fb 100644 --- a/pkgs/http_client_conformance_tests/lib/src/response_body_server_vm.dart +++ b/pkgs/http_client_conformance_tests/lib/src/response_body_server_vm.dart
@@ -4,6 +4,8 @@ import 'response_body_server.dart'; +export 'server_queue_helpers.dart' show StreamQueueOfNullableObjectExtension; + /// Starts the redirect test HTTP server in the same process. Future<StreamChannel<Object?>> startServer() async { final controller = StreamChannelController<Object?>(sync: true);
diff --git a/pkgs/http_client_conformance_tests/lib/src/response_body_server_web.dart b/pkgs/http_client_conformance_tests/lib/src/response_body_server_web.dart index 94bdaa9..4d23a48 100644 --- a/pkgs/http_client_conformance_tests/lib/src/response_body_server_web.dart +++ b/pkgs/http_client_conformance_tests/lib/src/response_body_server_web.dart
@@ -3,6 +3,8 @@ import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; +export 'server_queue_helpers.dart' show StreamQueueOfNullableObjectExtension; + /// Starts the redirect test HTTP server out-of-process. Future<StreamChannel<Object?>> startServer() async => spawnHybridUri(Uri( scheme: 'package',
diff --git a/pkgs/http_client_conformance_tests/lib/src/response_body_streamed_server_vm.dart b/pkgs/http_client_conformance_tests/lib/src/response_body_streamed_server_vm.dart index 01d84a1..4e4eaff 100644 --- a/pkgs/http_client_conformance_tests/lib/src/response_body_streamed_server_vm.dart +++ b/pkgs/http_client_conformance_tests/lib/src/response_body_streamed_server_vm.dart
@@ -4,6 +4,8 @@ import 'response_body_streamed_server.dart'; +export 'server_queue_helpers.dart' show StreamQueueOfNullableObjectExtension; + /// Starts the redirect test HTTP server in the same process. Future<StreamChannel<Object?>> startServer() async { final controller = StreamChannelController<Object?>(sync: true);
diff --git a/pkgs/http_client_conformance_tests/lib/src/response_body_streamed_server_web.dart b/pkgs/http_client_conformance_tests/lib/src/response_body_streamed_server_web.dart index a9ce00b..e04ebd6 100644 --- a/pkgs/http_client_conformance_tests/lib/src/response_body_streamed_server_web.dart +++ b/pkgs/http_client_conformance_tests/lib/src/response_body_streamed_server_web.dart
@@ -3,6 +3,8 @@ import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; +export 'server_queue_helpers.dart' show StreamQueueOfNullableObjectExtension; + /// Starts the redirect test HTTP server out-of-process. Future<StreamChannel<Object?>> startServer() async => spawnHybridUri(Uri( scheme: 'package',
diff --git a/pkgs/http_client_conformance_tests/lib/src/response_body_streamed_test.dart b/pkgs/http_client_conformance_tests/lib/src/response_body_streamed_test.dart index 28686fa..f355d6c 100644 --- a/pkgs/http_client_conformance_tests/lib/src/response_body_streamed_test.dart +++ b/pkgs/http_client_conformance_tests/lib/src/response_body_streamed_test.dart
@@ -10,7 +10,7 @@ import 'package:test/test.dart'; import 'response_body_streamed_server_vm.dart' - if (dart.library.html) 'response_body_streamed_server_web.dart'; + if (dart.library.js_interop) 'response_body_streamed_server_web.dart'; /// Tests that the [Client] correctly implements HTTP responses with bodies of /// unbounded size. @@ -28,7 +28,7 @@ setUpAll(() async { httpServerChannel = await startServer(); httpServerQueue = StreamQueue(httpServerChannel.stream); - host = 'localhost:${await httpServerQueue.next}'; + host = 'localhost:${await httpServerQueue.nextAsInt}'; }); tearDownAll(() => httpServerChannel.sink.add(null));
diff --git a/pkgs/http_client_conformance_tests/lib/src/response_body_tests.dart b/pkgs/http_client_conformance_tests/lib/src/response_body_tests.dart index 91ee549..34c29f6 100644 --- a/pkgs/http_client_conformance_tests/lib/src/response_body_tests.dart +++ b/pkgs/http_client_conformance_tests/lib/src/response_body_tests.dart
@@ -8,7 +8,7 @@ import 'package:test/test.dart'; import 'response_body_server_vm.dart' - if (dart.library.html) 'response_body_server_web.dart'; + if (dart.library.js_interop) 'response_body_server_web.dart'; /// Tests that the [Client] correctly implements HTTP responses with bodies. /// @@ -26,7 +26,7 @@ setUpAll(() async { httpServerChannel = await startServer(); httpServerQueue = StreamQueue(httpServerChannel.stream); - host = 'localhost:${await httpServerQueue.next}'; + host = 'localhost:${await httpServerQueue.nextAsInt}'; }); tearDownAll(() => httpServerChannel.sink.add(null));
diff --git a/pkgs/http_client_conformance_tests/lib/src/response_headers_server_vm.dart b/pkgs/http_client_conformance_tests/lib/src/response_headers_server_vm.dart index b7d4a01..c99a021 100644 --- a/pkgs/http_client_conformance_tests/lib/src/response_headers_server_vm.dart +++ b/pkgs/http_client_conformance_tests/lib/src/response_headers_server_vm.dart
@@ -4,6 +4,8 @@ import 'response_headers_server.dart'; +export 'server_queue_helpers.dart' show StreamQueueOfNullableObjectExtension; + /// Starts the redirect test HTTP server in the same process. Future<StreamChannel<Object?>> startServer() async { final controller = StreamChannelController<Object?>(sync: true);
diff --git a/pkgs/http_client_conformance_tests/lib/src/response_headers_server_web.dart b/pkgs/http_client_conformance_tests/lib/src/response_headers_server_web.dart index 8ee938a..0e6dabd 100644 --- a/pkgs/http_client_conformance_tests/lib/src/response_headers_server_web.dart +++ b/pkgs/http_client_conformance_tests/lib/src/response_headers_server_web.dart
@@ -3,6 +3,8 @@ import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; +export 'server_queue_helpers.dart' show StreamQueueOfNullableObjectExtension; + /// Starts the redirect test HTTP server out-of-process. Future<StreamChannel<Object?>> startServer() async => spawnHybridUri(Uri( scheme: 'package',
diff --git a/pkgs/http_client_conformance_tests/lib/src/response_headers_tests.dart b/pkgs/http_client_conformance_tests/lib/src/response_headers_tests.dart index 4f92042..84f0fb6 100644 --- a/pkgs/http_client_conformance_tests/lib/src/response_headers_tests.dart +++ b/pkgs/http_client_conformance_tests/lib/src/response_headers_tests.dart
@@ -8,7 +8,7 @@ import 'package:test/test.dart'; import 'response_headers_server_vm.dart' - if (dart.library.html) 'response_headers_server_web.dart'; + if (dart.library.js_interop) 'response_headers_server_web.dart'; /// Tests that the [Client] correctly processes response headers. void testResponseHeaders(Client client) async { @@ -20,7 +20,7 @@ setUp(() async { httpServerChannel = await startServer(); httpServerQueue = StreamQueue(httpServerChannel.stream); - host = 'localhost:${await httpServerQueue.next}'; + host = 'localhost:${await httpServerQueue.nextAsInt}'; }); test('single header', () async {
diff --git a/pkgs/http_client_conformance_tests/lib/src/response_status_line_server_vm.dart b/pkgs/http_client_conformance_tests/lib/src/response_status_line_server_vm.dart index ff2ea84..053bd11 100644 --- a/pkgs/http_client_conformance_tests/lib/src/response_status_line_server_vm.dart +++ b/pkgs/http_client_conformance_tests/lib/src/response_status_line_server_vm.dart
@@ -4,6 +4,8 @@ import 'response_status_line_server.dart'; +export 'server_queue_helpers.dart' show StreamQueueOfNullableObjectExtension; + /// Starts the redirect test HTTP server in the same process. Future<StreamChannel<Object?>> startServer() async { final controller = StreamChannelController<Object?>(sync: true);
diff --git a/pkgs/http_client_conformance_tests/lib/src/response_status_line_server_web.dart b/pkgs/http_client_conformance_tests/lib/src/response_status_line_server_web.dart index f1ebbcb..d70a325 100644 --- a/pkgs/http_client_conformance_tests/lib/src/response_status_line_server_web.dart +++ b/pkgs/http_client_conformance_tests/lib/src/response_status_line_server_web.dart
@@ -3,6 +3,8 @@ import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; +export 'server_queue_helpers.dart' show StreamQueueOfNullableObjectExtension; + /// Starts the redirect test HTTP server out-of-process. Future<StreamChannel<Object?>> startServer() async => spawnHybridUri(Uri( scheme: 'package',
diff --git a/pkgs/http_client_conformance_tests/lib/src/response_status_line_tests.dart b/pkgs/http_client_conformance_tests/lib/src/response_status_line_tests.dart index b618fa0..6eb70c5 100644 --- a/pkgs/http_client_conformance_tests/lib/src/response_status_line_tests.dart +++ b/pkgs/http_client_conformance_tests/lib/src/response_status_line_tests.dart
@@ -8,7 +8,7 @@ import 'package:test/test.dart'; import 'response_status_line_server_vm.dart' - if (dart.library.html) 'response_status_line_server_web.dart'; + if (dart.library.js_interop) 'response_status_line_server_web.dart'; /// Tests that the [Client] correctly processes the response status line (e.g. /// 'HTTP/1.1 200 OK\r\n'). @@ -23,7 +23,7 @@ setUp(() async { httpServerChannel = await startServer(); httpServerQueue = StreamQueue(httpServerChannel.stream); - host = 'localhost:${await httpServerQueue.next}'; + host = 'localhost:${await httpServerQueue.nextAsInt}'; }); test('complete', () async {
diff --git a/pkgs/http_client_conformance_tests/lib/src/server_errors_server_vm.dart b/pkgs/http_client_conformance_tests/lib/src/server_errors_server_vm.dart index 257adcf..e5aa09f 100644 --- a/pkgs/http_client_conformance_tests/lib/src/server_errors_server_vm.dart +++ b/pkgs/http_client_conformance_tests/lib/src/server_errors_server_vm.dart
@@ -4,6 +4,8 @@ import 'server_errors_server.dart'; +export 'server_queue_helpers.dart' show StreamQueueOfNullableObjectExtension; + /// Starts the redirect test HTTP server in the same process. Future<StreamChannel<Object?>> startServer() async { final controller = StreamChannelController<Object?>(sync: true);
diff --git a/pkgs/http_client_conformance_tests/lib/src/server_errors_server_web.dart b/pkgs/http_client_conformance_tests/lib/src/server_errors_server_web.dart index cc763e3..9614f36 100644 --- a/pkgs/http_client_conformance_tests/lib/src/server_errors_server_web.dart +++ b/pkgs/http_client_conformance_tests/lib/src/server_errors_server_web.dart
@@ -3,6 +3,8 @@ import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; +export 'server_queue_helpers.dart' show StreamQueueOfNullableObjectExtension; + /// Starts the redirect test HTTP server out-of-process. Future<StreamChannel<Object?>> startServer() async => spawnHybridUri(Uri( scheme: 'package',
diff --git a/pkgs/http_client_conformance_tests/lib/src/server_errors_test.dart b/pkgs/http_client_conformance_tests/lib/src/server_errors_test.dart index 65de499..1a83696 100644 --- a/pkgs/http_client_conformance_tests/lib/src/server_errors_test.dart +++ b/pkgs/http_client_conformance_tests/lib/src/server_errors_test.dart
@@ -8,7 +8,7 @@ import 'package:test/test.dart'; import 'server_errors_server_vm.dart' - if (dart.library.html) 'server_errors_server_web.dart'; + if (dart.library.js_interop) 'server_errors_server_web.dart'; /// Tests that the [Client] correctly handles server errors. void testServerErrors(Client client, {bool redirectAlwaysAllowed = false}) { @@ -20,7 +20,7 @@ setUpAll(() async { httpServerChannel = await startServer(); httpServerQueue = StreamQueue(httpServerChannel.stream); - host = 'localhost:${await httpServerQueue.next}'; + host = 'localhost:${await httpServerQueue.nextAsInt}'; }); tearDownAll(() => httpServerChannel.sink.add(null));
diff --git a/pkgs/http_client_conformance_tests/lib/src/server_queue_helpers.dart b/pkgs/http_client_conformance_tests/lib/src/server_queue_helpers.dart new file mode 100644 index 0000000..df87ddd --- /dev/null +++ b/pkgs/http_client_conformance_tests/lib/src/server_queue_helpers.dart
@@ -0,0 +1,10 @@ +// Copyright (c) 2023, 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 'package:async/async.dart'; + +extension StreamQueueOfNullableObjectExtension on StreamQueue<Object?> { + /// When run under dart2wasm, JSON numbers are always returned as [double]. + Future<int> get nextAsInt async => ((await next) as num).toInt(); +}
diff --git a/pkgs/http_client_conformance_tests/pubspec.yaml b/pkgs/http_client_conformance_tests/pubspec.yaml index 4264059..520f131 100644 --- a/pkgs/http_client_conformance_tests/pubspec.yaml +++ b/pkgs/http_client_conformance_tests/pubspec.yaml
@@ -2,18 +2,19 @@ description: >- A library that tests whether implementations of package:http's `Client` class behave as expected. -publish_to: none repository: https://github.com/dart-lang/http/tree/master/pkgs/http_client_conformance_tests +publish_to: none + environment: - sdk: '>=2.19.0 <3.0.0' + sdk: ^3.0.0 dependencies: async: ^2.8.2 dart_style: ^2.2.3 - http: '>=0.13.4 <2.0.0' + http: ^1.0.0 stream_channel: ^2.1.1 test: ^1.21.2 dev_dependencies: - dart_flutter_team_lints: ^1.0.0 + dart_flutter_team_lints: ^2.0.0
diff --git a/pkgs/http_profile/pubspec.yaml b/pkgs/http_profile/pubspec.yaml index 9e3b69e..1b5891e 100644 --- a/pkgs/http_profile/pubspec.yaml +++ b/pkgs/http_profile/pubspec.yaml
@@ -1,10 +1,11 @@ name: http_profile description: >- - A library used by HTTP client authors to integrate with the DevTools - Network tab. -publish_to: none + A library used by HTTP client authors to integrate with the DevTools Network + tab. repository: https://github.com/dart-lang/http/tree/master/pkgs/http_profile +publish_to: none + environment: sdk: ^3.0.0
diff --git a/pkgs/java_http/pubspec.yaml b/pkgs/java_http/pubspec.yaml index ecba4f1..c04429a 100644 --- a/pkgs/java_http/pubspec.yaml +++ b/pkgs/java_http/pubspec.yaml
@@ -1,7 +1,9 @@ name: java_http -description: A Dart package for making HTTP requests using java.net.HttpURLConnection. version: 0.0.1 +description: >- + A Dart package for making HTTP requests using java.net.HttpURLConnection. repository: https://github.com/dart-lang/http/tree/master/pkgs/java_http + publish_to: none environment: @@ -14,9 +16,8 @@ path: ^1.8.0 dev_dependencies: - dart_flutter_team_lints: ^1.0.0 + dart_flutter_team_lints: ^2.0.0 http_client_conformance_tests: path: ../http_client_conformance_tests/ jnigen: ^0.5.0 - lints: ^2.0.0 test: ^1.21.0
diff --git a/tool/ci.sh b/tool/ci.sh index 26395d2..d4cc8d2 100755 --- a/tool/ci.sh +++ b/tool/ci.sh
@@ -1,9 +1,10 @@ #!/bin/bash -# Created with package:mono_repo v6.6.0 +# Created with package:mono_repo v6.6.1 # Support built in commands on windows out of the box. + # When it is a flutter repo (check the pubspec.yaml for "sdk: flutter") -# then "flutter" is called instead of "pub". +# then "flutter pub" is called instead of "dart pub". # This assumes that the Flutter SDK has been installed in a previous step. function pub() { if grep -Fq "sdk: flutter" "${PWD}/pubspec.yaml"; then @@ -12,18 +13,13 @@ command dart pub "$@" fi } -# When it is a flutter repo (check the pubspec.yaml for "sdk: flutter") -# then "flutter" is called instead of "pub". -# This assumes that the Flutter SDK has been installed in a previous step. + function format() { - if grep -Fq "sdk: flutter" "${PWD}/pubspec.yaml"; then - command flutter format "$@" - else - command dart format "$@" - fi + command dart format "$@" } + # When it is a flutter repo (check the pubspec.yaml for "sdk: flutter") -# then "flutter" is called instead of "pub". +# then "flutter analyze" is called instead of "dart analyze". # This assumes that the Flutter SDK has been installed in a previous step. function analyze() { if grep -Fq "sdk: flutter" "${PWD}/pubspec.yaml"; then @@ -99,6 +95,10 @@ echo 'dart test --platform chrome' dart test --platform chrome || EXIT_CODE=$? ;; + test_4) + echo 'dart test --test-randomize-ordering-seed=random -p chrome -c dart2wasm' + dart test --test-randomize-ordering-seed=random -p chrome -c dart2wasm || EXIT_CODE=$? + ;; *) echo -e "\033[31mUnknown TASK '${TASK}' - TERMINATING JOB\033[0m" exit 64