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

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: removed mRecycled and used lock when calling native methods on the adapter object 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 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
327 } 339 }
328 340
329 @Override 341 @Override
330 public void start() { 342 public void start() {
331 synchronized (mLock) { 343 synchronized (mLock) {
332 if (mCanceled) { 344 if (mCanceled) {
333 return; 345 return;
334 } 346 }
335 347
336 validateNotStarted(); 348 validateNotStarted();
337 validateNotRecycled(); 349 validateNativeAdapterNotDestroyed();
338 350
339 mStarted = true; 351 mStarted = true;
340 352
341 if (mHeaders != null && !mHeaders.isEmpty()) { 353 if (mHeaders != null && !mHeaders.isEmpty()) {
342 for (Entry<String, String> entry : mHeaders.entrySet()) { 354 for (Entry<String, String> entry : mHeaders.entrySet()) {
343 nativeAddHeader(mUrlRequestAdapter, entry.getKey(), 355 nativeAddHeader(mUrlRequestAdapter, entry.getKey(),
344 entry.getValue()); 356 entry.getValue());
345 } 357 }
346 } 358 }
347 359
(...skipping 29 matching lines...) Expand all
377 389
378 @Override 390 @Override
379 public void cancel() { 391 public void cancel() {
380 synchronized (mLock) { 392 synchronized (mLock) {
381 if (mCanceled) { 393 if (mCanceled) {
382 return; 394 return;
383 } 395 }
384 396
385 mCanceled = true; 397 mCanceled = true;
386 398
387 if (!mRecycled) { 399 if (mUrlRequestAdapter != 0) {
388 nativeCancel(mUrlRequestAdapter); 400 nativeCancel(mUrlRequestAdapter);
389 } 401 }
390 } 402 }
391 } 403 }
392 404
393 @Override 405 @Override
394 public boolean isCanceled() { 406 public boolean isCanceled() {
395 synchronized (mLock) { 407 synchronized (mLock) {
396 return mCanceled; 408 return mCanceled;
397 } 409 }
398 } 410 }
399 411
400 public boolean isRecycled() {
401 synchronized (mLock) {
402 return mRecycled;
403 }
404 }
405
406 @Override 412 @Override
407 public String getNegotiatedProtocol() { 413 public String getNegotiatedProtocol() {
408 validateNotRecycled(); 414 synchronized (mLock) {
xunjieli 2015/03/03 22:53:12 I realize there can be a race when we call get* me
409 validateHeadersAvailable(); 415 validateNativeAdapterNotDestroyed();
410 return nativeGetNegotiatedProtocol(mUrlRequestAdapter); 416 validateHeadersAvailable();
417 return nativeGetNegotiatedProtocol(mUrlRequestAdapter);
418 }
411 } 419 }
412 420
413 @Override 421 @Override
414 public String getContentType() { 422 public String getContentType() {
415 return mContentType; 423 return mContentType;
416 } 424 }
417 425
418 @Override 426 @Override
419 public String getHeader(String name) { 427 public String getHeader(String name) {
420 validateNotRecycled(); 428 synchronized (mLock) {
421 validateHeadersAvailable(); 429 validateNativeAdapterNotDestroyed();
422 return nativeGetHeader(mUrlRequestAdapter, name); 430 validateHeadersAvailable();
431 return nativeGetHeader(mUrlRequestAdapter, name);
432 }
423 } 433 }
424 434
425 // All response headers. 435 // All response headers.
426 @Override 436 @Override
427 public Map<String, List<String>> getAllHeaders() { 437 public Map<String, List<String>> getAllHeaders() {
428 validateNotRecycled(); 438 synchronized (mLock) {
429 validateHeadersAvailable(); 439 validateNativeAdapterNotDestroyed();
430 ResponseHeadersMap result = new ResponseHeadersMap(); 440 validateHeadersAvailable();
431 nativeGetAllHeaders(mUrlRequestAdapter, result); 441 ResponseHeadersMap result = new ResponseHeadersMap();
432 return result; 442 nativeGetAllHeaders(mUrlRequestAdapter, result);
443 return result;
444 }
433 } 445 }
434 446
435 @Override 447 @Override
436 public String getUrl() { 448 public String getUrl() {
437 return mUrl; 449 return mUrl;
438 } 450 }
439 451
440 private static int convertRequestPriority(int priority) { 452 private static int convertRequestPriority(int priority) {
441 switch (priority) { 453 switch (priority) {
442 case HttpUrlRequest.REQUEST_PRIORITY_IDLE: 454 case HttpUrlRequest.REQUEST_PRIORITY_IDLE:
(...skipping 13 matching lines...) Expand all
456 468
457 private void onContentLengthOverLimit() { 469 private void onContentLengthOverLimit() {
458 mContentLengthOverLimit = true; 470 mContentLengthOverLimit = true;
459 cancel(); 471 cancel();
460 } 472 }
461 473
462 /** 474 /**
463 * A callback invoked when the response has been fully consumed. 475 * A callback invoked when the response has been fully consumed.
464 */ 476 */
465 private void onRequestComplete() { 477 private void onRequestComplete() {
478 mErrorCode = nativeGetErrorCode(mUrlRequestAdapter);
479 mErrorString = nativeGetErrorString(mUrlRequestAdapter);
480 // When there is an error or redirects have been disabled,
481 // onResponseStarted is often not invoked.
482 // Populate status code and status text if that's the case.
483 // Note that besides redirects, these two fields may be set on the
484 // request for AUTH and CERT requests.
485 if (mErrorCode != ChromiumUrlRequestError.SUCCESS) {
486 mHttpStatusCode = nativeGetHttpStatusCode(mUrlRequestAdapter);
487 mHttpStatusText = nativeGetHttpStatusText(mUrlRequestAdapter);
488 }
466 mListener.onRequestComplete(this); 489 mListener.onRequestComplete(this);
467 } 490 }
468 491
469 private void validateNotRecycled() { 492 private void validateNativeAdapterNotDestroyed() {
470 if (mRecycled) { 493 if (mUrlRequestAdapter == 0) {
471 throw new IllegalStateException("Accessing recycled request"); 494 throw new IllegalStateException("Adapter has been destroyed");
472 } 495 }
473 } 496 }
474 497
475 private void validateNotStarted() { 498 private void validateNotStarted() {
476 if (mStarted) { 499 if (mStarted) {
477 throw new IllegalStateException("Request already started"); 500 throw new IllegalStateException("Request already started");
478 } 501 }
479 } 502 }
480 503
481 private void validateHeadersAvailable() { 504 private void validateHeadersAvailable() {
(...skipping 26 matching lines...) Expand all
508 "Exception trying to cancel request", cancel_exception); 531 "Exception trying to cancel request", cancel_exception);
509 } 532 }
510 } 533 }
511 534
512 /** 535 /**
513 * A callback invoked when the first chunk of the response has arrived. 536 * A callback invoked when the first chunk of the response has arrived.
514 */ 537 */
515 @CalledByNative 538 @CalledByNative
516 private void onResponseStarted() { 539 private void onResponseStarted() {
517 try { 540 try {
541 mHttpStatusCode = nativeGetHttpStatusCode(mUrlRequestAdapter);
542 mHttpStatusText = nativeGetHttpStatusText(mUrlRequestAdapter);
518 mContentType = nativeGetContentType(mUrlRequestAdapter); 543 mContentType = nativeGetContentType(mUrlRequestAdapter);
519 mContentLength = nativeGetContentLength(mUrlRequestAdapter); 544 mContentLength = nativeGetContentLength(mUrlRequestAdapter);
520 mHeadersAvailable = true; 545 mHeadersAvailable = true;
521 546
522 if (mContentLengthLimit > 0 547 if (mContentLengthLimit > 0
523 && mContentLength > mContentLengthLimit 548 && mContentLength > mContentLengthLimit
524 && mCancelIfContentLengthOverLimit) { 549 && mCancelIfContentLengthOverLimit) {
525 onContentLengthOverLimit(); 550 onContentLengthOverLimit();
526 return; 551 return;
527 } 552 }
528 553
529 if (mBufferFullResponse && mContentLength != -1 554 if (mBufferFullResponse && mContentLength != -1
530 && !mContentLengthOverLimit) { 555 && !mContentLengthOverLimit) {
531 ((ChunkedWritableByteChannel) getSink()).setCapacity( 556 ((ChunkedWritableByteChannel) getSink()).setCapacity(
532 (int) mContentLength); 557 (int) mContentLength);
533 } 558 }
534 559
535 if (mOffset != 0) { 560 if (mOffset != 0) {
536 // The server may ignore the request for a byte range, in which 561 // 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 562 // case status code will be 200, instead of 206. Note that we
538 // cannot call getHttpStatusCode as it rewrites 206 into 200. 563 // cannot call getHttpStatusCode as it rewrites 206 into 200.
539 if (nativeGetHttpStatusCode(mUrlRequestAdapter) == 200) { 564 if (mHttpStatusCode == 200) {
540 // TODO(mef): Revisit this logic. 565 // TODO(mef): Revisit this logic.
541 if (mContentLength != -1) { 566 if (mContentLength != -1) {
542 mContentLength -= mOffset; 567 mContentLength -= mOffset;
543 } 568 }
544 mSkippingToOffset = true; 569 mSkippingToOffset = true;
545 } else { 570 } else {
546 mSize = mOffset; 571 mSize = mOffset;
547 } 572 }
548 } 573 }
549 mListener.onResponseStarted(this); 574 mListener.onResponseStarted(this);
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
600 @SuppressWarnings("unused") 625 @SuppressWarnings("unused")
601 @CalledByNative 626 @CalledByNative
602 private void finish() { 627 private void finish() {
603 try { 628 try {
604 synchronized (mLock) { 629 synchronized (mLock) {
605 if (mDisableRedirects) { 630 if (mDisableRedirects) {
606 mHeadersAvailable = true; 631 mHeadersAvailable = true;
607 } 632 }
608 mFinished = true; 633 mFinished = true;
609 634
610 if (mRecycled) { 635 if (mUrlRequestAdapter == 0) {
611 return; 636 return;
612 } 637 }
613 try { 638 try {
614 mSink.close(); 639 mSink.close();
615 } catch (IOException e) { 640 } catch (IOException e) {
616 // Ignore 641 // Ignore
617 } 642 }
618 try { 643 try {
619 if (mUploadChannel != null && mUploadChannel.isOpen()) { 644 if (mUploadChannel != null && mUploadChannel.isOpen()) {
620 mUploadChannel.close(); 645 mUploadChannel.close();
621 } 646 }
622 } catch (IOException e) { 647 } catch (IOException e) {
623 // Ignore 648 // Ignore
624 } 649 }
625 onRequestComplete(); 650 onRequestComplete();
626 nativeDestroyRequestAdapter(mUrlRequestAdapter); 651 nativeDestroyRequestAdapter(mUrlRequestAdapter);
627 mUrlRequestAdapter = 0; 652 mUrlRequestAdapter = 0;
628 mRecycled = true;
629 } 653 }
630 } catch (Exception e) { 654 } catch (Exception e) {
631 mSinkException = new IOException("Exception in finish", e); 655 mSinkException = new IOException("Exception in finish", e);
632 } 656 }
633 } 657 }
634 658
635 /** 659 /**
636 * Appends header |name| with value |value| to |headersMap|. 660 * Appends header |name| with value |value| to |headersMap|.
637 */ 661 */
638 @SuppressWarnings("unused") 662 @SuppressWarnings("unused")
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
720 private native void nativeGetAllHeaders(long urlRequestAdapter, 744 private native void nativeGetAllHeaders(long urlRequestAdapter,
721 ResponseHeadersMap headers); 745 ResponseHeadersMap headers);
722 746
723 private native String nativeGetNegotiatedProtocol(long urlRequestAdapter); 747 private native String nativeGetNegotiatedProtocol(long urlRequestAdapter);
724 748
725 // Explicit class to work around JNI-generator generics confusion. 749 // Explicit class to work around JNI-generator generics confusion.
726 private static class ResponseHeadersMap extends 750 private static class ResponseHeadersMap extends
727 HashMap<String, List<String>> { 751 HashMap<String, List<String>> {
728 } 752 }
729 } 753 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698