Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🚀 Supports the WASM environment #2274

Merged
merged 9 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ jobs:
- uses: bluefireteam/melos-action@v3
with:
run-bootstrap: false
- name: Remove dio_web_adapter overrides
if: ${{ matrix.sdk == 'min' }}
run: rm -rf plugins/web_adapter
kuhnroyal marked this conversation as resolved.
Show resolved Hide resolved
- name: Check satisfied packages
run: |
dart ./scripts/melos_packages.dart
Expand Down
1 change: 1 addition & 0 deletions dio/lib/src/adapter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'dart:typed_data';
import 'package:meta/meta.dart';

import 'adapters/io_adapter.dart'
if (dart.library.js_interop) 'adapters/browser_adapter.dart'
if (dart.library.html) 'adapters/browser_adapter.dart' as adapter;
import 'headers.dart';
import 'options.dart';
Expand Down
4 changes: 3 additions & 1 deletion dio/lib/src/compute/compute.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@

import 'dart:async';

import 'compute_io.dart' if (dart.library.html) 'compute_web.dart' as _c;
import 'compute_io.dart'
if (dart.library.js_interop) 'compute_web.dart'
if (dart.library.html) 'compute_web.dart' as _c;

/// Signature for the callback passed to [compute].
///
Expand Down
1 change: 1 addition & 0 deletions dio/lib/src/dio.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:async';
import 'adapter.dart';
import 'cancel_token.dart';
import 'dio/dio_for_native.dart'
if (dart.library.js_interop) 'dio/dio_for_browser.dart'
if (dart.library.html) 'dio/dio_for_browser.dart';
import 'dio_mixin.dart';
import 'headers.dart';
Expand Down
1 change: 1 addition & 0 deletions dio/lib/src/dio_mixin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import 'headers.dart';
import 'interceptors/imply_content_type.dart';
import 'options.dart';
import 'progress_stream/io_progress_stream.dart'
if (dart.library.js_interop) 'progress_stream/browser_progress_stream.dart'
if (dart.library.html) 'progress_stream/browser_progress_stream.dart';
import 'response.dart';
import 'response/response_stream_handler.dart';
Expand Down
1 change: 1 addition & 0 deletions dio/lib/src/multipart_file.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'dart:typed_data' show Uint8List;
import 'package:http_parser/http_parser.dart' show MediaType;

import 'multipart_file/io_multipart_file.dart'
if (dart.library.js_interop) 'multipart_file/browser_multipart_file.dart'
if (dart.library.html) 'multipart_file/browser_multipart_file.dart';
import 'utils.dart';

Expand Down
6 changes: 3 additions & 3 deletions example_flutter_app/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

android {
compileSdkVersion 33
compileSdkVersion flutter.compileSdkVersion

defaultConfig {
applicationId "com.example.flutterApp"
minSdkVersion 16
targetSdkVersion 33
minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
Expand Down
5 changes: 5 additions & 0 deletions melos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -79,18 +79,23 @@ scripts:
run: melos run test:web:single
env:
TEST_PLATFORM: chrome
WITH_WASM: true
test:web:firefox:
name: Dart Web tests in firefox
run: melos run test:web:single
env:
TEST_PLATFORM: firefox
WITH_WASM: false
test:web:single:
name: Dart Web tests in a browser
exec: |
if [ "$TARGET_DART_SDK" = "min" ]; then
dart test --platform ${TEST_PLATFORM} --preset=${TEST_PRESET:-default},${TARGET_DART_SDK:-stable} --chain-stack-traces
else
dart test --platform ${TEST_PLATFORM} --coverage=coverage/${TEST_PLATFORM} --preset=${TEST_PRESET:-default},${TARGET_DART_SDK:-stable} --chain-stack-traces
if [ "$WITH_WASM" = "true" ]; then
dart test --platform ${TEST_PLATFORM} --coverage=coverage/${TEST_PLATFORM} --preset=${TEST_PRESET:-default},${TARGET_DART_SDK:-stable} --chain-stack-traces --compiler=dart2wasm
fi
fi
packageFilters:
flutter: false
Expand Down
3 changes: 2 additions & 1 deletion plugins/web_adapter/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## Unreleased

*None.*
- Supports the WASM environment. Users should upgrade the adapter with
`dart pub upgrade` or `flutter pub upgrade` to use the WASM-supported version.

## 1.0.1

Expand Down
27 changes: 5 additions & 22 deletions plugins/web_adapter/lib/dio_web_adapter.dart
Original file line number Diff line number Diff line change
@@ -1,24 +1,7 @@
library dio_web_adapter;

// We separate implementations as `html` and `js_interop`
// only for convenient diffing.

export 'src/html/adapter.dart';

// export 'src/js_interop/adapter.dart';

export 'src/html/compute.dart';

// export 'src/js_interop/compute.dart';

export 'src/html/dio_impl.dart';

// export 'src/js_interop/dio_impl.dart';

export 'src/html/multipart_file.dart';

// export 'src/js_interop/multipart_file.dart';

export 'src/html/progress_stream.dart';

// export 'src/js_interop/progress_stream.dart';
export 'src/adapter.dart';
export 'src/compute.dart';
export 'src/dio_impl.dart';
export 'src/multipart_file.dart';
export 'src/progress_stream.dart';
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
// export '../js_interop/adapter.dart';

import 'dart:async';
import 'dart:convert';
import 'dart:html';
import 'dart:js_interop';
import 'dart:typed_data';

import 'package:dio/dio.dart';
import 'package:dio/src/utils.dart';
import 'package:meta/meta.dart';
import 'package:web/web.dart' as web;

BrowserHttpClientAdapter createAdapter() => BrowserHttpClientAdapter();

Expand All @@ -17,7 +16,7 @@ class BrowserHttpClientAdapter implements HttpClientAdapter {

/// These are aborted if the client is closed.
@visibleForTesting
final xhrs = <HttpRequest>{};
final xhrs = <web.XMLHttpRequest>{};

/// Whether to send credentials such as cookies or authorization headers for
/// cross-site requests.
Expand All @@ -34,7 +33,7 @@ class BrowserHttpClientAdapter implements HttpClientAdapter {
Stream<Uint8List>? requestStream,
Future<void>? cancelFuture,
) async {
final xhr = HttpRequest();
final xhr = web.XMLHttpRequest();
xhrs.add(xhr);
xhr
..open(options.method, '${options.uri}')
Expand Down Expand Up @@ -65,16 +64,16 @@ class BrowserHttpClientAdapter implements HttpClientAdapter {
final completer = Completer<ResponseBody>();

xhr.onLoad.first.then((_) {
final Uint8List body = (xhr.response as ByteBuffer).asUint8List();
final ByteBuffer body = (xhr.response as JSArrayBuffer).toDart;
completer.complete(
ResponseBody.fromBytes(
body,
xhr.status!,
headers: xhr.responseHeaders.map((k, v) => MapEntry(k, v.split(','))),
body.asUint8List(),
xhr.status,
headers: xhr.getResponseHeaders(),
statusMessage: xhr.statusText,
isRedirect: xhr.status == 302 ||
xhr.status == 301 ||
options.uri.toString() != xhr.responseUrl,
options.uri.toString() != xhr.responseURL,
),
);
});
Expand Down Expand Up @@ -108,16 +107,19 @@ class BrowserHttpClientAdapter implements HttpClientAdapter {
// Upload progress events only get triggered if the request body exists,
// so we can check it beforehand.
if (requestStream != null) {
final xhrUploadProgressStream =
web.EventStreamProviders.progressEvent.forTarget(xhr.upload);

if (connectTimeoutTimer != null) {
xhr.upload.onProgress.listen((event) {
xhrUploadProgressStream.listen((_) {
connectTimeoutTimer?.cancel();
connectTimeoutTimer = null;
});
}

if (sendTimeout > Duration.zero) {
final uploadStopwatch = Stopwatch();
xhr.upload.onProgress.listen((event) {
xhrUploadProgressStream.listen((_) {
if (!uploadStopwatch.isRunning) {
uploadStopwatch.start();
}
Expand All @@ -138,12 +140,23 @@ class BrowserHttpClientAdapter implements HttpClientAdapter {

final onSendProgress = options.onSendProgress;
if (onSendProgress != null) {
xhr.upload.onProgress.listen((event) {
if (event.loaded != null && event.total != null) {
onSendProgress(event.loaded!, event.total!);
}
xhrUploadProgressStream.listen((event) {
onSendProgress(event.loaded, event.total);
});
}
} else {
if (sendTimeout > Duration.zero) {
warningLog(
'sendTimeout cannot be used without a request body to send',
StackTrace.current,
);
}
if (options.onSendProgress != null) {
warningLog(
'onSendProgress cannot be used without a request body to send',
StackTrace.current,
);
}
}

final receiveStopwatch = Stopwatch();
Expand Down Expand Up @@ -180,16 +193,14 @@ class BrowserHttpClientAdapter implements HttpClientAdapter {
}

xhr.onProgress.listen(
(ProgressEvent event) {
(event) {
if (connectTimeoutTimer != null) {
connectTimeoutTimer!.cancel();
connectTimeoutTimer = null;
}
watchReceiveTimeout();
if (options.onReceiveProgress != null &&
event.loaded != null &&
event.total != null) {
options.onReceiveProgress!(event.loaded!, event.total!);
if (options.onReceiveProgress != null) {
options.onReceiveProgress!(event.loaded, event.total);
}
},
onDone: () => stopWatchReceiveTimeout(),
Expand All @@ -210,7 +221,7 @@ class BrowserHttpClientAdapter implements HttpClientAdapter {
);
});

xhr.onTimeout.first.then((_) {
web.EventStreamProviders.timeoutEvent.forTarget(xhr).first.then((_) {
final isConnectTimeout = connectTimeoutTimer != null;
if (connectTimeoutTimer != null) {
connectTimeoutTimer?.cancel();
Expand All @@ -236,8 +247,8 @@ class BrowserHttpClientAdapter implements HttpClientAdapter {
});

cancelFuture?.then((_) {
if (xhr.readyState < HttpRequest.DONE &&
xhr.readyState > HttpRequest.UNSENT) {
if (xhr.readyState < web.XMLHttpRequest.DONE &&
xhr.readyState > web.XMLHttpRequest.UNSENT) {
connectTimeoutTimer?.cancel();
try {
xhr.abort();
Expand Down Expand Up @@ -274,7 +285,7 @@ class BrowserHttpClientAdapter implements HttpClientAdapter {
cancelOnError: true,
);
final bytes = await completer.future;
xhr.send(bytes);
xhr.send(bytes.toJS);
} else {
xhr.send();
}
Expand All @@ -296,3 +307,28 @@ class BrowserHttpClientAdapter implements HttpClientAdapter {
xhrs.clear();
}
}

extension on web.XMLHttpRequest {
Map<String, List<String>> getResponseHeaders() {
final headersString = getAllResponseHeaders();
final headers = <String, List<String>>{};
if (headersString.isEmpty) {
return headers;
}
final headersList = headersString.split('\r\n');
for (final header in headersList) {
if (header.isEmpty) {
continue;
}

final splitIdx = header.indexOf(': ');
if (splitIdx == -1) {
continue;
}
final key = header.substring(0, splitIdx).toLowerCase();
final value = header.substring(splitIdx + 2);
(headers[key] ??= []).add(value);
}
return headers;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@
// The file is intentionally not refactored so that it is easier to keep the
// compute package up to date with Flutter's implementation.

// export '../js_interop/compute.dart';

import 'package:dio/src/compute/compute.dart' as c;

/// The dart:html implementation of [c.compute].
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// export '../js_interop/dio_impl.dart';

import 'package:dio/dio.dart';

import 'adapter.dart';
Expand Down
Loading