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

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

Issue 945843003: [Cronet] Do not call into native adapter after it is destroyed in ChromiumUrlRequest.java (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressed Misha's comments Created 5 years, 9 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
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.net; 5 package org.chromium.net;
6 6
7 import android.util.Log; 7 import android.util.Log;
8 8
9 import org.apache.http.conn.ConnectTimeoutException; 9 import org.apache.http.conn.ConnectTimeoutException;
10 import org.chromium.base.CalledByNative; 10 import org.chromium.base.CalledByNative;
(...skipping 29 matching lines...) Expand all
40 private final WritableByteChannel mSink; 40 private final WritableByteChannel mSink;
41 private Map<String, String> mAdditionalHeaders; 41 private Map<String, String> mAdditionalHeaders;
42 private String mUploadContentType; 42 private String mUploadContentType;
43 private String mMethod; 43 private String mMethod;
44 private byte[] mUploadData; 44 private byte[] mUploadData;
45 private ReadableByteChannel mUploadChannel; 45 private ReadableByteChannel mUploadChannel;
46 private boolean mChunkedUpload; 46 private boolean mChunkedUpload;
47 private IOException mSinkException; 47 private IOException mSinkException;
48 private volatile boolean mStarted; 48 private volatile boolean mStarted;
49 private volatile boolean mCanceled; 49 private volatile boolean mCanceled;
50 private volatile boolean mRecycled;
51 private volatile boolean mFinished; 50 private volatile boolean mFinished;
52 private boolean mHeadersAvailable; 51 private boolean mHeadersAvailable;
53 private String mContentType;
54 private long mUploadContentLength; 52 private long mUploadContentLength;
55 private final HttpUrlRequestListener mListener; 53 private final HttpUrlRequestListener mListener;
56 private boolean mBufferFullResponse; 54 private boolean mBufferFullResponse;
57 private long mOffset; 55 private long mOffset;
58 private long mContentLength;
59 private long mContentLengthLimit; 56 private long mContentLengthLimit;
60 private boolean mCancelIfContentLengthOverLimit; 57 private boolean mCancelIfContentLengthOverLimit;
61 private boolean mContentLengthOverLimit; 58 private boolean mContentLengthOverLimit;
62 private boolean mSkippingToOffset; 59 private boolean mSkippingToOffset;
63 private long mSize; 60 private long mSize;
61
64 // Indicates whether redirects have been disabled. 62 // Indicates whether redirects have been disabled.
65 private boolean mDisableRedirects; 63 private boolean mDisableRedirects;
64
65 // Http status code. Default to 0. Populated in onResponseStarted().
66 private int mHttpStatusCode = 0;
67
68 // Http status text. Default to null. Populated in onResponseStarted().
69 private String mHttpStatusText;
70
71 // Content type. Default to null. Populated in onResponseStarted().
72 private String mContentType;
73
74 // Compressed content length as reported by the server. Populated in onRespo nseStarted().
75 private long mContentLength;
76
77 // Native error code. Default to no error. Populated in onRequestComplete().
78 private int mErrorCode = ChromiumUrlRequestError.SUCCESS;
79
80 // Native error string. Default to null. Populated in onRequestComplete().
81 private String mErrorString;
82
83 // Protects access of mUrlRequestAdapter, mStarted, mCanceled, and mFinished .
66 private final Object mLock = new Object(); 84 private final Object mLock = new Object();
67 85
68 public ChromiumUrlRequest(ChromiumUrlRequestContext requestContext, 86 public ChromiumUrlRequest(ChromiumUrlRequestContext requestContext,
69 String url, int priority, Map<String, String> headers, 87 String url, int priority, Map<String, String> headers,
70 HttpUrlRequestListener listener) { 88 HttpUrlRequestListener listener) {
71 this(requestContext, url, priority, headers, 89 this(requestContext, url, priority, headers,
72 new ChunkedWritableByteChannel(), listener); 90 new ChunkedWritableByteChannel(), listener);
73 mBufferFullResponse = true; 91 mBufferFullResponse = true;
74 } 92 }
75 93
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
125 } 143 }
126 144
127 @Override 145 @Override
128 public void setContentLengthLimit(long limit, boolean cancelEarly) { 146 public void setContentLengthLimit(long limit, boolean cancelEarly) {
129 mContentLengthLimit = limit; 147 mContentLengthLimit = limit;
130 mCancelIfContentLengthOverLimit = cancelEarly; 148 mCancelIfContentLengthOverLimit = cancelEarly;
131 } 149 }
132 150
133 @Override 151 @Override
134 public int getHttpStatusCode() { 152 public int getHttpStatusCode() {
135 int httpStatusCode = nativeGetHttpStatusCode(mUrlRequestAdapter);
136
137 // TODO(mef): Investigate the following: 153 // TODO(mef): Investigate the following:
138 // If we have been able to successfully resume a previously interrupted 154 // If we have been able to successfully resume a previously interrupted
139 // download, the status code will be 206, not 200. Since the rest of the 155 // download, the status code will be 206, not 200. Since the rest of the
140 // application is expecting 200 to indicate success, we need to fake it. 156 // application is expecting 200 to indicate success, we need to fake it.
141 if (httpStatusCode == 206) { 157 if (mHttpStatusCode == 206) {
142 httpStatusCode = 200; 158 return 200;
143 } 159 }
144 return httpStatusCode; 160 return mHttpStatusCode;
145 } 161 }
146 162
147 @Override 163 @Override
148 public String getHttpStatusText() { 164 public String getHttpStatusText() {
149 return nativeGetHttpStatusText(mUrlRequestAdapter); 165 return mHttpStatusText;
150 } 166 }
151 167
152 /** 168 /**
153 * Returns an exception if any, or null if the request was completed 169 * Returns an exception if any, or null if the request has not completed or
154 * successfully. 170 * completed successfully.
155 */ 171 */
156 @Override 172 @Override
157 public IOException getException() { 173 public IOException getException() {
158 if (mSinkException != null) { 174 if (mSinkException != null) {
159 return mSinkException; 175 return mSinkException;
160 } 176 }
161 177
162 validateNotRecycled(); 178 switch (mErrorCode) {
163
164 int errorCode = nativeGetErrorCode(mUrlRequestAdapter);
165 switch (errorCode) {
166 case ChromiumUrlRequestError.SUCCESS: 179 case ChromiumUrlRequestError.SUCCESS:
167 if (mContentLengthOverLimit) { 180 if (mContentLengthOverLimit) {
168 return new ResponseTooLargeException(); 181 return new ResponseTooLargeException();
169 } 182 }
170 return null; 183 return null;
171 case ChromiumUrlRequestError.UNKNOWN: 184 case ChromiumUrlRequestError.UNKNOWN:
172 return new IOException( 185 return new IOException(mErrorString);
173 nativeGetErrorString(mUrlRequestAdapter));
174 case ChromiumUrlRequestError.MALFORMED_URL: 186 case ChromiumUrlRequestError.MALFORMED_URL:
175 return new MalformedURLException("Malformed URL: " + mUrl); 187 return new MalformedURLException("Malformed URL: " + mUrl);
176 case ChromiumUrlRequestError.CONNECTION_TIMED_OUT: 188 case ChromiumUrlRequestError.CONNECTION_TIMED_OUT:
177 return new ConnectTimeoutException("Connection timed out"); 189 return new ConnectTimeoutException("Connection timed out");
178 case ChromiumUrlRequestError.UNKNOWN_HOST: 190 case ChromiumUrlRequestError.UNKNOWN_HOST:
179 String host; 191 String host;
180 try { 192 try {
181 host = new URL(mUrl).getHost(); 193 host = new URL(mUrl).getHost();
182 } catch (MalformedURLException e) { 194 } catch (MalformedURLException e) {
183 host = mUrl; 195 host = mUrl;
184 } 196 }
185 return new UnknownHostException("Unknown host: " + host); 197 return new UnknownHostException("Unknown host: " + host);
186 case ChromiumUrlRequestError.TOO_MANY_REDIRECTS: 198 case ChromiumUrlRequestError.TOO_MANY_REDIRECTS:
187 return new IOException("Request failed because there were too " 199 return new IOException("Request failed because there were too "
188 + "many redirects or redirects have been disabled"); 200 + "many redirects or redirects have been disabled");
189 default: 201 default:
190 throw new IllegalStateException( 202 throw new IllegalStateException(
191 "Unrecognized error code: " + errorCode); 203 "Unrecognized error code: " + mErrorCode);
192 } 204 }
193 } 205 }
194 206
195 @Override 207 @Override
196 public ByteBuffer getByteBuffer() { 208 public ByteBuffer getByteBuffer() {
197 return ((ChunkedWritableByteChannel) getSink()).getByteBuffer(); 209 return ((ChunkedWritableByteChannel) getSink()).getByteBuffer();
198 } 210 }
199 211
200 @Override 212 @Override
201 public byte[] getResponseAsBytes() { 213 public byte[] getResponseAsBytes() {
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after
310 } 322 }
311 323
312 @Override 324 @Override
313 public void setHttpMethod(String method) { 325 public void setHttpMethod(String method) {
314 validateNotStarted(); 326 validateNotStarted();
315 mMethod = method; 327 mMethod = method;
316 } 328 }
317 329
318 @Override 330 @Override
319 public void disableRedirects() { 331 public void disableRedirects() {
320 mDisableRedirects = true; 332 synchronized (mLock) {
321 validateNotStarted(); 333 validateNotStarted();
322 nativeDisableRedirects(mUrlRequestAdapter); 334 validateNativeAdapterNotDestroyed();
335 mDisableRedirects = true;
336 nativeDisableRedirects(mUrlRequestAdapter);
337 }
323 } 338 }
324 339
325 public WritableByteChannel getSink() { 340 public WritableByteChannel getSink() {
326 return mSink; 341 return mSink;
327 } 342 }
328 343
329 @Override 344 @Override
330 public void start() { 345 public void start() {
331 synchronized (mLock) { 346 synchronized (mLock) {
332 if (mCanceled) { 347 if (mCanceled) {
333 return; 348 return;
334 } 349 }
335 350
336 validateNotStarted(); 351 validateNotStarted();
337 validateNotRecycled(); 352 validateNativeAdapterNotDestroyed();
338 353
339 mStarted = true; 354 mStarted = true;
340 355
341 if (mHeaders != null && !mHeaders.isEmpty()) { 356 if (mHeaders != null && !mHeaders.isEmpty()) {
342 for (Entry<String, String> entry : mHeaders.entrySet()) { 357 for (Entry<String, String> entry : mHeaders.entrySet()) {
343 nativeAddHeader(mUrlRequestAdapter, entry.getKey(), 358 nativeAddHeader(mUrlRequestAdapter, entry.getKey(),
344 entry.getValue()); 359 entry.getValue());
345 } 360 }
346 } 361 }
347 362
(...skipping 29 matching lines...) Expand all
377 392
378 @Override 393 @Override
379 public void cancel() { 394 public void cancel() {
380 synchronized (mLock) { 395 synchronized (mLock) {
381 if (mCanceled) { 396 if (mCanceled) {
382 return; 397 return;
383 } 398 }
384 399
385 mCanceled = true; 400 mCanceled = true;
386 401
387 if (!mRecycled) { 402 if (mUrlRequestAdapter != 0) {
388 nativeCancel(mUrlRequestAdapter); 403 nativeCancel(mUrlRequestAdapter);
389 } 404 }
390 } 405 }
391 } 406 }
392 407
393 @Override 408 @Override
394 public boolean isCanceled() { 409 public boolean isCanceled() {
395 synchronized (mLock) { 410 synchronized (mLock) {
396 return mCanceled; 411 return mCanceled;
397 } 412 }
398 } 413 }
399 414
400 public boolean isRecycled() {
401 synchronized (mLock) {
402 return mRecycled;
403 }
404 }
405
406 @Override 415 @Override
407 public String getNegotiatedProtocol() { 416 public String getNegotiatedProtocol() {
408 validateNotRecycled(); 417 synchronized (mLock) {
409 validateHeadersAvailable(); 418 validateNativeAdapterNotDestroyed();
410 return nativeGetNegotiatedProtocol(mUrlRequestAdapter); 419 validateHeadersAvailable();
420 return nativeGetNegotiatedProtocol(mUrlRequestAdapter);
421 }
411 } 422 }
412 423
413 @Override 424 @Override
414 public String getContentType() { 425 public String getContentType() {
415 return mContentType; 426 return mContentType;
416 } 427 }
417 428
418 @Override 429 @Override
419 public String getHeader(String name) { 430 public String getHeader(String name) {
420 validateNotRecycled(); 431 synchronized (mLock) {
421 validateHeadersAvailable(); 432 validateNativeAdapterNotDestroyed();
422 return nativeGetHeader(mUrlRequestAdapter, name); 433 validateHeadersAvailable();
434 return nativeGetHeader(mUrlRequestAdapter, name);
435 }
423 } 436 }
424 437
425 // All response headers. 438 // All response headers.
426 @Override 439 @Override
427 public Map<String, List<String>> getAllHeaders() { 440 public Map<String, List<String>> getAllHeaders() {
428 validateNotRecycled(); 441 synchronized (mLock) {
429 validateHeadersAvailable(); 442 validateNativeAdapterNotDestroyed();
430 ResponseHeadersMap result = new ResponseHeadersMap(); 443 validateHeadersAvailable();
431 nativeGetAllHeaders(mUrlRequestAdapter, result); 444 ResponseHeadersMap result = new ResponseHeadersMap();
432 return result; 445 nativeGetAllHeaders(mUrlRequestAdapter, result);
446 return result;
447 }
433 } 448 }
434 449
435 @Override 450 @Override
436 public String getUrl() { 451 public String getUrl() {
437 return mUrl; 452 return mUrl;
438 } 453 }
439 454
440 private static int convertRequestPriority(int priority) { 455 private static int convertRequestPriority(int priority) {
441 switch (priority) { 456 switch (priority) {
442 case HttpUrlRequest.REQUEST_PRIORITY_IDLE: 457 case HttpUrlRequest.REQUEST_PRIORITY_IDLE:
(...skipping 13 matching lines...) Expand all
456 471
457 private void onContentLengthOverLimit() { 472 private void onContentLengthOverLimit() {
458 mContentLengthOverLimit = true; 473 mContentLengthOverLimit = true;
459 cancel(); 474 cancel();
460 } 475 }
461 476
462 /** 477 /**
463 * A callback invoked when the response has been fully consumed. 478 * A callback invoked when the response has been fully consumed.
464 */ 479 */
465 private void onRequestComplete() { 480 private void onRequestComplete() {
481 mErrorCode = nativeGetErrorCode(mUrlRequestAdapter);
482 mErrorString = nativeGetErrorString(mUrlRequestAdapter);
483 // When there is an error or redirects have been disabled,
484 // onResponseStarted is often not invoked.
485 // Populate status code and status text if that's the case.
486 // Note that besides redirects, these two fields may be set on the
487 // request for AUTH and CERT requests.
488 if (mErrorCode != ChromiumUrlRequestError.SUCCESS) {
489 mHttpStatusCode = nativeGetHttpStatusCode(mUrlRequestAdapter);
490 mHttpStatusText = nativeGetHttpStatusText(mUrlRequestAdapter);
491 }
466 mListener.onRequestComplete(this); 492 mListener.onRequestComplete(this);
467 } 493 }
468 494
469 private void validateNotRecycled() { 495 private void validateNativeAdapterNotDestroyed() {
470 if (mRecycled) { 496 if (mUrlRequestAdapter == 0) {
471 throw new IllegalStateException("Accessing recycled request"); 497 throw new IllegalStateException("Adapter has been destroyed");
472 } 498 }
473 } 499 }
474 500
475 private void validateNotStarted() { 501 private void validateNotStarted() {
476 if (mStarted) { 502 if (mStarted) {
477 throw new IllegalStateException("Request already started"); 503 throw new IllegalStateException("Request already started");
478 } 504 }
479 } 505 }
480 506
481 private void validateHeadersAvailable() { 507 private void validateHeadersAvailable() {
(...skipping 26 matching lines...) Expand all
508 "Exception trying to cancel request", cancel_exception); 534 "Exception trying to cancel request", cancel_exception);
509 } 535 }
510 } 536 }
511 537
512 /** 538 /**
513 * A callback invoked when the first chunk of the response has arrived. 539 * A callback invoked when the first chunk of the response has arrived.
514 */ 540 */
515 @CalledByNative 541 @CalledByNative
516 private void onResponseStarted() { 542 private void onResponseStarted() {
517 try { 543 try {
544 mHttpStatusCode = nativeGetHttpStatusCode(mUrlRequestAdapter);
545 mHttpStatusText = nativeGetHttpStatusText(mUrlRequestAdapter);
518 mContentType = nativeGetContentType(mUrlRequestAdapter); 546 mContentType = nativeGetContentType(mUrlRequestAdapter);
519 mContentLength = nativeGetContentLength(mUrlRequestAdapter); 547 mContentLength = nativeGetContentLength(mUrlRequestAdapter);
520 mHeadersAvailable = true; 548 mHeadersAvailable = true;
521 549
522 if (mContentLengthLimit > 0 550 if (mContentLengthLimit > 0
523 && mContentLength > mContentLengthLimit 551 && mContentLength > mContentLengthLimit
524 && mCancelIfContentLengthOverLimit) { 552 && mCancelIfContentLengthOverLimit) {
525 onContentLengthOverLimit(); 553 onContentLengthOverLimit();
526 return; 554 return;
527 } 555 }
528 556
529 if (mBufferFullResponse && mContentLength != -1 557 if (mBufferFullResponse && mContentLength != -1
530 && !mContentLengthOverLimit) { 558 && !mContentLengthOverLimit) {
531 ((ChunkedWritableByteChannel) getSink()).setCapacity( 559 ((ChunkedWritableByteChannel) getSink()).setCapacity(
532 (int) mContentLength); 560 (int) mContentLength);
533 } 561 }
534 562
535 if (mOffset != 0) { 563 if (mOffset != 0) {
536 // The server may ignore the request for a byte range, in which 564 // The server may ignore the request for a byte range, in which
537 // case status code will be 200, instead of 206. Note that we 565 // case status code will be 200, instead of 206. Note that we
538 // cannot call getHttpStatusCode as it rewrites 206 into 200. 566 // cannot call getHttpStatusCode as it rewrites 206 into 200.
539 if (nativeGetHttpStatusCode(mUrlRequestAdapter) == 200) { 567 if (mHttpStatusCode == 200) {
540 // TODO(mef): Revisit this logic. 568 // TODO(mef): Revisit this logic.
541 if (mContentLength != -1) { 569 if (mContentLength != -1) {
542 mContentLength -= mOffset; 570 mContentLength -= mOffset;
543 } 571 }
544 mSkippingToOffset = true; 572 mSkippingToOffset = true;
545 } else { 573 } else {
546 mSize = mOffset; 574 mSize = mOffset;
547 } 575 }
548 } 576 }
549 mListener.onResponseStarted(this); 577 mListener.onResponseStarted(this);
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
600 @SuppressWarnings("unused") 628 @SuppressWarnings("unused")
601 @CalledByNative 629 @CalledByNative
602 private void finish() { 630 private void finish() {
603 try { 631 try {
604 synchronized (mLock) { 632 synchronized (mLock) {
605 if (mDisableRedirects) { 633 if (mDisableRedirects) {
606 mHeadersAvailable = true; 634 mHeadersAvailable = true;
607 } 635 }
608 mFinished = true; 636 mFinished = true;
609 637
610 if (mRecycled) { 638 if (mUrlRequestAdapter == 0) {
611 return; 639 return;
612 } 640 }
613 try { 641 try {
614 mSink.close(); 642 mSink.close();
615 } catch (IOException e) { 643 } catch (IOException e) {
616 // Ignore 644 // Ignore
617 } 645 }
618 try { 646 try {
619 if (mUploadChannel != null && mUploadChannel.isOpen()) { 647 if (mUploadChannel != null && mUploadChannel.isOpen()) {
620 mUploadChannel.close(); 648 mUploadChannel.close();
621 } 649 }
622 } catch (IOException e) { 650 } catch (IOException e) {
623 // Ignore 651 // Ignore
624 } 652 }
625 onRequestComplete(); 653 onRequestComplete();
626 nativeDestroyRequestAdapter(mUrlRequestAdapter); 654 nativeDestroyRequestAdapter(mUrlRequestAdapter);
627 mUrlRequestAdapter = 0; 655 mUrlRequestAdapter = 0;
628 mRecycled = true;
629 } 656 }
630 } catch (Exception e) { 657 } catch (Exception e) {
631 mSinkException = new IOException("Exception in finish", e); 658 mSinkException = new IOException("Exception in finish", e);
632 } 659 }
633 } 660 }
634 661
635 /** 662 /**
636 * Appends header |name| with value |value| to |headersMap|. 663 * Appends header |name| with value |value| to |headersMap|.
637 */ 664 */
638 @SuppressWarnings("unused") 665 @SuppressWarnings("unused")
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
720 private native void nativeGetAllHeaders(long urlRequestAdapter, 747 private native void nativeGetAllHeaders(long urlRequestAdapter,
721 ResponseHeadersMap headers); 748 ResponseHeadersMap headers);
722 749
723 private native String nativeGetNegotiatedProtocol(long urlRequestAdapter); 750 private native String nativeGetNegotiatedProtocol(long urlRequestAdapter);
724 751
725 // Explicit class to work around JNI-generator generics confusion. 752 // Explicit class to work around JNI-generator generics confusion.
726 private static class ResponseHeadersMap extends 753 private static class ResponseHeadersMap extends
727 HashMap<String, List<String>> { 754 HashMap<String, List<String>> {
728 } 755 }
729 } 756 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698