Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(781)

Side by Side Diff: components/cronet/android/java/src/org/chromium/net/CronetUrlRequest.java

Issue 1849753002: [Cronet] Separate Cronet implementation and API by package name. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: sync Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.net;
6
7 import android.os.SystemClock;
8 import android.support.annotation.Nullable;
9 import android.util.Log;
10
11 import org.chromium.base.VisibleForTesting;
12 import org.chromium.base.annotations.CalledByNative;
13 import org.chromium.base.annotations.JNIAdditionalImport;
14 import org.chromium.base.annotations.JNINamespace;
15 import org.chromium.base.annotations.NativeClassQualifiedName;
16 import org.chromium.net.CronetEngine.UrlRequestInfo;
17 import org.chromium.net.CronetEngine.UrlRequestMetrics;
18
19 import java.nio.ByteBuffer;
20 import java.util.AbstractMap;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.concurrent.Executor;
26 import java.util.concurrent.RejectedExecutionException;
27
28 import javax.annotation.concurrent.GuardedBy;
29
30 /**
31 * UrlRequest using Chromium HTTP stack implementation. Could be accessed from
32 * any thread on Executor. Cancel can be called from any thread.
33 * All @CallByNative methods are called on native network thread
34 * and post tasks with listener calls onto Executor. Upon return from listener
35 * callback native request adapter is called on executive thread and posts
36 * native tasks to native network thread. Because Cancel could be called from
37 * any thread it is protected by mUrlRequestAdapterLock.
38 */
39 @JNINamespace("cronet")
40 // Qualifies UrlRequest.StatusListener which is used in onStatus, a JNI method.
41 @JNIAdditionalImport(UrlRequest.class)
42 final class CronetUrlRequest implements UrlRequest {
43 private static final UrlRequestMetrics EMPTY_METRICS =
44 new UrlRequestMetrics(null, null, null, null);
45
46 /* Native adapter object, owned by UrlRequest. */
47 @GuardedBy("mUrlRequestAdapterLock")
48 private long mUrlRequestAdapter;
49
50 @GuardedBy("mUrlRequestAdapterLock")
51 private boolean mStarted = false;
52 @GuardedBy("mUrlRequestAdapterLock")
53 private boolean mWaitingOnRedirect = false;
54 @GuardedBy("mUrlRequestAdapterLock")
55 private boolean mWaitingOnRead = false;
56 @GuardedBy("mUrlRequestAdapterLock")
57 @Nullable
58 private final UrlRequestMetricsAccumulator mRequestMetricsAccumulator;
59
60 /*
61 * Synchronize access to mUrlRequestAdapter, mStarted, mWaitingOnRedirect,
62 * and mWaitingOnRead.
63 */
64 private final Object mUrlRequestAdapterLock = new Object();
65 private final CronetUrlRequestContext mRequestContext;
66 private final Executor mExecutor;
67
68 /*
69 * URL chain contains the URL currently being requested, and
70 * all URLs previously requested. New URLs are added before
71 * mCallback.onRedirectReceived is called.
72 */
73 private final List<String> mUrlChain = new ArrayList<String>();
74 private long mReceivedBytesCountFromRedirects;
75
76 private final UrlRequest.Callback mCallback;
77 private final String mInitialUrl;
78 private final int mPriority;
79 private String mInitialMethod;
80 private final HeadersList mRequestHeaders = new HeadersList();
81 private final Collection<Object> mRequestAnnotations;
82 private final boolean mDisableCache;
83 private final boolean mDisableConnectionMigration;
84
85 private CronetUploadDataStream mUploadDataStream;
86
87 private UrlResponseInfo mResponseInfo;
88
89 /*
90 * Listener callback is repeatedly invoked when each read is completed, so i t
91 * is cached as a member variable.
92 */
93 private OnReadCompletedRunnable mOnReadCompletedTask;
94
95 private Runnable mOnDestroyedCallbackForTesting;
96
97 private static final class HeadersList extends ArrayList<Map.Entry<String, S tring>> {}
98
99 private final class OnReadCompletedRunnable implements Runnable {
100 // Buffer passed back from current invocation of onReadCompleted.
101 ByteBuffer mByteBuffer;
102
103 @Override
104 public void run() {
105 // Null out mByteBuffer, to pass buffer ownership to callback or rel ease if done.
106 ByteBuffer buffer = mByteBuffer;
107 mByteBuffer = null;
108
109 try {
110 synchronized (mUrlRequestAdapterLock) {
111 if (isDoneLocked()) {
112 return;
113 }
114 mWaitingOnRead = true;
115 }
116 mCallback.onReadCompleted(CronetUrlRequest.this, mResponseInfo, buffer);
117 } catch (Exception e) {
118 onCallbackException(e);
119 }
120 }
121 }
122
123 CronetUrlRequest(CronetUrlRequestContext requestContext, String url, int pri ority,
124 UrlRequest.Callback callback, Executor executor, Collection<Object> requestAnnotations,
125 boolean metricsCollectionEnabled, boolean disableCache,
126 boolean disableConnectionMigration) {
127 if (url == null) {
128 throw new NullPointerException("URL is required");
129 }
130 if (callback == null) {
131 throw new NullPointerException("Listener is required");
132 }
133 if (executor == null) {
134 throw new NullPointerException("Executor is required");
135 }
136 if (requestAnnotations == null) {
137 throw new NullPointerException("requestAnnotations is required");
138 }
139
140 mRequestContext = requestContext;
141 mInitialUrl = url;
142 mUrlChain.add(url);
143 mPriority = convertRequestPriority(priority);
144 mCallback = callback;
145 mExecutor = executor;
146 mRequestAnnotations = requestAnnotations;
147 mRequestMetricsAccumulator =
148 metricsCollectionEnabled ? new UrlRequestMetricsAccumulator() : null;
149 mDisableCache = disableCache;
150 mDisableConnectionMigration = disableConnectionMigration;
151 }
152
153 @Override
154 public void setHttpMethod(String method) {
155 checkNotStarted();
156 if (method == null) {
157 throw new NullPointerException("Method is required.");
158 }
159 mInitialMethod = method;
160 }
161
162 @Override
163 public void addHeader(String header, String value) {
164 checkNotStarted();
165 if (header == null) {
166 throw new NullPointerException("Invalid header name.");
167 }
168 if (value == null) {
169 throw new NullPointerException("Invalid header value.");
170 }
171 mRequestHeaders.add(new AbstractMap.SimpleImmutableEntry<String, String> (header, value));
172 }
173
174 @Override
175 public void setUploadDataProvider(UploadDataProvider uploadDataProvider, Exe cutor executor) {
176 if (uploadDataProvider == null) {
177 throw new NullPointerException("Invalid UploadDataProvider.");
178 }
179 if (mInitialMethod == null) {
180 mInitialMethod = "POST";
181 }
182 mUploadDataStream = new CronetUploadDataStream(uploadDataProvider, execu tor);
183 }
184
185 @Override
186 public void start() {
187 synchronized (mUrlRequestAdapterLock) {
188 checkNotStarted();
189
190 try {
191 mUrlRequestAdapter =
192 nativeCreateRequestAdapter(mRequestContext.getUrlRequest ContextAdapter(),
193 mInitialUrl, mPriority, mDisableCache, mDisableC onnectionMigration);
194 mRequestContext.onRequestStarted();
195 if (mInitialMethod != null) {
196 if (!nativeSetHttpMethod(mUrlRequestAdapter, mInitialMethod) ) {
197 throw new IllegalArgumentException("Invalid http method " + mInitialMethod);
198 }
199 }
200
201 boolean hasContentType = false;
202 for (Map.Entry<String, String> header : mRequestHeaders) {
203 if (header.getKey().equalsIgnoreCase("Content-Type")
204 && !header.getValue().isEmpty()) {
205 hasContentType = true;
206 }
207 if (!nativeAddRequestHeader(
208 mUrlRequestAdapter, header.getKey(), header.getV alue())) {
209 throw new IllegalArgumentException(
210 "Invalid header " + header.getKey() + "=" + head er.getValue());
211 }
212 }
213 if (mUploadDataStream != null) {
214 if (!hasContentType) {
215 throw new IllegalArgumentException(
216 "Requests with upload data must have a Content-T ype.");
217 }
218 mStarted = true;
219 mUploadDataStream.postTaskToExecutor(new Runnable() {
220 @Override
221 public void run() {
222 mUploadDataStream.initializeWithRequest(CronetUrlReq uest.this);
223 synchronized (mUrlRequestAdapterLock) {
224 if (isDoneLocked()) {
225 return;
226 }
227 mUploadDataStream.attachNativeAdapterToRequest(m UrlRequestAdapter);
228 startInternalLocked();
229 }
230 }
231 });
232 return;
233 }
234 } catch (RuntimeException e) {
235 // If there's an exception, cleanup and then throw the
236 // exception to the caller.
237 destroyRequestAdapter(false);
238 throw e;
239 }
240 mStarted = true;
241 startInternalLocked();
242 }
243 }
244
245 /*
246 * Starts fully configured request. Could execute on UploadDataProvider exec utor.
247 * Caller is expected to ensure that request isn't canceled and mUrlRequestA dapter is valid.
248 */
249 @GuardedBy("mUrlRequestAdapterLock")
250 private void startInternalLocked() {
251 if (mRequestMetricsAccumulator != null) {
252 mRequestMetricsAccumulator.onRequestStarted();
253 }
254 nativeStart(mUrlRequestAdapter);
255 }
256
257 @Override
258 public void followRedirect() {
259 synchronized (mUrlRequestAdapterLock) {
260 if (!mWaitingOnRedirect) {
261 throw new IllegalStateException("No redirect to follow.");
262 }
263 mWaitingOnRedirect = false;
264
265 if (isDoneLocked()) {
266 return;
267 }
268
269 nativeFollowDeferredRedirect(mUrlRequestAdapter);
270 }
271 }
272
273 @Override
274 public void read(ByteBuffer buffer) {
275 Preconditions.checkHasRemaining(buffer);
276 Preconditions.checkDirect(buffer);
277 synchronized (mUrlRequestAdapterLock) {
278 if (!mWaitingOnRead) {
279 throw new IllegalStateException("Unexpected read attempt.");
280 }
281 mWaitingOnRead = false;
282
283 if (isDoneLocked()) {
284 return;
285 }
286
287 if (!nativeReadData(mUrlRequestAdapter, buffer, buffer.position(), b uffer.limit())) {
288 // Still waiting on read. This is just to have consistent
289 // behavior with the other error cases.
290 mWaitingOnRead = true;
291 throw new IllegalArgumentException("Unable to call native read") ;
292 }
293 }
294 }
295
296 @Override
297 public void cancel() {
298 synchronized (mUrlRequestAdapterLock) {
299 if (isDoneLocked() || !mStarted) {
300 return;
301 }
302 destroyRequestAdapter(true);
303 }
304 }
305
306 @Override
307 public boolean isDone() {
308 synchronized (mUrlRequestAdapterLock) {
309 return isDoneLocked();
310 }
311 }
312
313 @GuardedBy("mUrlRequestAdapterLock")
314 private boolean isDoneLocked() {
315 return mStarted && mUrlRequestAdapter == 0;
316 }
317
318 @Override
319 public void getStatus(final UrlRequest.StatusListener listener) {
320 synchronized (mUrlRequestAdapterLock) {
321 if (mUrlRequestAdapter != 0) {
322 nativeGetStatus(mUrlRequestAdapter, listener);
323 return;
324 }
325 }
326 Runnable task = new Runnable() {
327 @Override
328 public void run() {
329 listener.onStatus(UrlRequest.Status.INVALID);
330 }
331 };
332 postTaskToExecutor(task);
333 }
334
335 @VisibleForTesting
336 public void setOnDestroyedCallbackForTesting(Runnable onDestroyedCallbackFor Testing) {
337 mOnDestroyedCallbackForTesting = onDestroyedCallbackForTesting;
338 }
339
340 @VisibleForTesting
341 void setOnDestroyedUploadCallbackForTesting(Runnable onDestroyedUploadCallba ckForTesting) {
342 mUploadDataStream.setOnDestroyedCallbackForTesting(onDestroyedUploadCall backForTesting);
343 }
344
345 @VisibleForTesting
346 long getUrlRequestAdapterForTesting() {
347 synchronized (mUrlRequestAdapterLock) {
348 return mUrlRequestAdapter;
349 }
350 }
351
352 /**
353 * Posts task to application Executor. Used for Listener callbacks
354 * and other tasks that should not be executed on network thread.
355 */
356 private void postTaskToExecutor(Runnable task) {
357 try {
358 mExecutor.execute(task);
359 } catch (RejectedExecutionException failException) {
360 Log.e(CronetUrlRequestContext.LOG_TAG,
361 "Exception posting task to executor", failException);
362 // If posting a task throws an exception, then there is no choice
363 // but to destroy the request without invoking the callback.
364 destroyRequestAdapter(false);
365 }
366 }
367
368 private static int convertRequestPriority(int priority) {
369 switch (priority) {
370 case Builder.REQUEST_PRIORITY_IDLE:
371 return RequestPriority.IDLE;
372 case Builder.REQUEST_PRIORITY_LOWEST:
373 return RequestPriority.LOWEST;
374 case Builder.REQUEST_PRIORITY_LOW:
375 return RequestPriority.LOW;
376 case Builder.REQUEST_PRIORITY_MEDIUM:
377 return RequestPriority.MEDIUM;
378 case Builder.REQUEST_PRIORITY_HIGHEST:
379 return RequestPriority.HIGHEST;
380 default:
381 return RequestPriority.MEDIUM;
382 }
383 }
384
385 private UrlResponseInfo prepareResponseInfoOnNetworkThread(int httpStatusCod e,
386 String httpStatusText, String[] headers, boolean wasCached, String n egotiatedProtocol,
387 String proxyServer) {
388 HeadersList headersList = new HeadersList();
389 for (int i = 0; i < headers.length; i += 2) {
390 headersList.add(new AbstractMap.SimpleImmutableEntry<String, String> (
391 headers[i], headers[i + 1]));
392 }
393 return new UrlResponseInfo(new ArrayList<String>(mUrlChain), httpStatusC ode, httpStatusText,
394 headersList, wasCached, negotiatedProtocol, proxyServer);
395 }
396
397 private void checkNotStarted() {
398 synchronized (mUrlRequestAdapterLock) {
399 if (mStarted || isDoneLocked()) {
400 throw new IllegalStateException("Request is already started.");
401 }
402 }
403 }
404
405 private void destroyRequestAdapter(boolean sendOnCanceled) {
406 synchronized (mUrlRequestAdapterLock) {
407 if (mUrlRequestAdapter == 0) {
408 return;
409 }
410 if (mRequestMetricsAccumulator != null) {
411 mRequestMetricsAccumulator.onRequestFinished();
412 }
413 nativeDestroy(mUrlRequestAdapter, sendOnCanceled);
414 mRequestContext.reportFinished(this);
415 mRequestContext.onRequestDestroyed();
416 mUrlRequestAdapter = 0;
417 if (mOnDestroyedCallbackForTesting != null) {
418 mOnDestroyedCallbackForTesting.run();
419 }
420 }
421 }
422
423 /**
424 * If callback method throws an exception, request gets canceled
425 * and exception is reported via onFailed listener callback.
426 * Only called on the Executor.
427 */
428 private void onCallbackException(Exception e) {
429 UrlRequestException requestError =
430 new UrlRequestException("Exception received from UrlRequest.Call back", e);
431 Log.e(CronetUrlRequestContext.LOG_TAG,
432 "Exception in CalledByNative method", e);
433 // Do not call into listener if request is finished.
434 synchronized (mUrlRequestAdapterLock) {
435 if (isDoneLocked()) {
436 return;
437 }
438 destroyRequestAdapter(false);
439 }
440 try {
441 mCallback.onFailed(this, mResponseInfo, requestError);
442 } catch (Exception failException) {
443 Log.e(CronetUrlRequestContext.LOG_TAG,
444 "Exception notifying of failed request", failException);
445 }
446 }
447
448 /**
449 * Called when UploadDataProvider encounters an error.
450 */
451 void onUploadException(Throwable e) {
452 UrlRequestException uploadError =
453 new UrlRequestException("Exception received from UploadDataProvi der", e);
454 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in upload method", e);
455 failWithException(uploadError);
456 }
457
458 /**
459 * Fails the request with an exception. Can be called on any thread.
460 */
461 private void failWithException(final UrlRequestException exception) {
462 Runnable task = new Runnable() {
463 @Override
464 public void run() {
465 synchronized (mUrlRequestAdapterLock) {
466 if (isDoneLocked()) {
467 return;
468 }
469 destroyRequestAdapter(false);
470 }
471 try {
472 mCallback.onFailed(CronetUrlRequest.this, mResponseInfo, exc eption);
473 } catch (Exception e) {
474 Log.e(CronetUrlRequestContext.LOG_TAG,
475 "Exception in onError method", e);
476 }
477 }
478 };
479 postTaskToExecutor(task);
480 }
481
482 ////////////////////////////////////////////////
483 // Private methods called by the native code.
484 // Always called on network thread.
485 ////////////////////////////////////////////////
486
487 /**
488 * Called before following redirects. The redirect will automatically be
489 * followed, unless the request is paused or canceled during this
490 * callback. If the redirect response has a body, it will be ignored.
491 * This will only be called between start and onResponseStarted.
492 *
493 * @param newLocation Location where request is redirected.
494 * @param httpStatusCode from redirect response
495 * @param receivedBytesCount count of bytes received for redirect response
496 * @param headers an array of response headers with keys at the even indices
497 * followed by the corresponding values at the odd indices.
498 */
499 @SuppressWarnings("unused")
500 @CalledByNative
501 private void onRedirectReceived(final String newLocation, int httpStatusCode ,
502 String httpStatusText, String[] headers, boolean wasCached, String n egotiatedProtocol,
503 String proxyServer, long receivedBytesCount) {
504 final UrlResponseInfo responseInfo = prepareResponseInfoOnNetworkThread( httpStatusCode,
505 httpStatusText, headers, wasCached, negotiatedProtocol, proxySer ver);
506 mReceivedBytesCountFromRedirects += receivedBytesCount;
507 responseInfo.setReceivedBytesCount(mReceivedBytesCountFromRedirects);
508
509 // Have to do this after creating responseInfo.
510 mUrlChain.add(newLocation);
511
512 Runnable task = new Runnable() {
513 @Override
514 public void run() {
515 synchronized (mUrlRequestAdapterLock) {
516 if (isDoneLocked()) {
517 return;
518 }
519 mWaitingOnRedirect = true;
520 }
521
522 try {
523 mCallback.onRedirectReceived(CronetUrlRequest.this, response Info, newLocation);
524 } catch (Exception e) {
525 onCallbackException(e);
526 }
527 }
528 };
529 postTaskToExecutor(task);
530 }
531
532 /**
533 * Called when the final set of headers, after all redirects,
534 * is received. Can only be called once for each request.
535 */
536 @SuppressWarnings("unused")
537 @CalledByNative
538 private void onResponseStarted(int httpStatusCode, String httpStatusText, St ring[] headers,
539 boolean wasCached, String negotiatedProtocol, String proxyServer) {
540 mResponseInfo = prepareResponseInfoOnNetworkThread(httpStatusCode, httpS tatusText, headers,
541 wasCached, negotiatedProtocol, proxyServer);
542 Runnable task = new Runnable() {
543 @Override
544 public void run() {
545 synchronized (mUrlRequestAdapterLock) {
546 if (isDoneLocked()) {
547 return;
548 }
549 if (mRequestMetricsAccumulator != null) {
550 mRequestMetricsAccumulator.onResponseStarted();
551 }
552 mWaitingOnRead = true;
553 }
554
555 try {
556 mCallback.onResponseStarted(CronetUrlRequest.this, mResponse Info);
557 } catch (Exception e) {
558 onCallbackException(e);
559 }
560 }
561 };
562 postTaskToExecutor(task);
563 }
564
565 /**
566 * Called whenever data is received. The ByteBuffer remains
567 * valid only until listener callback. Or if the callback
568 * pauses the request, it remains valid until the request is resumed.
569 * Cancelling the request also invalidates the buffer.
570 *
571 * @param byteBuffer ByteBuffer containing received data, starting at
572 * initialPosition. Guaranteed to have at least one read byte. Its
573 * limit has not yet been updated to reflect the bytes read.
574 * @param bytesRead Number of bytes read.
575 * @param initialPosition Original position of byteBuffer when passed to
576 * read(). Used as a minimal check that the buffer hasn't been
577 * modified while reading from the network.
578 * @param initialLimit Original limit of byteBuffer when passed to
579 * read(). Used as a minimal check that the buffer hasn't been
580 * modified while reading from the network.
581 * @param receivedBytesCount number of bytes received.
582 */
583 @SuppressWarnings("unused")
584 @CalledByNative
585 private void onReadCompleted(final ByteBuffer byteBuffer, int bytesRead, int initialPosition,
586 int initialLimit, long receivedBytesCount) {
587 mResponseInfo.setReceivedBytesCount(mReceivedBytesCountFromRedirects + r eceivedBytesCount);
588 if (byteBuffer.position() != initialPosition || byteBuffer.limit() != in itialLimit) {
589 failWithException(new UrlRequestException(
590 "ByteBuffer modified externally during read", null));
591 return;
592 }
593 if (mOnReadCompletedTask == null) {
594 mOnReadCompletedTask = new OnReadCompletedRunnable();
595 }
596 byteBuffer.position(initialPosition + bytesRead);
597 mOnReadCompletedTask.mByteBuffer = byteBuffer;
598 postTaskToExecutor(mOnReadCompletedTask);
599 }
600
601 /**
602 * Called when request is completed successfully, no callbacks will be
603 * called afterwards.
604 *
605 * @param receivedBytesCount number of bytes received.
606 */
607 @SuppressWarnings("unused")
608 @CalledByNative
609 private void onSucceeded(long receivedBytesCount) {
610 mResponseInfo.setReceivedBytesCount(mReceivedBytesCountFromRedirects + r eceivedBytesCount);
611 Runnable task = new Runnable() {
612 @Override
613 public void run() {
614 synchronized (mUrlRequestAdapterLock) {
615 if (isDoneLocked()) {
616 return;
617 }
618 // Destroy adapter first, so request context could be shut
619 // down from the listener.
620 destroyRequestAdapter(false);
621 }
622 try {
623 mCallback.onSucceeded(CronetUrlRequest.this, mResponseInfo);
624 } catch (Exception e) {
625 Log.e(CronetUrlRequestContext.LOG_TAG,
626 "Exception in onComplete method", e);
627 }
628 }
629 };
630 postTaskToExecutor(task);
631 }
632
633 /**
634 * Called when error has occured, no callbacks will be called afterwards.
635 *
636 * @param errorCode error code from {@link UrlRequestException.ERROR_LISTENE R_EXCEPTION_THROWN
637 * UrlRequestException.ERROR_*}.
638 * @param nativeError native net error code.
639 * @param errorString textual representation of the error code.
640 * @param receivedBytesCount number of bytes received.
641 */
642 @SuppressWarnings("unused")
643 @CalledByNative
644 private void onError(int errorCode, int nativeError, int nativeQuicError, St ring errorString,
645 long receivedBytesCount) {
646 if (mResponseInfo != null) {
647 mResponseInfo.setReceivedBytesCount(
648 mReceivedBytesCountFromRedirects + receivedBytesCount);
649 }
650 if (errorCode == UrlRequestException.ERROR_QUIC_PROTOCOL_FAILED) {
651 failWithException(new QuicException(
652 "Exception in CronetUrlRequest: " + errorString, nativeError , nativeQuicError));
653 } else {
654 failWithException(new UrlRequestException(
655 "Exception in CronetUrlRequest: " + errorString, errorCode, nativeError));
656 }
657 }
658
659 /**
660 * Called when request is canceled, no callbacks will be called afterwards.
661 */
662 @SuppressWarnings("unused")
663 @CalledByNative
664 private void onCanceled() {
665 Runnable task = new Runnable() {
666 @Override
667 public void run() {
668 try {
669 mCallback.onCanceled(CronetUrlRequest.this, mResponseInfo);
670 } catch (Exception e) {
671 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in onCance led method", e);
672 }
673 }
674 };
675 postTaskToExecutor(task);
676 }
677
678 /**
679 * Called by the native code when request status is fetched from the
680 * native stack.
681 */
682 @SuppressWarnings("unused")
683 @CalledByNative
684 private void onStatus(final UrlRequest.StatusListener listener, final int lo adState) {
685 Runnable task = new Runnable() {
686 @Override
687 public void run() {
688 listener.onStatus(UrlRequest.Status.convertLoadState(loadState)) ;
689 }
690 };
691 postTaskToExecutor(task);
692 }
693
694 UrlRequestInfo getRequestInfo() {
695 return new UrlRequestInfo(mInitialUrl, mRequestAnnotations,
696 (mRequestMetricsAccumulator != null ? mRequestMetricsAccumulator .getRequestMetrics()
697 : EMPTY_METRICS),
698 mResponseInfo);
699 }
700
701 private final class UrlRequestMetricsAccumulator {
702 @Nullable private Long mRequestStartTime;
703 @Nullable private Long mTtfbMs;
704 @Nullable private Long mTotalTimeMs;
705
706 private UrlRequestMetrics getRequestMetrics() {
707 return new UrlRequestMetrics(mTtfbMs, mTotalTimeMs,
708 null, // TODO(klm): Compute sentBytesCount.
709 (mResponseInfo != null ? mResponseInfo.getReceivedBytesCount () : 0));
710 }
711
712 private void onRequestStarted() {
713 if (mRequestStartTime != null) {
714 throw new IllegalStateException("onRequestStarted called repeate dly");
715 }
716 mRequestStartTime = SystemClock.elapsedRealtime();
717 }
718
719 private void onRequestFinished() {
720 if (mRequestStartTime != null && mTotalTimeMs == null) {
721 mTotalTimeMs = SystemClock.elapsedRealtime() - mRequestStartTime ;
722 }
723 }
724
725 private void onResponseStarted() {
726 if (mRequestStartTime != null && mTtfbMs == null) {
727 mTtfbMs = SystemClock.elapsedRealtime() - mRequestStartTime;
728 }
729 }
730 }
731
732 // Native methods are implemented in cronet_url_request_adapter.cc.
733
734 private native long nativeCreateRequestAdapter(long urlRequestContextAdapter , String url,
735 int priority, boolean disableCache, boolean disableConnectionMigrati on);
736
737 @NativeClassQualifiedName("CronetURLRequestAdapter")
738 private native boolean nativeSetHttpMethod(long nativePtr, String method);
739
740 @NativeClassQualifiedName("CronetURLRequestAdapter")
741 private native boolean nativeAddRequestHeader(long nativePtr, String name, S tring value);
742
743 @NativeClassQualifiedName("CronetURLRequestAdapter")
744 private native void nativeStart(long nativePtr);
745
746 @NativeClassQualifiedName("CronetURLRequestAdapter")
747 private native void nativeFollowDeferredRedirect(long nativePtr);
748
749 @NativeClassQualifiedName("CronetURLRequestAdapter")
750 private native boolean nativeReadData(long nativePtr, ByteBuffer byteBuffer,
751 int position, int capacity);
752
753 @NativeClassQualifiedName("CronetURLRequestAdapter")
754 private native void nativeDestroy(long nativePtr, boolean sendOnCanceled);
755
756 @NativeClassQualifiedName("CronetURLRequestAdapter")
757 private native void nativeGetStatus(long nativePtr, UrlRequest.StatusListene r listener);
758 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698