diff --git a/pkgs/cronet_http/CHANGELOG.md b/pkgs/cronet_http/CHANGELOG.md index 24171067fd..a17aa74357 100644 --- a/pkgs/cronet_http/CHANGELOG.md +++ b/pkgs/cronet_http/CHANGELOG.md @@ -1,3 +1,16 @@ +## 1.10.0-wip + +* Add support for streaming request bodies on `StreamedRequest` via Cronet's + `UploadDataProvider` API. In-memory `Request` bodies continue to use the + existing byte-buffer upload path. +* Add `UploadDataProviderProxy` Kotlin bridge so Dart can implement Cronet's + `UploadDataProvider` through JNI (jnigen cannot subclass abstract Java + classes). +* Regenerate JNI bindings for `UploadDataProvider`, `UploadDataSink`, and + `UploadDataProviderProxy`. +* Add `package:async` dependency for `StreamQueue` when streaming uploads. +* Enable streamed request body conformance tests (`canStreamRequestBody: true`). + ## 1.9.0 * Add `CronetEngine.startNetLogToFile` and `CronetEngine.stopNetLog`. diff --git a/pkgs/cronet_http/android/src/main/kotlin/io/flutter/plugins/cronet_http/UploadDataProviderProxy.kt b/pkgs/cronet_http/android/src/main/kotlin/io/flutter/plugins/cronet_http/UploadDataProviderProxy.kt new file mode 100644 index 0000000000..30c6c1f6e2 --- /dev/null +++ b/pkgs/cronet_http/android/src/main/kotlin/io/flutter/plugins/cronet_http/UploadDataProviderProxy.kt @@ -0,0 +1,56 @@ +// Copyright (c) 2023, 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. + +// Cronet uploads request bodies by subclassing the abstract class +// `UploadDataProvider`. Cronet calls `getLength()`, then repeatedly calls +// `read()` to pull bytes into a `ByteBuffer`, and may call `rewind()` on +// redirects before calling `close()` when the upload finishes. +// +// `package:jnigen` does not support subclassing abstract Java classes from Dart +// (see https://github.com/dart-lang/jnigen/issues/348). +// +// This file provides an interface `UploadDataProviderInterface`, which can be +// implemented in Dart, and a wrapper class `UploadDataProviderProxy`, which +// can be passed to Cronet's `UrlRequest.Builder.setUploadDataProvider`. + +package io.flutter.plugins.cronet_http + +import androidx.annotation.Keep +import org.chromium.net.UploadDataProvider +import org.chromium.net.UploadDataSink +import java.nio.ByteBuffer + +// Due to a bug (https://github.com/dart-lang/native/issues/2421) where JNIgen +// does not synchronize nullabilities across the class hierarchy and the fact +// that UploadDataProvider is a Java class with no nullability annotations, +// generating both `UploadDataProviderProxy` and `UploadDataProvider` together +// with different nullabilities causes the super method to have a looser type +// for parameters which is a Dart compilation error. +// That is why `read` and `rewind` parameters on the interface are defined as +// nullable to match `UploadDataProvider` while in reality Cronet always +// passes non-null values. + +@Keep +class UploadDataProviderProxy( + private val callback: UploadDataProviderInterface +) : UploadDataProvider() { + + @Keep + interface UploadDataProviderInterface { + fun getLength(): Long + fun read(uploadDataSink: UploadDataSink?, byteBuffer: ByteBuffer?) + fun rewind(uploadDataSink: UploadDataSink?) + fun close() + } + + override fun getLength(): Long = callback.getLength() + + override fun read(uploadDataSink: UploadDataSink, byteBuffer: ByteBuffer) = + callback.read(uploadDataSink, byteBuffer) + + override fun rewind(uploadDataSink: UploadDataSink) = + callback.rewind(uploadDataSink) + + override fun close() = callback.close() +} diff --git a/pkgs/cronet_http/example/integration_test/client_test.dart b/pkgs/cronet_http/example/integration_test/client_test.dart index 3b3be5efc3..d7d151576c 100644 --- a/pkgs/cronet_http/example/integration_test/client_test.dart +++ b/pkgs/cronet_http/example/integration_test/client_test.dart @@ -19,7 +19,7 @@ Future testConformance() async { try { testAll( CronetClient.defaultCronetEngine, - canStreamRequestBody: false, + canStreamRequestBody: true, canReceiveSetCookieHeaders: true, canSendCookieHeaders: true, supportsAbort: true, @@ -34,7 +34,7 @@ Future testConformance() async { try { testAll( CronetClient.defaultCronetEngine, - canStreamRequestBody: false, + canStreamRequestBody: true, canReceiveSetCookieHeaders: true, canSendCookieHeaders: true, supportsAbort: true, @@ -52,7 +52,7 @@ Future testConformance() async { cacheMode: CacheMode.disabled, userAgent: 'Test Agent (Future)'); return CronetClient.fromCronetEngine(engine); }, - canStreamRequestBody: false, + canStreamRequestBody: true, canReceiveSetCookieHeaders: true, canSendCookieHeaders: true, supportsAbort: true, diff --git a/pkgs/cronet_http/jnigen.yaml b/pkgs/cronet_http/jnigen.yaml index b78ec2d2b4..b1881eb549 100644 --- a/pkgs/cronet_http/jnigen.yaml +++ b/pkgs/cronet_http/jnigen.yaml @@ -11,6 +11,7 @@ output: classes: - 'io.flutter.plugins.cronet_http.UrlRequestCallbackProxy' + - 'io.flutter.plugins.cronet_http.UploadDataProviderProxy' - 'java.io.IOException' - 'java.lang.Exception' - 'java.lang.Throwable' @@ -24,3 +25,5 @@ classes: - 'org.chromium.net.UploadDataProviders' - 'org.chromium.net.UrlRequest' - 'org.chromium.net.UrlResponseInfo' + - 'org.chromium.net.UploadDataSink' + - 'org.chromium.net.UploadDataProvider' diff --git a/pkgs/cronet_http/lib/src/cronet_client.dart b/pkgs/cronet_http/lib/src/cronet_client.dart index 6ccce6eb59..9d2f45f67b 100644 --- a/pkgs/cronet_http/lib/src/cronet_client.dart +++ b/pkgs/cronet_http/lib/src/cronet_client.dart @@ -3,7 +3,10 @@ // BSD-style license that can be found in the LICENSE file. import 'dart:async'; +import 'dart:math'; +import 'dart:typed_data'; +import 'package:async/async.dart'; import 'package:http/http.dart'; import 'package:http_profile/http_profile.dart'; import 'package:jni/jni.dart'; @@ -650,6 +653,113 @@ class CronetClient extends BaseClient { requestMethod: request.method, requestUri: request.url.toString()); + /// Returns true if [stream] includes at least one list with an element. + /// + /// Since [_hasData] consumes [stream], returns a new stream containing the + /// equivalent data. + static Future<(bool, Stream>)> _hasData( + Stream> stream, + ) async { + final queue = StreamQueue(stream); + while (await queue.hasNext && (await queue.peek).isEmpty) { + await queue.next; + } + + return (await queue.hasNext, queue.rest); + } + + /// Streams [stream] to Cronet on demand. + (jb.UploadDataProvider, Future Function()) _streamingUploadProvider( + Stream> stream, + int? contentLength, + HttpClientRequestProfile? profile, + ) { + // Cronet's UploadDataProvider.read() rejects a non-final zero-byte read, so + // strip empty chunks (a StreamedRequest may emit them anywhere) to keep + // every read non-empty. The final zero-byte read is handled + // via onReadSucceeded(true). + final queue = StreamQueue>(stream.where((c) => c.isNotEmpty)); + Uint8List? current; + var offset = 0; + var bytesSent = 0; + var disposed = false; + Future dispose() async { + if (disposed) return; + disposed = true; + await queue.cancel(immediate: true); + } + + // true if `current` has unconsumed bytes; false at end of stream. + Future ensureChunk() async { + if (current != null && offset < current!.length) return true; + if (!await queue.hasNext) { + current = null; + return false; + } + final chunk = await queue.next; + current = chunk is Uint8List ? chunk : Uint8List.fromList(chunk); + offset = 0; + return true; + } + + final impl = + jb.UploadDataProviderProxy$UploadDataProviderInterface.implement( + jb.$UploadDataProviderProxy$UploadDataProviderInterface( + getLength: () => contentLength ?? -1, + read$async: true, + read: (uploadDataSink, byteBuffer) async { + final sink = uploadDataSink!; + try { + if (!await ensureChunk()) { + if (contentLength == null) { + sink.onReadSucceeded(true); + } else { + sink.onReadError(jb.IOException.new1( + 'Body ended before contentLength'.toJString())); + } + return; + } + + final dst = byteBuffer!; + final pos = dst.position; + final available = current!.length - offset; + + if (contentLength != null && + available > contentLength - bytesSent) { + sink.onReadError(jb.IOException.new1( + 'Body exceeded contentLength'.toJString(), + )); + return; + } + + final n = min(dst.remaining, available); + dst.asUint8List().setRange(pos, pos + n, current!, offset); + dst.position = pos + n; + + profile?.requestData.bodySink.add( + Uint8List.sublistView(current!, offset, offset + n), + ); + + offset += n; + bytesSent += n; + sink.onReadSucceeded(false); + } catch (e) { + sink.onReadError(jb.IOException.new1('$e'.toJString())); + } + }, + rewind: (uploadDataSink) { + // One-shot stream: cannot replay. + uploadDataSink!.onRewindError(jb.IOException.new1( + 'Streamed request bodies cannot be rewound'.toJString())); + }, + close: () { + unawaited(dispose()); + }, + ), + ); + return (jb.UploadDataProviderProxy(impl), dispose); + } + /// Sends an HTTP request and asynchronously returns the response. @override Future send(BaseRequest request) async { @@ -684,10 +794,22 @@ class CronetClient extends BaseClient { } final stream = request.finalize(); - final body = await stream.toBytes(); - profile?.requestData.bodySink.add(body); + + final inMemoryBody = request is Request ? await stream.toBytes() : null; + if (inMemoryBody != null) { + profile?.requestData.bodySink.add(inMemoryBody); + } + + final bool hasBody; + Stream>? bodyStream; + if (inMemoryBody != null) { + hasBody = inMemoryBody.isNotEmpty; + } else { + (hasBody, bodyStream) = await _hasData(stream); + } final responseCompleter = Completer(); + Future Function()? disposeUpload; return await using((arena) async { final jUrl = request.url.toString().toJString()..releasedBy(arena); @@ -702,7 +824,7 @@ class CronetClient extends BaseClient { ..setHttpMethod(jMethod); var headers = request.headers; - if (body.isNotEmpty && + if (hasBody && !headers.keys.any((h) => h.toLowerCase() == 'content-type')) { // Cronet requires that requests containing upload data set a // 'Content-Type' header. @@ -711,24 +833,30 @@ class CronetClient extends BaseClient { headers.forEach((k, v) => builder.addHeader( k.toJString()..releasedBy(arena), v.toJString()..releasedBy(arena))); - if (body.isNotEmpty) { - final JByteBuffer data; - try { - data = body.toJByteBuffer()..releasedBy(arena); - } on JThrowable catch (e) { - // There are no unit tests for this code. You can verify this behavior - // manually by incrementally increasing the amount of body data in - // `CronetClient.post` until you get this exception. - if (e.message.contains('java.lang.OutOfMemoryError:')) { - throw ClientException( - 'Not enough memory for request body: ${e.message}', - request.url); + if (hasBody) { + if (inMemoryBody != null) { + final JByteBuffer data; + try { + data = inMemoryBody.toJByteBuffer()..releasedBy(arena); + } on JThrowable catch (e) { + // There are no unit tests for this code. You can verify this + // behavior manually by incrementally increasing the amount of body + // data in `CronetClient.post` until you get this exception. + if (e.message.contains('java.lang.OutOfMemoryError:')) { + throw ClientException( + 'Not enough memory for request body: ${e.message}', + request.url); + } + rethrow; } - rethrow; + builder.setUploadDataProvider( + jb.UploadDataProviders.create$2(data), _executor); + } else { + final (provider, dispose) = _streamingUploadProvider( + bodyStream!, request.contentLength, profile); + disposeUpload = dispose; + builder.setUploadDataProvider(provider, _executor); } - - builder.setUploadDataProvider( - jb.UploadDataProviders.create$2(data), _executor); } // Not releasing `cronetRequest` as it's used in `whenComplete` callback. @@ -736,8 +864,13 @@ class CronetClient extends BaseClient { if (request case Abortable(:final abortTrigger?)) { unawaited(abortTrigger.whenComplete(cronetRequest.cancel)); } - cronetRequest.start(); - return responseCompleter.future; + try { + cronetRequest.start(); + + return await responseCompleter.future; + } finally { + await disposeUpload?.call(); + } }); } } diff --git a/pkgs/cronet_http/lib/src/jni/jni_bindings.dart b/pkgs/cronet_http/lib/src/jni/jni_bindings.dart index 77011c1c35..843965242d 100644 --- a/pkgs/cronet_http/lib/src/jni/jni_bindings.dart +++ b/pkgs/cronet_http/lib/src/jni/jni_bindings.dart @@ -871,6 +871,458 @@ final class $UrlRequestCallbackProxy$Type$ r'Lio/flutter/plugins/cronet_http/UrlRequestCallbackProxy;'; } +/// from: `io.flutter.plugins.cronet_http.UploadDataProviderProxy$UploadDataProviderInterface` +extension type UploadDataProviderProxy$UploadDataProviderInterface._( + jni$_.JObject _$this) implements jni$_.JObject { + static final _class = jni$_.JClass.forName( + r'io/flutter/plugins/cronet_http/UploadDataProviderProxy$UploadDataProviderInterface'); + + /// The type which includes information such as the signature of this class. + static const jni$_.JType + type = $UploadDataProviderProxy$UploadDataProviderInterface$Type$(); + + /// Maps a specific port to the implemented interface. + static final core$_ + .Map + _$impls = {}; + static jni$_.JObjectPtr _$invoke( + core$_.int port, + jni$_.JObjectPtr descriptor, + jni$_.JObjectPtr args, + ) { + return _$invokeMethod( + port, + jni$_.MethodInvocation.fromAddresses( + 0, + descriptor.address, + args.address, + ), + ); + } + + static final jni$_.Pointer< + jni$_.NativeFunction< + jni$_.JObjectPtr Function( + jni$_.Int64, jni$_.JObjectPtr, jni$_.JObjectPtr)>> + _$invokePointer = jni$_.Pointer.fromFunction(_$invoke); + + static jni$_.Pointer _$invokeMethod( + core$_.int $p, + jni$_.MethodInvocation $i, + ) { + try { + final $d = $i.methodDescriptor.toDartString(releaseOriginal: true); + final $a = $i.args; + if ($d == r'getLength()J') { + final $r = _$impls[$p]!.getLength(); + return $r.toJLong().reference.toPointer(); + } + if ($d == + r'read(Lorg/chromium/net/UploadDataSink;Ljava/nio/ByteBuffer;)V') { + _$impls[$p]!.read( + ($a![0] as UploadDataSink?), + ($a![1] as jni$_.JByteBuffer?), + ); + return jni$_.nullptr; + } + if ($d == r'rewind(Lorg/chromium/net/UploadDataSink;)V') { + _$impls[$p]!.rewind( + ($a![0] as UploadDataSink?), + ); + return jni$_.nullptr; + } + if ($d == r'close()V') { + _$impls[$p]!.close(); + return jni$_.nullptr; + } + } catch (e) { + return jni$_.ProtectedJniExtensions.newDartException(e); + } + return jni$_.nullptr; + } + + static void implementIn( + jni$_.JImplementer implementer, + $UploadDataProviderProxy$UploadDataProviderInterface $impl, + ) { + late final jni$_.RawReceivePort $p; + $p = jni$_.RawReceivePort(($m) { + if ($m == null) { + _$impls.remove($p.sendPort.nativePort); + $p.close(); + return; + } + final $i = jni$_.MethodInvocation.fromMessage($m); + final $r = _$invokeMethod($p.sendPort.nativePort, $i); + jni$_.ProtectedJniExtensions.returnResult($i.result, $r); + }); + implementer.add( + r'io.flutter.plugins.cronet_http.UploadDataProviderProxy$UploadDataProviderInterface', + $p, + _$invokePointer, + [ + if ($impl.read$async) + r'read(Lorg/chromium/net/UploadDataSink;Ljava/nio/ByteBuffer;)V', + if ($impl.rewind$async) r'rewind(Lorg/chromium/net/UploadDataSink;)V', + if ($impl.close$async) r'close()V', + ], + ); + final $a = $p.sendPort.nativePort; + _$impls[$a] = $impl; + } + + factory UploadDataProviderProxy$UploadDataProviderInterface.implement( + $UploadDataProviderProxy$UploadDataProviderInterface $impl, + ) { + final $i = jni$_.JImplementer(); + implementIn($i, $impl); + return $i.implement(); + } +} + +extension UploadDataProviderProxy$UploadDataProviderInterface$$Methods + on UploadDataProviderProxy$UploadDataProviderInterface { + static final _id_getLength = + UploadDataProviderProxy$UploadDataProviderInterface._class + .instanceMethodId( + r'getLength', + r'()J', + ); + + static final _getLength = jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + )>>('globalEnv_CallLongMethod') + .asFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + )>(); + + /// from: `public fun getLength(): kotlin.Long` + core$_.int getLength() { + return _getLength(reference.pointer, _id_getLength.pointer).long; + } + + static final _id_read = UploadDataProviderProxy$UploadDataProviderInterface + ._class + .instanceMethodId( + r'read', + r'(Lorg/chromium/net/UploadDataSink;Ljava/nio/ByteBuffer;)V', + ); + + static final _read = jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.VarArgs< + ( + jni$_.Pointer, + jni$_.Pointer + )>)>>('globalEnv_CallVoidMethod') + .asFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.Pointer, + jni$_.Pointer)>(); + + /// from: `public fun read(uploadDataSink: org.chromium.net.UploadDataSink?, byteBuffer: java.nio.ByteBuffer?): kotlin.Unit` + void read( + UploadDataSink? uploadDataSink, + jni$_.JByteBuffer? byteBuffer, + ) { + final _$uploadDataSink = uploadDataSink?.reference ?? jni$_.jNullReference; + final _$byteBuffer = byteBuffer?.reference ?? jni$_.jNullReference; + _read(reference.pointer, _id_read.pointer, _$uploadDataSink.pointer, + _$byteBuffer.pointer) + .check(); + } + + static final _id_rewind = UploadDataProviderProxy$UploadDataProviderInterface + ._class + .instanceMethodId( + r'rewind', + r'(Lorg/chromium/net/UploadDataSink;)V', + ); + + static final _rewind = jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.VarArgs<(jni$_.Pointer,)>)>>( + 'globalEnv_CallVoidMethod') + .asFunction< + jni$_.JThrowablePtr Function(jni$_.Pointer, + jni$_.JMethodIDPtr, jni$_.Pointer)>(); + + /// from: `public fun rewind(uploadDataSink: org.chromium.net.UploadDataSink?): kotlin.Unit` + void rewind( + UploadDataSink? uploadDataSink, + ) { + final _$uploadDataSink = uploadDataSink?.reference ?? jni$_.jNullReference; + _rewind(reference.pointer, _id_rewind.pointer, _$uploadDataSink.pointer) + .check(); + } + + static final _id_close = UploadDataProviderProxy$UploadDataProviderInterface + ._class + .instanceMethodId( + r'close', + r'()V', + ); + + static final _close = jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + )>>('globalEnv_CallVoidMethod') + .asFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + )>(); + + /// from: `public fun close(): kotlin.Unit` + void close() { + _close(reference.pointer, _id_close.pointer).check(); + } +} + +abstract base mixin class $UploadDataProviderProxy$UploadDataProviderInterface { + factory $UploadDataProviderProxy$UploadDataProviderInterface({ + required core$_.int Function() getLength, + required void Function( + UploadDataSink? uploadDataSink, jni$_.JByteBuffer? byteBuffer) + read, + core$_.bool read$async, + required void Function(UploadDataSink? uploadDataSink) rewind, + core$_.bool rewind$async, + required void Function() close, + core$_.bool close$async, + }) = _$UploadDataProviderProxy$UploadDataProviderInterface; + + core$_.int getLength(); + void read(UploadDataSink? uploadDataSink, jni$_.JByteBuffer? byteBuffer); + core$_.bool get read$async => false; + void rewind(UploadDataSink? uploadDataSink); + core$_.bool get rewind$async => false; + void close(); + core$_.bool get close$async => false; +} + +final class _$UploadDataProviderProxy$UploadDataProviderInterface + with $UploadDataProviderProxy$UploadDataProviderInterface { + _$UploadDataProviderProxy$UploadDataProviderInterface({ + required core$_.int Function() getLength, + required void Function( + UploadDataSink? uploadDataSink, jni$_.JByteBuffer? byteBuffer) + read, + this.read$async = false, + required void Function(UploadDataSink? uploadDataSink) rewind, + this.rewind$async = false, + required void Function() close, + this.close$async = false, + }) : _getLength = getLength, + _read = read, + _rewind = rewind, + _close = close; + + final core$_.int Function() _getLength; + final void Function( + UploadDataSink? uploadDataSink, jni$_.JByteBuffer? byteBuffer) _read; + final core$_.bool read$async; + final void Function(UploadDataSink? uploadDataSink) _rewind; + final core$_.bool rewind$async; + final void Function() _close; + final core$_.bool close$async; + + core$_.int getLength() { + return _getLength(); + } + + void read(UploadDataSink? uploadDataSink, jni$_.JByteBuffer? byteBuffer) { + return _read(uploadDataSink, byteBuffer); + } + + void rewind(UploadDataSink? uploadDataSink) { + return _rewind(uploadDataSink); + } + + void close() { + return _close(); + } +} + +final class $UploadDataProviderProxy$UploadDataProviderInterface$Type$ + extends jni$_.JType { + @jni$_.internal + const $UploadDataProviderProxy$UploadDataProviderInterface$Type$(); + + @jni$_.internal + @core$_.override + String get signature => + r'Lio/flutter/plugins/cronet_http/UploadDataProviderProxy$UploadDataProviderInterface;'; +} + +/// from: `io.flutter.plugins.cronet_http.UploadDataProviderProxy` +extension type UploadDataProviderProxy._(jni$_.JObject _$this) + implements UploadDataProvider { + static final _class = jni$_.JClass.forName( + r'io/flutter/plugins/cronet_http/UploadDataProviderProxy'); + + /// The type which includes information such as the signature of this class. + static const jni$_.JType type = + $UploadDataProviderProxy$Type$(); + static final _id_new$ = _class.constructorId( + r'(Lio/flutter/plugins/cronet_http/UploadDataProviderProxy$UploadDataProviderInterface;)V', + ); + + static final _new$ = jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.VarArgs<(jni$_.Pointer,)>)>>( + 'globalEnv_NewObject') + .asFunction< + jni$_.JniResult Function(jni$_.Pointer, + jni$_.JMethodIDPtr, jni$_.Pointer)>(); + + /// from: `public void (io.flutter.plugins.cronet_http.UploadDataProviderProxy$UploadDataProviderInterface uploadDataProviderInterface)` + /// The returned object must be released after use, by calling the [release] method. + factory UploadDataProviderProxy( + UploadDataProviderProxy$UploadDataProviderInterface + uploadDataProviderInterface, + ) { + final _$uploadDataProviderInterface = uploadDataProviderInterface.reference; + return _new$(_class.reference.pointer, _id_new$.pointer, + _$uploadDataProviderInterface.pointer) + .object(); + } +} + +extension UploadDataProviderProxy$$Methods on UploadDataProviderProxy { + static final _id_get$length = UploadDataProviderProxy._class.instanceMethodId( + r'getLength', + r'()J', + ); + + static final _get$length = jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + )>>('globalEnv_CallLongMethod') + .asFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + )>(); + + /// from: `public fun getLength(): kotlin.Long` + core$_.int get length { + return _get$length(reference.pointer, _id_get$length.pointer).long; + } + + static final _id_read = UploadDataProviderProxy._class.instanceMethodId( + r'read', + r'(Lorg/chromium/net/UploadDataSink;Ljava/nio/ByteBuffer;)V', + ); + + static final _read = jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.VarArgs< + ( + jni$_.Pointer, + jni$_.Pointer + )>)>>('globalEnv_CallVoidMethod') + .asFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.Pointer, + jni$_.Pointer)>(); + + /// from: `public fun read(uploadDataSink: org.chromium.net.UploadDataSink, byteBuffer: java.nio.ByteBuffer): kotlin.Unit` + void read( + UploadDataSink uploadDataSink, + jni$_.JByteBuffer byteBuffer, + ) { + final _$uploadDataSink = uploadDataSink.reference; + final _$byteBuffer = byteBuffer.reference; + _read(reference.pointer, _id_read.pointer, _$uploadDataSink.pointer, + _$byteBuffer.pointer) + .check(); + } + + static final _id_rewind = UploadDataProviderProxy._class.instanceMethodId( + r'rewind', + r'(Lorg/chromium/net/UploadDataSink;)V', + ); + + static final _rewind = jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.VarArgs<(jni$_.Pointer,)>)>>( + 'globalEnv_CallVoidMethod') + .asFunction< + jni$_.JThrowablePtr Function(jni$_.Pointer, + jni$_.JMethodIDPtr, jni$_.Pointer)>(); + + /// from: `public fun rewind(uploadDataSink: org.chromium.net.UploadDataSink): kotlin.Unit` + void rewind( + UploadDataSink uploadDataSink, + ) { + final _$uploadDataSink = uploadDataSink.reference; + _rewind(reference.pointer, _id_rewind.pointer, _$uploadDataSink.pointer) + .check(); + } + + static final _id_close = UploadDataProviderProxy._class.instanceMethodId( + r'close', + r'()V', + ); + + static final _close = jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + )>>('globalEnv_CallVoidMethod') + .asFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + )>(); + + /// from: `public fun close(): kotlin.Unit` + void close() { + _close(reference.pointer, _id_close.pointer).check(); + } +} + +final class $UploadDataProviderProxy$Type$ + extends jni$_.JType { + @jni$_.internal + const $UploadDataProviderProxy$Type$(); + + @jni$_.internal + @core$_.override + String get signature => + r'Lio/flutter/plugins/cronet_http/UploadDataProviderProxy;'; +} + /// from: `java.io.IOException` extension type IOException._(jni$_.JObject _$this) implements Exception { static final _class = jni$_.JClass.forName(r'java/io/IOException'); @@ -4678,12 +5130,12 @@ extension type UploadDataProviders._(jni$_.JObject _$this) /// from: `static public org.chromium.net.UploadDataProvider create(java.io.File file)` /// The returned object must be released after use, by calling the [release] method. - static jni$_.JObject? create( + static UploadDataProvider? create( jni$_.JObject? file, ) { final _$file = file?.reference ?? jni$_.jNullReference; return _create(_class.reference.pointer, _id_create.pointer, _$file.pointer) - .object(); + .object(); } static final _id_create$1 = _class.staticMethodId( @@ -4704,13 +5156,13 @@ extension type UploadDataProviders._(jni$_.JObject _$this) /// from: `static public org.chromium.net.UploadDataProvider create(android.os.ParcelFileDescriptor fd)` /// The returned object must be released after use, by calling the [release] method. - static jni$_.JObject? create$1( + static UploadDataProvider? create$1( jni$_.JObject? fd, ) { final _$fd = fd?.reference ?? jni$_.jNullReference; return _create$1( _class.reference.pointer, _id_create$1.pointer, _$fd.pointer) - .object(); + .object(); } static final _id_create$2 = _class.staticMethodId( @@ -4731,13 +5183,13 @@ extension type UploadDataProviders._(jni$_.JObject _$this) /// from: `static public org.chromium.net.UploadDataProvider create(java.nio.ByteBuffer buffer)` /// The returned object must be released after use, by calling the [release] method. - static jni$_.JObject? create$2( + static UploadDataProvider? create$2( jni$_.JByteBuffer? buffer, ) { final _$buffer = buffer?.reference ?? jni$_.jNullReference; return _create$2( _class.reference.pointer, _id_create$2.pointer, _$buffer.pointer) - .object(); + .object(); } static final _id_create$3 = _class.staticMethodId( @@ -4766,7 +5218,7 @@ extension type UploadDataProviders._(jni$_.JObject _$this) /// from: `static public org.chromium.net.UploadDataProvider create(byte[] data, int offset, int length)` /// The returned object must be released after use, by calling the [release] method. - static jni$_.JObject? create$3( + static UploadDataProvider? create$3( jni$_.JByteArray? data, core$_.int offset, core$_.int length, @@ -4774,7 +5226,7 @@ extension type UploadDataProviders._(jni$_.JObject _$this) final _$data = data?.reference ?? jni$_.jNullReference; return _create$3(_class.reference.pointer, _id_create$3.pointer, _$data.pointer, offset, length) - .object(); + .object(); } static final _id_create$4 = _class.staticMethodId( @@ -4795,13 +5247,13 @@ extension type UploadDataProviders._(jni$_.JObject _$this) /// from: `static public org.chromium.net.UploadDataProvider create(byte[] data)` /// The returned object must be released after use, by calling the [release] method. - static jni$_.JObject? create$4( + static UploadDataProvider? create$4( jni$_.JByteArray? data, ) { final _$data = data?.reference ?? jni$_.jNullReference; return _create$4( _class.reference.pointer, _id_create$4.pointer, _$data.pointer) - .object(); + .object(); } } @@ -4978,7 +5430,7 @@ extension UrlRequest$Builder$$Methods on UrlRequest$Builder { /// from: `public abstract org.chromium.net.UrlRequest$Builder setUploadDataProvider(org.chromium.net.UploadDataProvider uploadDataProvider, java.util.concurrent.Executor executor)` /// The returned object must be released after use, by calling the [release] method. UrlRequest$Builder? setUploadDataProvider( - jni$_.JObject? uploadDataProvider, + UploadDataProvider? uploadDataProvider, jni$_.JObject? executor, ) { final _$uploadDataProvider = @@ -6176,3 +6628,247 @@ final class $UrlResponseInfo$Type$ extends jni$_.JType { @core$_.override String get signature => r'Lorg/chromium/net/UrlResponseInfo;'; } + +/// from: `org.chromium.net.UploadDataSink` +extension type UploadDataSink._(jni$_.JObject _$this) implements jni$_.JObject { + static final _class = + jni$_.JClass.forName(r'org/chromium/net/UploadDataSink'); + + /// The type which includes information such as the signature of this class. + static const jni$_.JType type = $UploadDataSink$Type$(); +} + +extension UploadDataSink$$Methods on UploadDataSink { + static final _id_onReadSucceeded = UploadDataSink._class.instanceMethodId( + r'onReadSucceeded', + r'(Z)V', + ); + + static final _onReadSucceeded = jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.VarArgs<(jni$_.Int32,)>)>>('globalEnv_CallVoidMethod') + .asFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, jni$_.JMethodIDPtr, core$_.int)>(); + + /// from: `public abstract void onReadSucceeded(boolean finalChunk)` + void onReadSucceeded( + core$_.bool finalChunk, + ) { + _onReadSucceeded( + reference.pointer, _id_onReadSucceeded.pointer, finalChunk ? 1 : 0) + .check(); + } + + static final _id_onReadError = UploadDataSink._class.instanceMethodId( + r'onReadError', + r'(Ljava/lang/Exception;)V', + ); + + static final _onReadError = jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.VarArgs<(jni$_.Pointer,)>)>>( + 'globalEnv_CallVoidMethod') + .asFunction< + jni$_.JThrowablePtr Function(jni$_.Pointer, + jni$_.JMethodIDPtr, jni$_.Pointer)>(); + + /// from: `public abstract void onReadError(java.lang.Exception exception)` + void onReadError( + Exception? exception, + ) { + final _$exception = exception?.reference ?? jni$_.jNullReference; + _onReadError( + reference.pointer, _id_onReadError.pointer, _$exception.pointer) + .check(); + } + + static final _id_onRewindSucceeded = UploadDataSink._class.instanceMethodId( + r'onRewindSucceeded', + r'()V', + ); + + static final _onRewindSucceeded = jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + )>>('globalEnv_CallVoidMethod') + .asFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + )>(); + + /// from: `public abstract void onRewindSucceeded()` + void onRewindSucceeded() { + _onRewindSucceeded(reference.pointer, _id_onRewindSucceeded.pointer) + .check(); + } + + static final _id_onRewindError = UploadDataSink._class.instanceMethodId( + r'onRewindError', + r'(Ljava/lang/Exception;)V', + ); + + static final _onRewindError = jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.VarArgs<(jni$_.Pointer,)>)>>( + 'globalEnv_CallVoidMethod') + .asFunction< + jni$_.JThrowablePtr Function(jni$_.Pointer, + jni$_.JMethodIDPtr, jni$_.Pointer)>(); + + /// from: `public abstract void onRewindError(java.lang.Exception exception)` + void onRewindError( + Exception? exception, + ) { + final _$exception = exception?.reference ?? jni$_.jNullReference; + _onRewindError( + reference.pointer, _id_onRewindError.pointer, _$exception.pointer) + .check(); + } +} + +final class $UploadDataSink$Type$ extends jni$_.JType { + @jni$_.internal + const $UploadDataSink$Type$(); + + @jni$_.internal + @core$_.override + String get signature => r'Lorg/chromium/net/UploadDataSink;'; +} + +/// from: `org.chromium.net.UploadDataProvider` +extension type UploadDataProvider._(jni$_.JObject _$this) + implements jni$_.JObject { + static final _class = + jni$_.JClass.forName(r'org/chromium/net/UploadDataProvider'); + + /// The type which includes information such as the signature of this class. + static const jni$_.JType type = + $UploadDataProvider$Type$(); +} + +extension UploadDataProvider$$Methods on UploadDataProvider { + static final _id_get$length = UploadDataProvider._class.instanceMethodId( + r'getLength', + r'()J', + ); + + static final _get$length = jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + )>>('globalEnv_CallLongMethod') + .asFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + )>(); + + /// from: `public abstract long getLength()` + core$_.int get length { + return _get$length(reference.pointer, _id_get$length.pointer).long; + } + + static final _id_read = UploadDataProvider._class.instanceMethodId( + r'read', + r'(Lorg/chromium/net/UploadDataSink;Ljava/nio/ByteBuffer;)V', + ); + + static final _read = jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.VarArgs< + ( + jni$_.Pointer, + jni$_.Pointer + )>)>>('globalEnv_CallVoidMethod') + .asFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.Pointer, + jni$_.Pointer)>(); + + /// from: `public abstract void read(org.chromium.net.UploadDataSink uploadDataSink, java.nio.ByteBuffer byteBuffer)` + void read( + UploadDataSink? uploadDataSink, + jni$_.JByteBuffer? byteBuffer, + ) { + final _$uploadDataSink = uploadDataSink?.reference ?? jni$_.jNullReference; + final _$byteBuffer = byteBuffer?.reference ?? jni$_.jNullReference; + _read(reference.pointer, _id_read.pointer, _$uploadDataSink.pointer, + _$byteBuffer.pointer) + .check(); + } + + static final _id_rewind = UploadDataProvider._class.instanceMethodId( + r'rewind', + r'(Lorg/chromium/net/UploadDataSink;)V', + ); + + static final _rewind = jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.VarArgs<(jni$_.Pointer,)>)>>( + 'globalEnv_CallVoidMethod') + .asFunction< + jni$_.JThrowablePtr Function(jni$_.Pointer, + jni$_.JMethodIDPtr, jni$_.Pointer)>(); + + /// from: `public abstract void rewind(org.chromium.net.UploadDataSink uploadDataSink)` + void rewind( + UploadDataSink? uploadDataSink, + ) { + final _$uploadDataSink = uploadDataSink?.reference ?? jni$_.jNullReference; + _rewind(reference.pointer, _id_rewind.pointer, _$uploadDataSink.pointer) + .check(); + } + + static final _id_close = UploadDataProvider._class.instanceMethodId( + r'close', + r'()V', + ); + + static final _close = jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + )>>('globalEnv_CallVoidMethod') + .asFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + )>(); + + /// from: `public void close()` + void close() { + _close(reference.pointer, _id_close.pointer).check(); + } +} + +final class $UploadDataProvider$Type$ extends jni$_.JType { + @jni$_.internal + const $UploadDataProvider$Type$(); + + @jni$_.internal + @core$_.override + String get signature => r'Lorg/chromium/net/UploadDataProvider;'; +} diff --git a/pkgs/cronet_http/pubspec.yaml b/pkgs/cronet_http/pubspec.yaml index 45e97af8ec..bd1a614512 100644 --- a/pkgs/cronet_http/pubspec.yaml +++ b/pkgs/cronet_http/pubspec.yaml @@ -1,5 +1,5 @@ name: cronet_http -version: 1.9.0 +version: 1.10.0 description: >- An Android Flutter plugin that provides access to the Cronet HTTP client. repository: https://github.com/dart-lang/http/tree/master/pkgs/cronet_http @@ -9,6 +9,7 @@ environment: flutter: '>=3.22.0' dependencies: + async: ^2.13.1 flutter: sdk: flutter http: ^1.5.0