@@ -135,6 +135,103 @@ Map<String, String> _cronetToClientHeaders(
135
135
key.toDartString (releaseOriginal: true ).toLowerCase (),
136
136
value.join (',' )));
137
137
138
+ jb.UrlRequestCallbackProxy_UrlRequestCallbackInterface _urlRequestCallbacks (
139
+ BaseRequest request, Completer <StreamedResponse > responseCompleter) {
140
+ StreamController <List <int >>? responseStream;
141
+ jb.ByteBuffer ? byteBuffer;
142
+ var numRedirects = 0 ;
143
+
144
+ // The order of callbacks generated by Cronet is documented here:
145
+ // https://developer.android.com/guide/topics/connectivity/cronet/lifecycle
146
+ return jb.UrlRequestCallbackProxy_UrlRequestCallbackInterface .implement (
147
+ jb.$UrlRequestCallbackProxy_UrlRequestCallbackInterfaceImpl (
148
+ onResponseStarted: (urlRequest, responseInfo) {
149
+ responseStream = StreamController ();
150
+ final responseHeaders =
151
+ _cronetToClientHeaders (responseInfo.getAllHeaders ());
152
+ int ? contentLength;
153
+
154
+ switch (responseHeaders['content-length' ]) {
155
+ case final contentLengthHeader?
156
+ when ! _digitRegex.hasMatch (contentLengthHeader):
157
+ responseCompleter.completeError (ClientException (
158
+ 'Invalid content-length header [$contentLengthHeader ].' ,
159
+ request.url,
160
+ ));
161
+ urlRequest.cancel ();
162
+ return ;
163
+ case final contentLengthHeader?:
164
+ contentLength = int .parse (contentLengthHeader);
165
+ }
166
+ responseCompleter.complete (StreamedResponse (
167
+ responseStream! .stream,
168
+ responseInfo.getHttpStatusCode (),
169
+ contentLength: contentLength,
170
+ reasonPhrase: responseInfo
171
+ .getHttpStatusText ()
172
+ .toDartString (releaseOriginal: true ),
173
+ request: request,
174
+ isRedirect: false ,
175
+ headers: responseHeaders,
176
+ ));
177
+
178
+ byteBuffer = jb.ByteBuffer .allocateDirect (1024 * 1024 );
179
+ urlRequest.read (byteBuffer! );
180
+ },
181
+ onRedirectReceived: (urlRequest, responseInfo, newLocationUrl) {
182
+ if (! request.followRedirects) {
183
+ urlRequest.cancel ();
184
+ responseCompleter.complete (StreamedResponse (
185
+ const Stream .empty (), // Cronet provides no body for redirects.
186
+ responseInfo.getHttpStatusCode (),
187
+ contentLength: 0 ,
188
+ reasonPhrase: responseInfo
189
+ .getHttpStatusText ()
190
+ .toDartString (releaseOriginal: true ),
191
+ request: request,
192
+ isRedirect: true ,
193
+ headers: _cronetToClientHeaders (responseInfo.getAllHeaders ())));
194
+ return ;
195
+ }
196
+ ++ numRedirects;
197
+ if (numRedirects <= request.maxRedirects) {
198
+ urlRequest.followRedirect ();
199
+ } else {
200
+ urlRequest.cancel ();
201
+ responseCompleter.completeError (
202
+ ClientException ('Redirect limit exceeded' , request.url));
203
+ }
204
+ },
205
+ onReadCompleted: (urlRequest, responseInfo, byteBuffer) {
206
+ byteBuffer.flip ();
207
+
208
+ final remaining = byteBuffer.remaining ();
209
+ final data = Uint8List (remaining);
210
+ // TODO: Use a more efficient approach when
211
+ // https://github.com/dart-lang/jnigen/issues/387 is fixed.
212
+ for (var i = 0 ; i < remaining; ++ i) {
213
+ data[i] = byteBuffer.get1 (i);
214
+ }
215
+ responseStream! .add (data);
216
+ byteBuffer.clear ();
217
+ urlRequest.read (byteBuffer);
218
+ },
219
+ onSucceeded: (urlRequest, responseInfo) {
220
+ responseStream! .sink.close ();
221
+ },
222
+ onFailed: (urlRequest, responseInfo, cronetException) {
223
+ final error = ClientException (
224
+ 'Cronet exception: ${cronetException .toString ()}' , request.url);
225
+ if (responseStream == null ) {
226
+ responseCompleter.completeError (error);
227
+ } else {
228
+ responseStream! .addError (error);
229
+ responseStream! .close ();
230
+ }
231
+ },
232
+ ));
233
+ }
234
+
138
235
/// A HTTP [Client] based on the
139
236
/// [Cronet] (https://developer.android.com/guide/topics/connectivity/cronet)
140
237
/// network stack.
@@ -219,106 +316,10 @@ class CronetClient extends BaseClient {
219
316
final responseCompleter = Completer <StreamedResponse >();
220
317
final engine = _engine! ._engine;
221
318
222
- late jb.UrlRequest cronetRequest;
223
- var numRedirects = 0 ;
224
- StreamController <List <int >>? responseStream;
225
- jb.ByteBuffer ? byteBuffer;
226
-
227
- // The order of callbacks generated by Cronet is documented here:
228
- // https://developer.android.com/guide/topics/connectivity/cronet/lifecycle
229
-
230
- final cronetCallbacks =
231
- jb.UrlRequestCallbackProxy_UrlRequestCallbackInterface .implement (
232
- jb.$UrlRequestCallbackProxy_UrlRequestCallbackInterfaceImpl (
233
- onResponseStarted: (urlRequest, responseInfo) {
234
- responseStream = StreamController ();
235
- final responseHeaders =
236
- _cronetToClientHeaders (responseInfo.getAllHeaders ());
237
- int ? contentLength;
238
-
239
- switch (responseHeaders['content-length' ]) {
240
- case final contentLengthHeader?
241
- when ! _digitRegex.hasMatch (contentLengthHeader):
242
- responseCompleter.completeError (ClientException (
243
- 'Invalid content-length header [$contentLengthHeader ].' ,
244
- request.url,
245
- ));
246
- urlRequest.cancel ();
247
- return ;
248
- case final contentLengthHeader?:
249
- contentLength = int .parse (contentLengthHeader);
250
- }
251
- responseCompleter.complete (StreamedResponse (
252
- responseStream! .stream,
253
- responseInfo.getHttpStatusCode (),
254
- contentLength: contentLength,
255
- reasonPhrase: responseInfo
256
- .getHttpStatusText ()
257
- .toDartString (releaseOriginal: true ),
258
- request: request,
259
- isRedirect: false ,
260
- headers: responseHeaders,
261
- ));
262
-
263
- byteBuffer = jb.ByteBuffer .allocateDirect (1024 * 1024 );
264
- urlRequest.read (byteBuffer! );
265
- },
266
- onRedirectReceived: (urlRequest, responseInfo, newLocationUrl) {
267
- if (! request.followRedirects) {
268
- cronetRequest.cancel ();
269
- responseCompleter.complete (StreamedResponse (
270
- const Stream .empty (), // Cronet provides no body for redirects.
271
- responseInfo.getHttpStatusCode (),
272
- contentLength: 0 ,
273
- reasonPhrase: responseInfo
274
- .getHttpStatusText ()
275
- .toDartString (releaseOriginal: true ),
276
- request: request,
277
- isRedirect: true ,
278
- headers: _cronetToClientHeaders (responseInfo.getAllHeaders ())));
279
- return ;
280
- }
281
- ++ numRedirects;
282
- if (numRedirects <= request.maxRedirects) {
283
- cronetRequest.followRedirect ();
284
- } else {
285
- cronetRequest.cancel ();
286
- responseCompleter.completeError (
287
- ClientException ('Redirect limit exceeded' , request.url));
288
- }
289
- },
290
- onReadCompleted: (urlRequest, responseInfo, byteBuffer) {
291
- byteBuffer.flip ();
292
-
293
- final remaining = byteBuffer.remaining ();
294
- final data = Uint8List (remaining);
295
- // TODO: Use a more efficient approach when
296
- // https://github.com/dart-lang/jnigen/issues/387 is fixed.
297
- for (var i = 0 ; i < remaining; ++ i) {
298
- data[i] = byteBuffer.get1 (i);
299
- }
300
- responseStream! .add (data);
301
- byteBuffer.clear ();
302
- cronetRequest.read (byteBuffer);
303
- },
304
- onSucceeded: (urlRequest, responseInfo) {
305
- responseStream! .sink.close ();
306
- },
307
- onFailed: (urlRequest, responseInfo, cronetException) {
308
- final error = ClientException (
309
- 'Cronet exception: ${cronetException .toString ()}' , request.url);
310
- if (responseStream == null ) {
311
- responseCompleter.completeError (error);
312
- } else {
313
- responseStream! .addError (error);
314
- responseStream! .close ();
315
- }
316
- },
317
- ));
318
-
319
319
final builder = engine.newUrlRequestBuilder (
320
320
request.url.toString ().toJString (),
321
- jb.UrlRequestCallbackProxy .new1 (cronetCallbacks),
321
+ jb.UrlRequestCallbackProxy .new1 (
322
+ _urlRequestCallbacks (request, responseCompleter)),
322
323
_executor,
323
324
);
324
325
@@ -341,7 +342,7 @@ class CronetClient extends BaseClient {
341
342
builder.setUploadDataProvider (
342
343
jb.UploadDataProviders .create4 (bodyBytes), _executor);
343
344
}
344
- cronetRequest = builder.build (). .start ();
345
- return responseCompleter.future. whenComplete (() => byteBuffer ? . release ()) ;
345
+ builder.build ().start ();
346
+ return responseCompleter.future;
346
347
}
347
348
}
0 commit comments