Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 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; | 50 private volatile boolean mRecycled; |
| 51 private volatile boolean mFinished; | 51 private volatile boolean mFinished; |
| 52 private boolean mHeadersAvailable; | 52 private boolean mHeadersAvailable; |
| 53 private String mContentType; | |
| 54 private long mUploadContentLength; | 53 private long mUploadContentLength; |
| 55 private final HttpUrlRequestListener mListener; | 54 private final HttpUrlRequestListener mListener; |
| 56 private boolean mBufferFullResponse; | 55 private boolean mBufferFullResponse; |
| 57 private long mOffset; | 56 private long mOffset; |
| 58 private long mContentLength; | |
| 59 private long mContentLengthLimit; | 57 private long mContentLengthLimit; |
| 60 private boolean mCancelIfContentLengthOverLimit; | 58 private boolean mCancelIfContentLengthOverLimit; |
| 61 private boolean mContentLengthOverLimit; | 59 private boolean mContentLengthOverLimit; |
| 62 private boolean mSkippingToOffset; | 60 private boolean mSkippingToOffset; |
| 63 private long mSize; | 61 private long mSize; |
| 62 | |
| 64 // Indicates whether redirects have been disabled. | 63 // Indicates whether redirects have been disabled. |
| 65 private boolean mDisableRedirects; | 64 private boolean mDisableRedirects; |
| 65 | |
| 66 // Http status code. Default to 0. Populated in onResponseStarted(). | |
| 67 private int mHttpStatusCode = 0; | |
| 68 | |
| 69 // Http status text. Default to null. Populated in onResponseStarted(). | |
| 70 private String mHttpStatusText; | |
| 71 | |
| 72 // Content type. Default to null. Populated in onResponseStarted(). | |
| 73 private String mContentType; | |
| 74 | |
| 75 // Compressed content length as reported by the server. Populated in onRespo nseStarted(). | |
| 76 private long mContentLength; | |
| 77 | |
| 78 // Native error code. Default to no error. Populated in onRequestComplete(). | |
| 79 private int mErrorCode = ChromiumUrlRequestError.SUCCESS; | |
| 80 | |
| 81 // Native error string. Default to null. Populated in onRequestComplete(). | |
| 82 private String mErrorString; | |
| 83 | |
| 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 Loading... | |
| 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 254 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 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. | |
|
mmenke
2015/03/02 19:22:50
Worth noting that other than the redirect case, th
xunjieli
2015/03/02 22:33:57
Done.
| |
| 482 // Populate status code and status text if that's the case. | |
| 483 if (mErrorCode != ChromiumUrlRequestError.SUCCESS) { | |
| 484 mHttpStatusCode = nativeGetHttpStatusCode(mUrlRequestAdapter); | |
| 485 mHttpStatusText = nativeGetHttpStatusText(mUrlRequestAdapter); | |
|
mmenke
2015/03/02 19:22:50
Hrm...If we were going to maintain this interface,
xunjieli
2015/03/02 19:36:15
Not sure what this means. HttpURLConnection isn't
mmenke
2015/03/02 20:41:58
mHttpStatusCode has a default value of 0. If we d
xunjieli
2015/03/02 22:33:56
I am not sure whether it is observable elsewhere,
| |
| 486 } | |
| 466 mListener.onRequestComplete(this); | 487 mListener.onRequestComplete(this); |
| 467 } | 488 } |
| 468 | 489 |
| 469 private void validateNotRecycled() { | 490 private void validateNotRecycled() { |
| 470 if (mRecycled) { | 491 if (mRecycled) { |
| 471 throw new IllegalStateException("Accessing recycled request"); | 492 throw new IllegalStateException("Accessing recycled request"); |
| 472 } | 493 } |
| 473 } | 494 } |
| 474 | 495 |
| 475 private void validateNotStarted() { | 496 private void validateNotStarted() { |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 508 "Exception trying to cancel request", cancel_exception); | 529 "Exception trying to cancel request", cancel_exception); |
| 509 } | 530 } |
| 510 } | 531 } |
| 511 | 532 |
| 512 /** | 533 /** |
| 513 * A callback invoked when the first chunk of the response has arrived. | 534 * A callback invoked when the first chunk of the response has arrived. |
| 514 */ | 535 */ |
| 515 @CalledByNative | 536 @CalledByNative |
| 516 private void onResponseStarted() { | 537 private void onResponseStarted() { |
| 517 try { | 538 try { |
| 539 mHttpStatusCode = nativeGetHttpStatusCode(mUrlRequestAdapter); | |
| 540 mHttpStatusText = nativeGetHttpStatusText(mUrlRequestAdapter); | |
| 518 mContentType = nativeGetContentType(mUrlRequestAdapter); | 541 mContentType = nativeGetContentType(mUrlRequestAdapter); |
| 519 mContentLength = nativeGetContentLength(mUrlRequestAdapter); | 542 mContentLength = nativeGetContentLength(mUrlRequestAdapter); |
| 520 mHeadersAvailable = true; | 543 mHeadersAvailable = true; |
| 521 | 544 |
| 522 if (mContentLengthLimit > 0 | 545 if (mContentLengthLimit > 0 |
| 523 && mContentLength > mContentLengthLimit | 546 && mContentLength > mContentLengthLimit |
| 524 && mCancelIfContentLengthOverLimit) { | 547 && mCancelIfContentLengthOverLimit) { |
| 525 onContentLengthOverLimit(); | 548 onContentLengthOverLimit(); |
| 526 return; | 549 return; |
| 527 } | 550 } |
| 528 | 551 |
| 529 if (mBufferFullResponse && mContentLength != -1 | 552 if (mBufferFullResponse && mContentLength != -1 |
| 530 && !mContentLengthOverLimit) { | 553 && !mContentLengthOverLimit) { |
| 531 ((ChunkedWritableByteChannel) getSink()).setCapacity( | 554 ((ChunkedWritableByteChannel) getSink()).setCapacity( |
| 532 (int) mContentLength); | 555 (int) mContentLength); |
| 533 } | 556 } |
| 534 | 557 |
| 535 if (mOffset != 0) { | 558 if (mOffset != 0) { |
| 536 // The server may ignore the request for a byte range, in which | 559 // 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 | 560 // case status code will be 200, instead of 206. Note that we |
| 538 // cannot call getHttpStatusCode as it rewrites 206 into 200. | 561 // cannot call getHttpStatusCode as it rewrites 206 into 200. |
| 539 if (nativeGetHttpStatusCode(mUrlRequestAdapter) == 200) { | 562 if (mHttpStatusCode == 200) { |
| 540 // TODO(mef): Revisit this logic. | 563 // TODO(mef): Revisit this logic. |
| 541 if (mContentLength != -1) { | 564 if (mContentLength != -1) { |
| 542 mContentLength -= mOffset; | 565 mContentLength -= mOffset; |
| 543 } | 566 } |
| 544 mSkippingToOffset = true; | 567 mSkippingToOffset = true; |
| 545 } else { | 568 } else { |
| 546 mSize = mOffset; | 569 mSize = mOffset; |
| 547 } | 570 } |
| 548 } | 571 } |
| 549 mListener.onResponseStarted(this); | 572 mListener.onResponseStarted(this); |
| (...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 720 private native void nativeGetAllHeaders(long urlRequestAdapter, | 743 private native void nativeGetAllHeaders(long urlRequestAdapter, |
| 721 ResponseHeadersMap headers); | 744 ResponseHeadersMap headers); |
| 722 | 745 |
| 723 private native String nativeGetNegotiatedProtocol(long urlRequestAdapter); | 746 private native String nativeGetNegotiatedProtocol(long urlRequestAdapter); |
| 724 | 747 |
| 725 // Explicit class to work around JNI-generator generics confusion. | 748 // Explicit class to work around JNI-generator generics confusion. |
| 726 private static class ResponseHeadersMap extends | 749 private static class ResponseHeadersMap extends |
| 727 HashMap<String, List<String>> { | 750 HashMap<String, List<String>> { |
| 728 } | 751 } |
| 729 } | 752 } |
| OLD | NEW |