Skip to content

Commit

Permalink
⚡️ Randomly split
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexV525 committed May 22, 2024
1 parent 0eab30a commit 3700d4c
Show file tree
Hide file tree
Showing 41 changed files with 1,128 additions and 448 deletions.
1 change: 1 addition & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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
315 changes: 2 additions & 313 deletions dio/lib/src/adapters/browser_adapter.dart
Original file line number Diff line number Diff line change
@@ -1,313 +1,2 @@
import 'dart:async';
import 'dart:convert';
import 'dart:html';
import 'dart:typed_data';

import 'package:meta/meta.dart';

import '../adapter.dart';
import '../dio_exception.dart';
import '../headers.dart';
import '../options.dart';
import '../utils.dart';

HttpClientAdapter createAdapter() => BrowserHttpClientAdapter();

/// The default [HttpClientAdapter] for Web platforms.
class BrowserHttpClientAdapter implements HttpClientAdapter {
BrowserHttpClientAdapter({this.withCredentials = false});

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

/// Whether to send credentials such as cookies or authorization headers for
/// cross-site requests.
///
/// Defaults to `false`.
///
/// You can also override this value using `Options.extra['withCredentials']`
/// for each request.
bool withCredentials;

@override
Future<ResponseBody> fetch(
RequestOptions options,
Stream<Uint8List>? requestStream,
Future<void>? cancelFuture,
) async {
final xhr = HttpRequest();
xhrs.add(xhr);
xhr
..open(options.method, '${options.uri}')
..responseType = 'arraybuffer';

final withCredentialsOption = options.extra['withCredentials'];
if (withCredentialsOption != null) {
xhr.withCredentials = withCredentialsOption == true;
} else {
xhr.withCredentials = withCredentials;
}

options.headers.remove(Headers.contentLengthHeader);
options.headers.forEach((key, v) {
if (v is Iterable) {
xhr.setRequestHeader(key, v.join(', '));
} else {
xhr.setRequestHeader(key, v.toString());
}
});

final sendTimeout = options.sendTimeout ?? Duration.zero;
final connectTimeout = options.connectTimeout ?? Duration.zero;
final receiveTimeout = options.receiveTimeout ?? Duration.zero;
final xhrTimeout = (connectTimeout + receiveTimeout).inMilliseconds;
xhr.timeout = xhrTimeout;

final completer = Completer<ResponseBody>();

xhr.onLoad.first.then((_) {
final Uint8List body = (xhr.response as ByteBuffer).asUint8List();
completer.complete(
ResponseBody.fromBytes(
body,
xhr.status!,
headers: xhr.responseHeaders.map((k, v) => MapEntry(k, v.split(','))),
statusMessage: xhr.statusText,
isRedirect: xhr.status == 302 ||
xhr.status == 301 ||
options.uri.toString() != xhr.responseUrl,
),
);
});

Timer? connectTimeoutTimer;
if (connectTimeout > Duration.zero) {
connectTimeoutTimer = Timer(
connectTimeout,
() {
connectTimeoutTimer = null;
if (completer.isCompleted) {
// connectTimeout is triggered after the fetch has been completed.
return;
}
xhr.abort();
completer.completeError(
DioException.connectionTimeout(
requestOptions: options,
timeout: connectTimeout,
),
StackTrace.current,
);
},
);
}

// This code is structured to call `xhr.upload.onProgress.listen` only when
// absolutely necessary, because registering an xhr upload listener prevents
// the request from being classified as a "simple request" by the CORS spec.
// Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests
// Upload progress events only get triggered if the request body exists,
// so we can check it beforehand.
if (requestStream != null) {
if (connectTimeoutTimer != null) {
xhr.upload.onProgress.listen((event) {
connectTimeoutTimer?.cancel();
connectTimeoutTimer = null;
});
}

if (sendTimeout > Duration.zero) {
final uploadStopwatch = Stopwatch();
xhr.upload.onProgress.listen((event) {
if (!uploadStopwatch.isRunning) {
uploadStopwatch.start();
}
final duration = uploadStopwatch.elapsed;
if (duration > sendTimeout) {
uploadStopwatch.stop();
completer.completeError(
DioException.sendTimeout(
timeout: sendTimeout,
requestOptions: options,
),
StackTrace.current,
);
xhr.abort();
}
});
}

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

final receiveStopwatch = Stopwatch();
Timer? receiveTimer;

void stopWatchReceiveTimeout() {
receiveTimer?.cancel();
receiveTimer = null;
receiveStopwatch.stop();
}

void watchReceiveTimeout() {
if (receiveTimeout <= Duration.zero) {
return;
}
receiveStopwatch.reset();
if (!receiveStopwatch.isRunning) {
receiveStopwatch.start();
}
receiveTimer?.cancel();
receiveTimer = Timer(receiveTimeout, () {
if (!completer.isCompleted) {
xhr.abort();
completer.completeError(
DioException.receiveTimeout(
timeout: receiveTimeout,
requestOptions: options,
),
StackTrace.current,
);
}
stopWatchReceiveTimeout();
});
}

xhr.onProgress.listen(
(ProgressEvent 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!);
}
},
onDone: () => stopWatchReceiveTimeout(),
);

xhr.onError.first.then((_) {
connectTimeoutTimer?.cancel();
// Unfortunately, the underlying XMLHttpRequest API doesn't expose any
// specific information about the error itself.
// See also: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequestEventTarget/onerror
completer.completeError(
DioException.connectionError(
requestOptions: options,
reason: 'The XMLHttpRequest onError callback was called. '
'This typically indicates an error on the network layer.',
),
StackTrace.current,
);
});

xhr.onTimeout.first.then((_) {
final isConnectTimeout = connectTimeoutTimer != null;
if (connectTimeoutTimer != null) {
connectTimeoutTimer?.cancel();
}
if (!completer.isCompleted) {
if (isConnectTimeout) {
completer.completeError(
DioException.connectionTimeout(
timeout: connectTimeout,
requestOptions: options,
),
);
} else {
completer.completeError(
DioException.receiveTimeout(
timeout: Duration(milliseconds: xhrTimeout),
requestOptions: options,
),
StackTrace.current,
);
}
}
});

cancelFuture?.then((_) {
if (xhr.readyState < HttpRequest.DONE &&
xhr.readyState > HttpRequest.UNSENT) {
connectTimeoutTimer?.cancel();
try {
xhr.abort();
} catch (_) {}
if (!completer.isCompleted) {
completer.completeError(
DioException.requestCancelled(
requestOptions: options,
reason: 'The XMLHttpRequest was aborted.',
),
);
}
}
});

if (requestStream != null) {
if (options.method == 'GET') {
debugLog(
'GET request with a body data are not support on the '
'web platform. Use POST/PUT instead.',
StackTrace.current,
);
}
final completer = Completer<Uint8List>();
final sink = ByteConversionSink.withCallback(
(bytes) => completer.complete(
bytes is Uint8List ? bytes : Uint8List.fromList(bytes),
),
);
requestStream.listen(
sink.add,
onError: (Object e, StackTrace s) => completer.completeError(e, s),
onDone: sink.close,
cancelOnError: true,
);
final bytes = await completer.future;
xhr.send(bytes);
} else {
xhr.send();
}
return completer.future.whenComplete(() {
xhrs.remove(xhr);
});
}

/// Closes the client.
///
/// This terminates all active requests.
@override
void close({bool force = false}) {
if (force) {
for (final xhr in xhrs) {
xhr.abort();
}
}
xhrs.clear();
}
}
export 'package:dio_web_adapter/dio_web_adapter.dart'
show createAdapter, BrowserHttpClientAdapter;
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
35 changes: 1 addition & 34 deletions dio/lib/src/compute/compute_web.dart
Original file line number Diff line number Diff line change
@@ -1,34 +1 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This file corresponds to Flutter's
// [`foundation/_isolates_web.dart`](https://github.com/flutter/flutter/blob/stable/packages/flutter/lib/src/foundation/_isolates_web.dart).
//
// Changes are only synced with the `stable` branch.
//
// Last synced commit:
// [978a2e7](https://github.com/flutter/flutter/commit/978a2e7bf6a2ed287130af8dbd94cef019fb7bef)
//
// The changes are currently manually synced. If you noticed that the Flutter's
// original `compute` function (and any of the related files) have changed
// on the `stable` branch and you would like to see those changes in the `compute` package
// please open an [issue](https://github.com/dartsidedev/compute/issues),
// and I'll try my best to "merge".
//
// The file is intentionally not refactored so that it is easier to keep the
// compute package up to date with Flutter's implementation.
import 'compute.dart' as c;

/// The dart:html implementation of [c.compute].
Future<R> compute<Q, R>(
c.ComputeCallback<Q, R> callback,
Q message, {
String? debugLabel,
}) async {
// To avoid blocking the UI immediately for an expensive function call, we
// pump a single frame to allow the framework to complete the current set
// of work.
await null;
return callback(message);
}
export 'package:dio_web_adapter/dio_web_adapter.dart' show compute;
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
Loading

0 comments on commit 3700d4c

Please sign in to comment.