Skip to content

Commit

Permalink
🚀 Supports the WASM environment (#2274)
Browse files Browse the repository at this point in the history
Resolves #2265

Signed-off-by: Alex Li <[email protected]>
  • Loading branch information
AlexV525 authored Aug 13, 2024
1 parent ff4eb26 commit 050b857
Show file tree
Hide file tree
Showing 21 changed files with 88 additions and 536 deletions.
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
- 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

0 comments on commit 050b857

Please sign in to comment.