From b97b8dc22ea808c4fbd63f73abd7af8ecf694323 Mon Sep 17 00:00:00 2001 From: Anikate De <40452578+Anikate-De@users.noreply.github.com> Date: Fri, 16 Aug 2024 05:45:00 +0530 Subject: [PATCH] pkgs/ok_http: OkHttpClientConfiguration and configurable timeouts. (#1289) --- pkgs/ok_http/CHANGELOG.md | 5 + .../client_configuration_test.dart | 98 ++++ pkgs/ok_http/jnigen.yaml | 1 + pkgs/ok_http/lib/src/jni/bindings.dart | 501 +++++++++++++++++- pkgs/ok_http/lib/src/ok_http_client.dart | 75 ++- pkgs/ok_http/pubspec.yaml | 2 +- 6 files changed, 658 insertions(+), 24 deletions(-) create mode 100644 pkgs/ok_http/example/integration_test/client_configuration_test.dart diff --git a/pkgs/ok_http/CHANGELOG.md b/pkgs/ok_http/CHANGELOG.md index 557f19db20..a6a2fd3f4e 100644 --- a/pkgs/ok_http/CHANGELOG.md +++ b/pkgs/ok_http/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.1.1-wip + +- `OkHttpClient` now receives an `OkHttpClientConfiguration` to configure the client on a per-call basis. +- `OkHttpClient` supports setting four types of timeouts: [`connectTimeout`](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-ok-http-client/-builder/connect-timeout.html), [`readTimeout`](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-ok-http-client/-builder/read-timeout.html), [`writeTimeout`](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-ok-http-client/-builder/write-timeout.html), and [`callTimeout`](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-ok-http-client/-builder/call-timeout.html), using the `OkHttpClientConfiguration`. + ## 0.1.0 - Implementation of [`BaseClient`](https://pub.dev/documentation/http/latest/http/BaseClient-class.html) and `send()` method using [`enqueue()` API](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-call/enqueue.html) diff --git a/pkgs/ok_http/example/integration_test/client_configuration_test.dart b/pkgs/ok_http/example/integration_test/client_configuration_test.dart new file mode 100644 index 0000000000..2cee14afb6 --- /dev/null +++ b/pkgs/ok_http/example/integration_test/client_configuration_test.dart @@ -0,0 +1,98 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:io'; + +import 'package:http/http.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:ok_http/ok_http.dart'; +import 'package:test/test.dart'; + +void testTimeouts() { + group('timeouts', () { + group('call timeout', () { + late HttpServer server; + + setUp(() async { + server = (await HttpServer.bind('localhost', 0)) + ..listen((request) async { + // Add a delay of `n` seconds for URI `http://localhost:port/n` + final delay = int.parse(request.requestedUri.pathSegments.last); + await Future.delayed(Duration(seconds: delay)); + + await request.drain(); + await request.response.close(); + }); + }); + tearDown(() { + server.close(); + }); + + test('exceeded', () { + final client = OkHttpClient( + configuration: const OkHttpClientConfiguration( + callTimeout: Duration(milliseconds: 500), + ), + ); + expect( + () async { + await client.get(Uri.parse('http://localhost:${server.port}/1')); + }, + throwsA( + isA().having( + (exception) => exception.message, + 'message', + startsWith('java.io.InterruptedIOException'), + ), + ), + ); + }); + + test('not exceeded', () async { + final client = OkHttpClient( + configuration: const OkHttpClientConfiguration( + callTimeout: Duration(milliseconds: 1500), + ), + ); + final response = await client.send( + Request( + 'GET', + Uri.http('localhost:${server.port}', '1'), + ), + ); + + expect(response.statusCode, 200); + expect(response.contentLength, 0); + }); + + test('not set', () async { + final client = OkHttpClient(); + + expect( + () async { + await client.send( + Request( + 'GET', + Uri.http('localhost:${server.port}', '11'), + ), + ); + }, + throwsA( + isA().having( + (exception) => exception.message, + 'message', + startsWith('java.net.SocketTimeoutException'), + ), + ), + ); + }); + }); + }); +} + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testTimeouts(); +} diff --git a/pkgs/ok_http/jnigen.yaml b/pkgs/ok_http/jnigen.yaml index 7f63d6c2d9..2e7f87a45a 100644 --- a/pkgs/ok_http/jnigen.yaml +++ b/pkgs/ok_http/jnigen.yaml @@ -36,6 +36,7 @@ classes: - "com.example.ok_http.WebSocketListenerProxy" - "okio.ByteString" - "com.example.ok_http.WebSocketInterceptor" + - "java.util.concurrent.TimeUnit" # Exclude the deprecated methods listed below # They cause syntax errors during the `dart format` step of JNIGen. diff --git a/pkgs/ok_http/lib/src/jni/bindings.dart b/pkgs/ok_http/lib/src/jni/bindings.dart index 66f0078d9a..608384389a 100644 --- a/pkgs/ok_http/lib/src/jni/bindings.dart +++ b/pkgs/ok_http/lib/src/jni/bindings.dart @@ -5008,7 +5008,7 @@ class OkHttpClient_Builder extends jni.JObject { /// The returned object must be released after use, by calling the [release] method. OkHttpClient_Builder callTimeout( int j, - jni.JObject timeUnit, + TimeUnit timeUnit, ) { return _callTimeout(reference.pointer, _id_callTimeout as jni.JMethodIDPtr, j, timeUnit.reference.pointer) @@ -5061,7 +5061,7 @@ class OkHttpClient_Builder extends jni.JObject { /// The returned object must be released after use, by calling the [release] method. OkHttpClient_Builder connectTimeout( int j, - jni.JObject timeUnit, + TimeUnit timeUnit, ) { return _connectTimeout( reference.pointer, @@ -5117,7 +5117,7 @@ class OkHttpClient_Builder extends jni.JObject { /// The returned object must be released after use, by calling the [release] method. OkHttpClient_Builder readTimeout( int j, - jni.JObject timeUnit, + TimeUnit timeUnit, ) { return _readTimeout(reference.pointer, _id_readTimeout as jni.JMethodIDPtr, j, timeUnit.reference.pointer) @@ -5170,7 +5170,7 @@ class OkHttpClient_Builder extends jni.JObject { /// The returned object must be released after use, by calling the [release] method. OkHttpClient_Builder writeTimeout( int j, - jni.JObject timeUnit, + TimeUnit timeUnit, ) { return _writeTimeout(reference.pointer, _id_writeTimeout as jni.JMethodIDPtr, j, timeUnit.reference.pointer) @@ -5223,7 +5223,7 @@ class OkHttpClient_Builder extends jni.JObject { /// The returned object must be released after use, by calling the [release] method. OkHttpClient_Builder pingInterval( int j, - jni.JObject timeUnit, + TimeUnit timeUnit, ) { return _pingInterval(reference.pointer, _id_pingInterval as jni.JMethodIDPtr, j, timeUnit.reference.pointer) @@ -8204,7 +8204,7 @@ class ConnectionPool extends jni.JObject { factory ConnectionPool.new1( int i, int j, - jni.JObject timeUnit, + TimeUnit timeUnit, ) { return ConnectionPool.fromReference(_new1(_class.reference.pointer, _id_new1 as jni.JMethodIDPtr, i, j, timeUnit.reference.pointer) @@ -8836,7 +8836,7 @@ class ExecutorService extends jni.JObject { /// from: public abstract boolean awaitTermination(long j, java.util.concurrent.TimeUnit timeUnit) bool awaitTermination( int j, - jni.JObject timeUnit, + TimeUnit timeUnit, ) { return _awaitTermination( reference.pointer, @@ -8902,7 +8902,7 @@ class ExecutorService extends jni.JObject { $a[0] .castTo(const jni.JLongType(), releaseOriginal: true) .longValue(releaseOriginal: true), - $a[1].castTo(const jni.JObjectType(), releaseOriginal: true), + $a[1].castTo(const $TimeUnitType(), releaseOriginal: true), ); return jni.JBoolean($r).reference.toPointer(); } @@ -8945,14 +8945,14 @@ abstract interface class $ExecutorServiceImpl { required jni.JList Function() shutdownNow, required bool Function() isShutdown, required bool Function() isTerminated, - required bool Function(int j, jni.JObject timeUnit) awaitTermination, + required bool Function(int j, TimeUnit timeUnit) awaitTermination, }) = _$ExecutorServiceImpl; void shutdown(); jni.JList shutdownNow(); bool isShutdown(); bool isTerminated(); - bool awaitTermination(int j, jni.JObject timeUnit); + bool awaitTermination(int j, TimeUnit timeUnit); } class _$ExecutorServiceImpl implements $ExecutorServiceImpl { @@ -8961,7 +8961,7 @@ class _$ExecutorServiceImpl implements $ExecutorServiceImpl { required jni.JList Function() shutdownNow, required bool Function() isShutdown, required bool Function() isTerminated, - required bool Function(int j, jni.JObject timeUnit) awaitTermination, + required bool Function(int j, TimeUnit timeUnit) awaitTermination, }) : _shutdown = shutdown, _shutdownNow = shutdownNow, _isShutdown = isShutdown, @@ -8972,7 +8972,7 @@ class _$ExecutorServiceImpl implements $ExecutorServiceImpl { final jni.JList Function() _shutdownNow; final bool Function() _isShutdown; final bool Function() _isTerminated; - final bool Function(int j, jni.JObject timeUnit) _awaitTermination; + final bool Function(int j, TimeUnit timeUnit) _awaitTermination; void shutdown() { return _shutdown(); @@ -8990,7 +8990,7 @@ class _$ExecutorServiceImpl implements $ExecutorServiceImpl { return _isTerminated(); } - bool awaitTermination(int j, jni.JObject timeUnit) { + bool awaitTermination(int j, TimeUnit timeUnit) { return _awaitTermination(j, timeUnit); } } @@ -13393,3 +13393,478 @@ final class $WebSocketInterceptorType other is $WebSocketInterceptorType; } } + +/// from: java.util.concurrent.TimeUnit +class TimeUnit extends jni.JObject { + @override + late final jni.JObjType $type = type; + + TimeUnit.fromReference( + jni.JReference reference, + ) : super.fromReference(reference); + + static final _class = jni.JClass.forName(r'java/util/concurrent/TimeUnit'); + + /// The type which includes information such as the signature of this class. + static const type = $TimeUnitType(); + static final _id_NANOSECONDS = _class.staticFieldId( + r'NANOSECONDS', + r'Ljava/util/concurrent/TimeUnit;', + ); + + /// from: static public final java.util.concurrent.TimeUnit NANOSECONDS + /// The returned object must be released after use, by calling the [release] method. + static TimeUnit get NANOSECONDS => + _id_NANOSECONDS.get(_class, const $TimeUnitType()); + + static final _id_MICROSECONDS = _class.staticFieldId( + r'MICROSECONDS', + r'Ljava/util/concurrent/TimeUnit;', + ); + + /// from: static public final java.util.concurrent.TimeUnit MICROSECONDS + /// The returned object must be released after use, by calling the [release] method. + static TimeUnit get MICROSECONDS => + _id_MICROSECONDS.get(_class, const $TimeUnitType()); + + static final _id_MILLISECONDS = _class.staticFieldId( + r'MILLISECONDS', + r'Ljava/util/concurrent/TimeUnit;', + ); + + /// from: static public final java.util.concurrent.TimeUnit MILLISECONDS + /// The returned object must be released after use, by calling the [release] method. + static TimeUnit get MILLISECONDS => + _id_MILLISECONDS.get(_class, const $TimeUnitType()); + + static final _id_SECONDS = _class.staticFieldId( + r'SECONDS', + r'Ljava/util/concurrent/TimeUnit;', + ); + + /// from: static public final java.util.concurrent.TimeUnit SECONDS + /// The returned object must be released after use, by calling the [release] method. + static TimeUnit get SECONDS => _id_SECONDS.get(_class, const $TimeUnitType()); + + static final _id_MINUTES = _class.staticFieldId( + r'MINUTES', + r'Ljava/util/concurrent/TimeUnit;', + ); + + /// from: static public final java.util.concurrent.TimeUnit MINUTES + /// The returned object must be released after use, by calling the [release] method. + static TimeUnit get MINUTES => _id_MINUTES.get(_class, const $TimeUnitType()); + + static final _id_HOURS = _class.staticFieldId( + r'HOURS', + r'Ljava/util/concurrent/TimeUnit;', + ); + + /// from: static public final java.util.concurrent.TimeUnit HOURS + /// The returned object must be released after use, by calling the [release] method. + static TimeUnit get HOURS => _id_HOURS.get(_class, const $TimeUnitType()); + + static final _id_DAYS = _class.staticFieldId( + r'DAYS', + r'Ljava/util/concurrent/TimeUnit;', + ); + + /// from: static public final java.util.concurrent.TimeUnit DAYS + /// The returned object must be released after use, by calling the [release] method. + static TimeUnit get DAYS => _id_DAYS.get(_class, const $TimeUnitType()); + + static final _id_values = _class.staticMethodId( + r'values', + r'()[Ljava/util/concurrent/TimeUnit;', + ); + + static final _values = ProtectedJniExtensions.lookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, + jni.JMethodIDPtr, + )>>('globalEnv_CallStaticObjectMethod') + .asFunction< + jni.JniResult Function( + ffi.Pointer, + jni.JMethodIDPtr, + )>(); + + /// from: static public java.util.concurrent.TimeUnit[] values() + /// The returned object must be released after use, by calling the [release] method. + static jni.JArray values() { + return _values(_class.reference.pointer, _id_values as jni.JMethodIDPtr) + .object(const jni.JArrayType($TimeUnitType())); + } + + static final _id_valueOf = _class.staticMethodId( + r'valueOf', + r'(Ljava/lang/String;)Ljava/util/concurrent/TimeUnit;', + ); + + static final _valueOf = ProtectedJniExtensions.lookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, + jni.JMethodIDPtr, + ffi.VarArgs<(ffi.Pointer,)>)>>( + 'globalEnv_CallStaticObjectMethod') + .asFunction< + jni.JniResult Function(ffi.Pointer, jni.JMethodIDPtr, + ffi.Pointer)>(); + + /// from: static public java.util.concurrent.TimeUnit valueOf(java.lang.String string) + /// The returned object must be released after use, by calling the [release] method. + static TimeUnit valueOf( + jni.JString string, + ) { + return _valueOf(_class.reference.pointer, _id_valueOf as jni.JMethodIDPtr, + string.reference.pointer) + .object(const $TimeUnitType()); + } + + static final _id_convert = _class.instanceMethodId( + r'convert', + r'(JLjava/util/concurrent/TimeUnit;)J', + ); + + static final _convert = ProtectedJniExtensions.lookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, + jni.JMethodIDPtr, + ffi.VarArgs<(ffi.Int64, ffi.Pointer)>)>>( + 'globalEnv_CallLongMethod') + .asFunction< + jni.JniResult Function(ffi.Pointer, jni.JMethodIDPtr, int, + ffi.Pointer)>(); + + /// from: public long convert(long j, java.util.concurrent.TimeUnit timeUnit) + int convert( + int j, + TimeUnit timeUnit, + ) { + return _convert(reference.pointer, _id_convert as jni.JMethodIDPtr, j, + timeUnit.reference.pointer) + .long; + } + + static final _id_convert1 = _class.instanceMethodId( + r'convert', + r'(Ljava/time/Duration;)J', + ); + + static final _convert1 = ProtectedJniExtensions.lookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, + jni.JMethodIDPtr, + ffi.VarArgs<(ffi.Pointer,)>)>>( + 'globalEnv_CallLongMethod') + .asFunction< + jni.JniResult Function(ffi.Pointer, jni.JMethodIDPtr, + ffi.Pointer)>(); + + /// from: public long convert(java.time.Duration duration) + int convert1( + jni.JObject duration, + ) { + return _convert1(reference.pointer, _id_convert1 as jni.JMethodIDPtr, + duration.reference.pointer) + .long; + } + + static final _id_toNanos = _class.instanceMethodId( + r'toNanos', + r'(J)J', + ); + + static final _toNanos = ProtectedJniExtensions.lookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, jni.JMethodIDPtr, + ffi.VarArgs<(ffi.Int64,)>)>>('globalEnv_CallLongMethod') + .asFunction< + jni.JniResult Function( + ffi.Pointer, jni.JMethodIDPtr, int)>(); + + /// from: public long toNanos(long j) + int toNanos( + int j, + ) { + return _toNanos(reference.pointer, _id_toNanos as jni.JMethodIDPtr, j).long; + } + + static final _id_toMicros = _class.instanceMethodId( + r'toMicros', + r'(J)J', + ); + + static final _toMicros = ProtectedJniExtensions.lookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, jni.JMethodIDPtr, + ffi.VarArgs<(ffi.Int64,)>)>>('globalEnv_CallLongMethod') + .asFunction< + jni.JniResult Function( + ffi.Pointer, jni.JMethodIDPtr, int)>(); + + /// from: public long toMicros(long j) + int toMicros( + int j, + ) { + return _toMicros(reference.pointer, _id_toMicros as jni.JMethodIDPtr, j) + .long; + } + + static final _id_toMillis = _class.instanceMethodId( + r'toMillis', + r'(J)J', + ); + + static final _toMillis = ProtectedJniExtensions.lookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, jni.JMethodIDPtr, + ffi.VarArgs<(ffi.Int64,)>)>>('globalEnv_CallLongMethod') + .asFunction< + jni.JniResult Function( + ffi.Pointer, jni.JMethodIDPtr, int)>(); + + /// from: public long toMillis(long j) + int toMillis( + int j, + ) { + return _toMillis(reference.pointer, _id_toMillis as jni.JMethodIDPtr, j) + .long; + } + + static final _id_toSeconds = _class.instanceMethodId( + r'toSeconds', + r'(J)J', + ); + + static final _toSeconds = ProtectedJniExtensions.lookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, jni.JMethodIDPtr, + ffi.VarArgs<(ffi.Int64,)>)>>('globalEnv_CallLongMethod') + .asFunction< + jni.JniResult Function( + ffi.Pointer, jni.JMethodIDPtr, int)>(); + + /// from: public long toSeconds(long j) + int toSeconds( + int j, + ) { + return _toSeconds(reference.pointer, _id_toSeconds as jni.JMethodIDPtr, j) + .long; + } + + static final _id_toMinutes = _class.instanceMethodId( + r'toMinutes', + r'(J)J', + ); + + static final _toMinutes = ProtectedJniExtensions.lookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, jni.JMethodIDPtr, + ffi.VarArgs<(ffi.Int64,)>)>>('globalEnv_CallLongMethod') + .asFunction< + jni.JniResult Function( + ffi.Pointer, jni.JMethodIDPtr, int)>(); + + /// from: public long toMinutes(long j) + int toMinutes( + int j, + ) { + return _toMinutes(reference.pointer, _id_toMinutes as jni.JMethodIDPtr, j) + .long; + } + + static final _id_toHours = _class.instanceMethodId( + r'toHours', + r'(J)J', + ); + + static final _toHours = ProtectedJniExtensions.lookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, jni.JMethodIDPtr, + ffi.VarArgs<(ffi.Int64,)>)>>('globalEnv_CallLongMethod') + .asFunction< + jni.JniResult Function( + ffi.Pointer, jni.JMethodIDPtr, int)>(); + + /// from: public long toHours(long j) + int toHours( + int j, + ) { + return _toHours(reference.pointer, _id_toHours as jni.JMethodIDPtr, j).long; + } + + static final _id_toDays = _class.instanceMethodId( + r'toDays', + r'(J)J', + ); + + static final _toDays = ProtectedJniExtensions.lookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, jni.JMethodIDPtr, + ffi.VarArgs<(ffi.Int64,)>)>>('globalEnv_CallLongMethod') + .asFunction< + jni.JniResult Function( + ffi.Pointer, jni.JMethodIDPtr, int)>(); + + /// from: public long toDays(long j) + int toDays( + int j, + ) { + return _toDays(reference.pointer, _id_toDays as jni.JMethodIDPtr, j).long; + } + + static final _id_timedWait = _class.instanceMethodId( + r'timedWait', + r'(Ljava/lang/Object;J)V', + ); + + static final _timedWait = ProtectedJniExtensions.lookup< + ffi.NativeFunction< + jni.JThrowablePtr Function( + ffi.Pointer, + jni.JMethodIDPtr, + ffi.VarArgs<(ffi.Pointer, ffi.Int64)>)>>( + 'globalEnv_CallVoidMethod') + .asFunction< + jni.JThrowablePtr Function(ffi.Pointer, jni.JMethodIDPtr, + ffi.Pointer, int)>(); + + /// from: public void timedWait(java.lang.Object object, long j) + void timedWait( + jni.JObject object, + int j, + ) { + _timedWait(reference.pointer, _id_timedWait as jni.JMethodIDPtr, + object.reference.pointer, j) + .check(); + } + + static final _id_timedJoin = _class.instanceMethodId( + r'timedJoin', + r'(Ljava/lang/Thread;J)V', + ); + + static final _timedJoin = ProtectedJniExtensions.lookup< + ffi.NativeFunction< + jni.JThrowablePtr Function( + ffi.Pointer, + jni.JMethodIDPtr, + ffi.VarArgs<(ffi.Pointer, ffi.Int64)>)>>( + 'globalEnv_CallVoidMethod') + .asFunction< + jni.JThrowablePtr Function(ffi.Pointer, jni.JMethodIDPtr, + ffi.Pointer, int)>(); + + /// from: public void timedJoin(java.lang.Thread thread, long j) + void timedJoin( + jni.JObject thread, + int j, + ) { + _timedJoin(reference.pointer, _id_timedJoin as jni.JMethodIDPtr, + thread.reference.pointer, j) + .check(); + } + + static final _id_sleep = _class.instanceMethodId( + r'sleep', + r'(J)V', + ); + + static final _sleep = ProtectedJniExtensions.lookup< + ffi.NativeFunction< + jni.JThrowablePtr Function( + ffi.Pointer, + jni.JMethodIDPtr, + ffi.VarArgs<(ffi.Int64,)>)>>('globalEnv_CallVoidMethod') + .asFunction< + jni.JThrowablePtr Function( + ffi.Pointer, jni.JMethodIDPtr, int)>(); + + /// from: public void sleep(long j) + void sleep( + int j, + ) { + _sleep(reference.pointer, _id_sleep as jni.JMethodIDPtr, j).check(); + } + + static final _id_toChronoUnit = _class.instanceMethodId( + r'toChronoUnit', + r'()Ljava/time/temporal/ChronoUnit;', + ); + + static final _toChronoUnit = ProtectedJniExtensions.lookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, + jni.JMethodIDPtr, + )>>('globalEnv_CallObjectMethod') + .asFunction< + jni.JniResult Function( + ffi.Pointer, + jni.JMethodIDPtr, + )>(); + + /// from: public java.time.temporal.ChronoUnit toChronoUnit() + /// The returned object must be released after use, by calling the [release] method. + jni.JObject toChronoUnit() { + return _toChronoUnit( + reference.pointer, _id_toChronoUnit as jni.JMethodIDPtr) + .object(const jni.JObjectType()); + } + + static final _id_of = _class.staticMethodId( + r'of', + r'(Ljava/time/temporal/ChronoUnit;)Ljava/util/concurrent/TimeUnit;', + ); + + static final _of = ProtectedJniExtensions.lookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, + jni.JMethodIDPtr, + ffi.VarArgs<(ffi.Pointer,)>)>>( + 'globalEnv_CallStaticObjectMethod') + .asFunction< + jni.JniResult Function(ffi.Pointer, jni.JMethodIDPtr, + ffi.Pointer)>(); + + /// from: static public java.util.concurrent.TimeUnit of(java.time.temporal.ChronoUnit chronoUnit) + /// The returned object must be released after use, by calling the [release] method. + static TimeUnit of( + jni.JObject chronoUnit, + ) { + return _of(_class.reference.pointer, _id_of as jni.JMethodIDPtr, + chronoUnit.reference.pointer) + .object(const $TimeUnitType()); + } +} + +final class $TimeUnitType extends jni.JObjType { + const $TimeUnitType(); + + @override + String get signature => r'Ljava/util/concurrent/TimeUnit;'; + + @override + TimeUnit fromReference(jni.JReference reference) => + TimeUnit.fromReference(reference); + + @override + jni.JObjType get superType => const jni.JObjectType(); + + @override + final superCount = 1; + + @override + int get hashCode => ($TimeUnitType).hashCode; + + @override + bool operator ==(Object other) { + return other.runtimeType == ($TimeUnitType) && other is $TimeUnitType; + } +} diff --git a/pkgs/ok_http/lib/src/ok_http_client.dart b/pkgs/ok_http/lib/src/ok_http_client.dart index 752dbaa41a..27af5bf3d7 100644 --- a/pkgs/ok_http/lib/src/ok_http_client.dart +++ b/pkgs/ok_http/lib/src/ok_http_client.dart @@ -21,6 +21,43 @@ import 'package:jni/jni.dart'; import 'jni/bindings.dart' as bindings; +/// Configurations for the [OkHttpClient]. +class OkHttpClientConfiguration { + /// The maximum duration to wait for a call to complete. + /// + /// If a call does not finish within the specified time, it will throw a + /// [ClientException] with the message "java.io.InterruptedIOException...". + /// + /// [Duration.zero] indicates no timeout. + /// + /// See [OkHttpClient.Builder.callTimeout](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-ok-http-client/-builder/call-timeout.html). + final Duration callTimeout; + + /// The maximum duration to wait while connecting a TCP Socket to the target + /// host. + /// + /// See [OkHttpClient.Builder.connectTimeout](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-ok-http-client/-builder/connect-timeout.html). + final Duration connectTimeout; + + /// The maximum duration to wait for a TCP Socket and for individual read + /// IO operations. + /// + /// See [OkHttpClient.Builder.readTimeout](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-ok-http-client/-builder/read-timeout.html). + final Duration readTimeout; + + /// The maximum duration to wait for individual write IO operations. + /// + /// See [OkHttpClient.Builder.writeTimeout](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-ok-http-client/-builder/write-timeout.html). + final Duration writeTimeout; + + const OkHttpClientConfiguration({ + this.callTimeout = Duration.zero, + this.connectTimeout = const Duration(milliseconds: 10000), + this.readTimeout = const Duration(milliseconds: 10000), + this.writeTimeout = const Duration(milliseconds: 10000), + }); +} + /// An HTTP [Client] utilizing the [OkHttp](https://square.github.io/okhttp/) client. /// /// Example Usage: @@ -47,7 +84,14 @@ class OkHttpClient extends BaseClient { late bindings.OkHttpClient _client; bool _isClosed = false; - OkHttpClient() { + /// The configuration for this client, applied on a per-call basis. + /// It can be updated multiple times during the client's lifecycle. + OkHttpClientConfiguration configuration; + + /// Creates a new instance of [OkHttpClient] with the given [configuration]. + OkHttpClient({ + this.configuration = const OkHttpClientConfiguration(), + }) { _client = bindings.OkHttpClient.new1(); } @@ -160,15 +204,26 @@ class OkHttpClient extends BaseClient { maxRedirects, followRedirects, bindings.RedirectReceivedCallback.implement( bindings.$RedirectReceivedCallbackImpl( - onRedirectReceived: (response, newLocation) { - profile?.responseData.addRedirect(HttpProfileRedirectData( - statusCode: response.code(), - method: - response.request().method().toDartString(releaseOriginal: true), - location: newLocation.toDartString(releaseOriginal: true), - )); - }, - ))).build(); + onRedirectReceived: (response, newLocation) { + profile?.responseData.addRedirect(HttpProfileRedirectData( + statusCode: response.code(), + method: response + .request() + .method() + .toDartString(releaseOriginal: true), + location: newLocation.toDartString(releaseOriginal: true), + )); + }, + ))) + .callTimeout(configuration.callTimeout.inMilliseconds, + bindings.TimeUnit.MILLISECONDS) + .connectTimeout(configuration.connectTimeout.inMilliseconds, + bindings.TimeUnit.MILLISECONDS) + .readTimeout(configuration.readTimeout.inMilliseconds, + bindings.TimeUnit.MILLISECONDS) + .writeTimeout(configuration.writeTimeout.inMilliseconds, + bindings.TimeUnit.MILLISECONDS) + .build(); // `enqueue()` schedules the request to be executed in the future. // https://square.github.io/okhttp/5.x/okhttp/okhttp3/-call/enqueue.html diff --git a/pkgs/ok_http/pubspec.yaml b/pkgs/ok_http/pubspec.yaml index 121c6b314c..93759491e5 100644 --- a/pkgs/ok_http/pubspec.yaml +++ b/pkgs/ok_http/pubspec.yaml @@ -1,5 +1,5 @@ name: ok_http -version: 0.1.0 +version: 0.1.1-wip description: >- An Android Flutter plugin that provides access to the OkHttp HTTP client. repository: https://github.com/dart-lang/http/tree/master/pkgs/ok_http