Skip to content
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
1 change: 1 addition & 0 deletions dio/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ See the [Migration Guide][] for the complete breaking changes list.**
- Fix the type conversion regression when using `MultipartFile.fromBytes`.
- Split the Web implementation to `package:dio_web_adapter`.
- Add FusedTransformer for improved performance when decoding JSON.
- Set FusedTransformer as the default transformer.
Comment thread
AlexV525 marked this conversation as resolved.
- Improves `InterceptorState.toString()`.

## 5.4.3+1
Expand Down
9 changes: 8 additions & 1 deletion dio/lib/src/dio_mixin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,15 @@ abstract class DioMixin implements Dio {

/// The default [Transformer] that transfers requests and responses
/// into corresponding content to send.
/// For response bodies greater than 50KB, a new Isolate will be spawned to
/// decode the response body to JSON.
/// Taken from https://github.com/flutter/flutter/blob/135454af32477f815a7525073027a3ff9eff1bfd/packages/flutter/lib/src/services/asset_bundle.dart#L87-L93
/// 50 KB of data should take 2-3 ms to parse on a Moto G4, and about 400 μs
/// on a Pixel 4.
@override
Transformer transformer = BackgroundTransformer();
Transformer transformer = FusedTransformer(
contentLengthIsolateThreshold: 50 * 1024,
);

bool _closed = false;

Expand Down
31 changes: 22 additions & 9 deletions dio/lib/src/transformers/fused_transformer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ import 'util/consolidate_bytes.dart';
/// but a custom threshold can be set to switch to an isolate for large responses by passing
/// [contentLengthIsolateThreshold].
class FusedTransformer extends Transformer {
FusedTransformer({this.contentLengthIsolateThreshold = -1});
FusedTransformer({
this.contentLengthIsolateThreshold = -1,
});

/// Always decode the response in the same isolate
factory FusedTransformer.sync() =>
FusedTransformer(contentLengthIsolateThreshold: -1);
factory FusedTransformer.sync() => FusedTransformer(
contentLengthIsolateThreshold: -1,
);

// whether to switch decoding to an isolate for large responses
// set to -1 to disable, 0 to always use isolate
Expand Down Expand Up @@ -54,15 +57,16 @@ class FusedTransformer extends Transformer {
}

final isJsonContent = Transformer.isJsonMimeType(
responseBody.headers[Headers.contentTypeHeader]?.first,
);
responseBody.headers[Headers.contentTypeHeader]?.first,
) &&
responseType == ResponseType.json;

final customResponseDecoder = options.responseDecoder;

// No custom decoder was specified for the response,
// and the response is json -> use the fast path decoder
if (isJsonContent && customResponseDecoder == null) {
return _fastUtf8JsonDecode(responseBody);
return _fastUtf8JsonDecode(options, responseBody);
}
final responseBytes = await consolidateBytes(responseBody.stream);

Expand All @@ -88,7 +92,7 @@ class FusedTransformer extends Transformer {
if (isJsonContent && decodedResponse != null) {
// slow path decoder, since there was a custom decoder specified
return jsonDecode(decodedResponse);
} else if (decodedResponse != null) {
} else if (customResponseDecoder != null) {
Comment thread
AlexV525 marked this conversation as resolved.
return decodedResponse;
} else {
// If the response is not JSON and no custom decoder was specified,
Expand All @@ -100,7 +104,10 @@ class FusedTransformer extends Transformer {
}
}

Future<Object?> _fastUtf8JsonDecode(ResponseBody responseBody) async {
Future<Object?> _fastUtf8JsonDecode(
RequestOptions options,
ResponseBody responseBody,
) async {
final contentLengthHeader =
responseBody.headers[Headers.contentLengthHeader];

Expand All @@ -111,6 +118,11 @@ class FusedTransformer extends Transformer {
// of the response or the length of the eagerly decoded response bytes
final int contentLength;

// Successful HEAD requests don't have a response body, even if the content-length header
// present.
final mightNotHaveResponseBodyDespiteContentLength =
options.method == 'HEAD';

// The eagerly decoded response bytes
// which is set if the content length is not specified and
// null otherwise (we'll feed the stream directly to the decoder in that case)
Expand All @@ -119,7 +131,8 @@ class FusedTransformer extends Transformer {
// If the content length is not specified, we need to consolidate the stream
// and count the bytes to determine if we should use an isolate
// otherwise we use the content length header
if (!hasContentLengthHeader) {
if (!hasContentLengthHeader ||
mightNotHaveResponseBodyDespiteContentLength) {
responseBytes = await consolidateBytes(responseBody.stream);
contentLength = responseBytes.length;
} else {
Expand Down
Loading