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. |
| 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); |
| 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 |