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

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

Issue 2339223002: Cronet API Refactoring (Closed)
Patch Set: Moved CronetSampleApp to AppCompat Created 4 years, 2 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.content.Context;
8 import android.text.TextUtils;
9
10 import java.io.FileNotFoundException;
11 import java.io.IOException;
12 import java.io.InputStream;
13 import java.io.OutputStream;
14 import java.net.HttpURLConnection;
15 import java.net.ProtocolException;
16 import java.net.URL;
17 import java.nio.ByteBuffer;
18 import java.nio.channels.ReadableByteChannel;
19 import java.nio.channels.WritableByteChannel;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Map.Entry;
23 import java.util.concurrent.ExecutorService;
24 import java.util.concurrent.Executors;
25 import java.util.concurrent.ThreadFactory;
26 import java.util.concurrent.atomic.AtomicInteger;
27 import java.util.zip.GZIPInputStream;
28
29 /**
30 * Network request using the HttpUrlConnection implementation.
31 * @deprecated Use {@link UrlRequest} instead.
32 * {@hide as it's deprecated}
33 */
34 @Deprecated
35 class HttpUrlConnectionUrlRequest implements HttpUrlRequest {
36
37 private static final int MAX_CHUNK_SIZE = 8192;
38
39 private static final int CONNECT_TIMEOUT = 3000;
40
41 private static final int READ_TIMEOUT = 90000;
42
43 private final Context mContext;
44
45 private final String mDefaultUserAgent;
46
47 private final String mUrl;
48
49 private final Map<String, String> mHeaders;
50
51 private final WritableByteChannel mSink;
52
53 private final HttpUrlRequestListener mListener;
54
55 private IOException mException;
56
57 private HttpURLConnection mConnection;
58
59 private long mOffset;
60
61 private int mContentLength;
62
63 private int mUploadContentLength;
64
65 private long mContentLengthLimit;
66
67 private boolean mCancelIfContentLengthOverLimit;
68
69 private boolean mContentLengthOverLimit;
70
71 private boolean mSkippingToOffset;
72
73 private long mSize;
74
75 private String mPostContentType;
76
77 private byte[] mPostData;
78
79 private ReadableByteChannel mPostDataChannel;
80
81 private String mContentType;
82
83 private int mHttpStatusCode;
84
85 private String mHttpStatusText;
86
87 private boolean mStarted;
88
89 private boolean mCanceled;
90
91 private String mMethod;
92
93 private InputStream mResponseStream;
94
95 private final Object mLock;
96
97 private static ExecutorService sExecutorService;
98
99 private static final Object sExecutorServiceLock = new Object();
100
101 HttpUrlConnectionUrlRequest(Context context, String defaultUserAgent,
102 String url, int requestPriority, Map<String, String> headers,
103 HttpUrlRequestListener listener) {
104 this(context, defaultUserAgent, url, requestPriority, headers,
105 new ChunkedWritableByteChannel(), listener);
106 }
107
108 HttpUrlConnectionUrlRequest(Context context, String defaultUserAgent,
109 String url, int requestPriority, Map<String, String> headers,
110 WritableByteChannel sink, HttpUrlRequestListener listener) {
111 if (context == null) {
112 throw new NullPointerException("Context is required");
113 }
114 if (url == null) {
115 throw new NullPointerException("URL is required");
116 }
117 mContext = context;
118 mDefaultUserAgent = defaultUserAgent;
119 mUrl = url;
120 mHeaders = headers;
121 mSink = sink;
122 mListener = listener;
123 mLock = new Object();
124 }
125
126 private static ExecutorService getExecutor() {
127 synchronized (sExecutorServiceLock) {
128 if (sExecutorService == null) {
129 ThreadFactory threadFactory = new ThreadFactory() {
130 private final AtomicInteger mCount = new AtomicInteger(1);
131
132 @Override
133 public Thread newThread(Runnable r) {
134 Thread thread = new Thread(r,
135 "HttpUrlConnection #"
136 + mCount.getAndIncrement());
137 // Note that this thread is not doing actual networking.
138 // It's only a controller.
139 thread.setPriority(Thread.NORM_PRIORITY);
140 return thread;
141 }
142 };
143 sExecutorService = Executors.newCachedThreadPool(threadFactory);
144 }
145 return sExecutorService;
146 }
147 }
148
149 @Override
150 public String getUrl() {
151 return mUrl;
152 }
153
154 @Override
155 public void setOffset(long offset) {
156 mOffset = offset;
157 }
158
159 @Override
160 public void setContentLengthLimit(long limit, boolean cancelEarly) {
161 mContentLengthLimit = limit;
162 mCancelIfContentLengthOverLimit = cancelEarly;
163 }
164
165 @Override
166 public void setUploadData(String contentType, byte[] data) {
167 validateNotStarted();
168 mPostContentType = contentType;
169 mPostData = data;
170 mPostDataChannel = null;
171 }
172
173 @Override
174 public void setUploadChannel(String contentType,
175 ReadableByteChannel channel, long contentLength) {
176 validateNotStarted();
177 if (contentLength > Integer.MAX_VALUE) {
178 throw new IllegalArgumentException(
179 "Upload contentLength is too big.");
180 }
181 mUploadContentLength = (int) contentLength;
182 mPostContentType = contentType;
183 mPostDataChannel = channel;
184 mPostData = null;
185 }
186
187
188 @Override
189 public void setHttpMethod(String method) {
190 validateNotStarted();
191 mMethod = method;
192 }
193
194 @Override
195 public void disableRedirects() {
196 validateNotStarted();
197 HttpURLConnection.setFollowRedirects(false);
198 }
199
200 @Override
201 public void start() {
202 getExecutor().execute(new Runnable() {
203 @Override
204 public void run() {
205 startOnExecutorThread();
206 }
207 });
208 }
209
210 private void startOnExecutorThread() {
211 boolean readingResponse = false;
212 try {
213 synchronized (mLock) {
214 if (mCanceled) {
215 return;
216 }
217 }
218
219 URL url = new URL(mUrl);
220 mConnection = (HttpURLConnection) url.openConnection();
221 // If configured, use the provided http verb.
222 if (mMethod != null) {
223 try {
224 mConnection.setRequestMethod(mMethod);
225 } catch (ProtocolException e) {
226 // Since request hasn't started earlier, it
227 // must be an illegal HTTP verb.
228 throw new IllegalArgumentException(e);
229 }
230 }
231 mConnection.setConnectTimeout(CONNECT_TIMEOUT);
232 mConnection.setReadTimeout(READ_TIMEOUT);
233 mConnection.setInstanceFollowRedirects(true);
234 if (mHeaders != null) {
235 for (Entry<String, String> header : mHeaders.entrySet()) {
236 mConnection.setRequestProperty(header.getKey(),
237 header.getValue());
238 }
239 }
240
241 if (mOffset != 0) {
242 mConnection.setRequestProperty("Range",
243 "bytes=" + mOffset + "-");
244 }
245
246 if (mConnection.getRequestProperty("User-Agent") == null) {
247 mConnection.setRequestProperty("User-Agent", mDefaultUserAgent);
248 }
249
250 if (mPostData != null || mPostDataChannel != null) {
251 uploadData();
252 }
253
254 InputStream stream = null;
255 try {
256 // We need to open the stream before asking for the response
257 // code.
258 stream = mConnection.getInputStream();
259 } catch (FileNotFoundException ex) {
260 // Ignore - the response has no body.
261 }
262
263 mHttpStatusCode = mConnection.getResponseCode();
264 mHttpStatusText = mConnection.getResponseMessage();
265 mContentType = mConnection.getContentType();
266 mContentLength = mConnection.getContentLength();
267 if (mContentLengthLimit > 0 && mContentLength > mContentLengthLimit
268 && mCancelIfContentLengthOverLimit) {
269 onContentLengthOverLimit();
270 return;
271 }
272
273 mListener.onResponseStarted(this);
274
275 mResponseStream = isError(mHttpStatusCode) ? mConnection
276 .getErrorStream()
277 : stream;
278
279 if (mResponseStream != null
280 && "gzip".equals(mConnection.getContentEncoding())) {
281 mResponseStream = new GZIPInputStream(mResponseStream);
282 mContentLength = -1;
283 }
284
285 if (mOffset != 0) {
286 // The server may ignore the request for a byte range.
287 if (mHttpStatusCode == HttpURLConnection.HTTP_OK) {
288 if (mContentLength != -1) {
289 mContentLength -= mOffset;
290 }
291 mSkippingToOffset = true;
292 } else {
293 mSize = mOffset;
294 }
295 }
296
297 if (mResponseStream != null) {
298 readingResponse = true;
299 readResponseAsync();
300 }
301 } catch (IOException e) {
302 mException = e;
303 } finally {
304 if (mPostDataChannel != null) {
305 try {
306 mPostDataChannel.close();
307 } catch (IOException e) {
308 // Ignore
309 }
310 }
311
312 // Don't call onRequestComplete yet if we are reading the response
313 // on a separate thread
314 if (!readingResponse) {
315 mListener.onRequestComplete(this);
316 }
317 }
318 }
319
320 private void uploadData() throws IOException {
321 mConnection.setDoOutput(true);
322 if (!TextUtils.isEmpty(mPostContentType)) {
323 mConnection.setRequestProperty("Content-Type", mPostContentType);
324 }
325
326 OutputStream uploadStream = null;
327 try {
328 if (mPostData != null) {
329 mConnection.setFixedLengthStreamingMode(mPostData.length);
330 uploadStream = mConnection.getOutputStream();
331 uploadStream.write(mPostData);
332 } else {
333 mConnection.setFixedLengthStreamingMode(mUploadContentLength);
334 uploadStream = mConnection.getOutputStream();
335 byte[] bytes = new byte[MAX_CHUNK_SIZE];
336 ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
337 while (mPostDataChannel.read(byteBuffer) > 0) {
338 byteBuffer.flip();
339 uploadStream.write(bytes, 0, byteBuffer.limit());
340 byteBuffer.clear();
341 }
342 }
343 } finally {
344 if (uploadStream != null) {
345 uploadStream.close();
346 }
347 }
348 }
349
350 private void readResponseAsync() {
351 getExecutor().execute(new Runnable() {
352 @Override
353 public void run() {
354 readResponse();
355 }
356 });
357 }
358
359 private void readResponse() {
360 try {
361 if (mResponseStream != null) {
362 readResponseStream();
363 }
364 } catch (IOException e) {
365 mException = e;
366 } finally {
367 try {
368 mConnection.disconnect();
369 } catch (ArrayIndexOutOfBoundsException t) {
370 // Ignore it.
371 }
372
373 try {
374 mSink.close();
375 } catch (IOException e) {
376 if (mException == null) {
377 mException = e;
378 }
379 }
380 }
381 mListener.onRequestComplete(this);
382 }
383
384 private void readResponseStream() throws IOException {
385 byte[] buffer = new byte[MAX_CHUNK_SIZE];
386 int size;
387 while (!isCanceled() && (size = mResponseStream.read(buffer)) != -1) {
388 int start = 0;
389 int count = size;
390 mSize += size;
391 if (mSkippingToOffset) {
392 if (mSize <= mOffset) {
393 continue;
394 } else {
395 mSkippingToOffset = false;
396 start = (int) (mOffset - (mSize - size));
397 count -= start;
398 }
399 }
400
401 if (mContentLengthLimit != 0 && mSize > mContentLengthLimit) {
402 count -= (int) (mSize - mContentLengthLimit);
403 if (count > 0) {
404 mSink.write(ByteBuffer.wrap(buffer, start, count));
405 }
406 onContentLengthOverLimit();
407 return;
408 }
409
410 mSink.write(ByteBuffer.wrap(buffer, start, count));
411 }
412 }
413
414 @Override
415 public void cancel() {
416 synchronized (mLock) {
417 if (mCanceled) {
418 return;
419 }
420
421 mCanceled = true;
422 }
423 }
424
425 @Override
426 public boolean isCanceled() {
427 synchronized (mLock) {
428 return mCanceled;
429 }
430 }
431
432 @Override
433 public String getNegotiatedProtocol() {
434 return "";
435 }
436
437 @Override
438 public boolean wasCached() {
439 return false;
440 }
441
442 @Override
443 public int getHttpStatusCode() {
444 int httpStatusCode = mHttpStatusCode;
445
446 // If we have been able to successfully resume a previously interrupted
447 // download,
448 // the status code will be 206, not 200. Since the rest of the
449 // application is
450 // expecting 200 to indicate success, we need to fake it.
451 if (httpStatusCode == HttpURLConnection.HTTP_PARTIAL) {
452 httpStatusCode = HttpURLConnection.HTTP_OK;
453 }
454 return httpStatusCode;
455 }
456
457 @Override
458 public String getHttpStatusText() {
459 return mHttpStatusText;
460 }
461
462 @Override
463 public IOException getException() {
464 if (mException == null && mContentLengthOverLimit) {
465 mException = new ResponseTooLargeException();
466 }
467 return mException;
468 }
469
470 private void onContentLengthOverLimit() {
471 mContentLengthOverLimit = true;
472 cancel();
473 }
474
475 private static boolean isError(int statusCode) {
476 return (statusCode / 100) != 2;
477 }
478
479 /**
480 * Returns the response as a ByteBuffer.
481 */
482 @Override
483 public ByteBuffer getByteBuffer() {
484 return ((ChunkedWritableByteChannel) mSink).getByteBuffer();
485 }
486
487 @Override
488 public byte[] getResponseAsBytes() {
489 return ((ChunkedWritableByteChannel) mSink).getBytes();
490 }
491
492 @Override
493 public long getContentLength() {
494 return mContentLength;
495 }
496
497 @Override
498 public String getContentType() {
499 return mContentType;
500 }
501
502 @Override
503 public String getHeader(String name) {
504 if (mConnection == null) {
505 throw new IllegalStateException("Response headers not available");
506 }
507 Map<String, List<String>> headerFields = mConnection.getHeaderFields();
508 if (headerFields != null) {
509 List<String> headerValues = headerFields.get(name);
510 if (headerValues != null) {
511 return TextUtils.join(", ", headerValues);
512 }
513 }
514 return null;
515 }
516
517 @Override
518 public Map<String, List<String>> getAllHeaders() {
519 if (mConnection == null) {
520 throw new IllegalStateException("Response headers not available");
521 }
522 return mConnection.getHeaderFields();
523 }
524
525 private void validateNotStarted() {
526 if (mStarted) {
527 throw new IllegalStateException("Request already started");
528 }
529 }
530 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698