Skip to content

Commit

Permalink
Improve performance of byte data on IO platform (cfug#1838)
Browse files Browse the repository at this point in the history
  • Loading branch information
kuhnroyal authored Jun 5, 2023
1 parent c1a44bb commit 193f783
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 18 deletions.
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.**
- `IOHttpClientAdapter.onHttpClientCreate` has been deprecated and is scheduled for removal in
Dio 6.0.0 - Please use the replacement `IOHttpClientAdapter.createHttpClient` instead.
- Using `CancelToken` no longer closes and re-creates `HttpClient` for each request when `IOHttpClientAdapter` is used.
- Improve performance when sending binary data (`List<int>`/`Uint8List`).

## 5.1.2

Expand Down
22 changes: 16 additions & 6 deletions dio/lib/src/dio_mixin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -636,14 +636,24 @@ abstract class DioMixin implements Dio {
options.headers[Headers.contentLengthHeader] = length.toString();
} else {
final List<int> bytes;
// Call request transformer.
final data = await transformer.transformRequest(options);
if (options.requestEncoder != null) {
bytes = options.requestEncoder!(data, options);

if (data is Uint8List) {
// Handle binary data which does not need to be transformed
bytes = data;
} else if (data is List<int>) {
// Handle binary data which does not need to be transformed
bytes = Uint8List.fromList(data);
} else {
//Default convert to utf8
bytes = utf8.encode(data);
// Call request transformer for anything else
final transformed = await transformer.transformRequest(options);
if (options.requestEncoder != null) {
bytes = options.requestEncoder!(transformed, options);
} else {
// Default convert to utf8
bytes = utf8.encode(transformed);
}
}

// support data sending progress
length = bytes.length;
options.headers[Headers.contentLengthHeader] = length.toString();
Expand Down
2 changes: 2 additions & 0 deletions dio/test/mock/http_mock.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:io';

import 'package:dio/dio.dart';
import 'package:mockito/annotations.dart';

import 'http_mock.mocks.dart';
Expand All @@ -13,6 +14,7 @@ final httpClientMock = MockHttpClient();
MockSpec<HttpClientRequest>(),
MockSpec<HttpClientResponse>(),
MockSpec<HttpHeaders>(),
MockSpec<Transformer>(),
],
)
class MockHttpOverrides extends HttpOverrides {
Expand Down
23 changes: 23 additions & 0 deletions dio/test/mock/http_mock.mocks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import 'dart:async' as _i4;
import 'dart:convert' as _i3;
import 'dart:io' as _i2;

import 'package:dio/src/adapter.dart' as _i7;
import 'package:dio/src/options.dart' as _i6;
import 'package:dio/src/transformer.dart' as _i5;
import 'package:mockito/mockito.dart' as _i1;

// ignore_for_file: type=lint
Expand Down Expand Up @@ -654,3 +657,23 @@ class MockHttpHeaders extends _i1.Mock implements _i2.HttpHeaders {
void clear() => super.noSuchMethod(Invocation.method(#clear, []),
returnValueForMissingStub: null);
}

/// A class which mocks [Transformer].
///
/// See the documentation for Mockito's code generation for more information.
class MockTransformer extends _i1.Mock implements _i5.Transformer {
MockTransformer() {
_i1.throwOnMissingStub(this);
}

@override
_i4.Future<String> transformRequest(_i6.RequestOptions? options) =>
(super.noSuchMethod(Invocation.method(#transformRequest, [options]),
returnValue: Future<String>.value('')) as _i4.Future<String>);
@override
_i4.Future<dynamic> transformResponse(
_i6.RequestOptions? options, _i7.ResponseBody? response) =>
(super.noSuchMethod(
Invocation.method(#transformResponse, [options, response]),
returnValue: Future<dynamic>.value()) as _i4.Future<dynamic>);
}
34 changes: 22 additions & 12 deletions dio/test/upload_stream_test.dart → dio/test/upload_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,37 @@ import 'dart:convert';
import 'dart:io';

import 'package:dio/dio.dart';
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';

import 'mock/http_mock.mocks.dart';

void main() {
final dio = Dio()
..options.baseUrl = 'https://httpbun.com/'
..interceptors.add(
QueuedInterceptorsWrapper(
onRequest: (options, handler) async {
// Delay 1 second before requests to avoid request too frequently.
await Future.delayed(const Duration(seconds: 1));
handler.next(options);
},
),
late Dio dio;

setUp(() {
dio = Dio()..options.baseUrl = 'https://httpbun.com/';
});

test('binary data should not be transformed', () async {
final bytes = List.generate(1024, (index) => index);
final transformer = MockTransformer();
when(transformer.transformResponse(any, any)).thenAnswer(
(i) => i.positionalArguments[1],
);
final r = await dio.put(
'/put',
data: bytes,
);
verifyNever(transformer.transformRequest(any));
expect(r.statusCode, 200);
});

test('stream', () async {
Response r;
const str = 'hello 😌';
final bytes = utf8.encode(str).toList();
final stream = Stream.fromIterable(bytes.map((e) => [e]));
r = await dio.put(
final r = await dio.put(
'/put',
data: stream,
options: Options(
Expand Down

0 comments on commit 193f783

Please sign in to comment.