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

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

Issue 475533003: Upstream changes to enable chunked uploads. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 4 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 | Annotate | Revision Log
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;
11 import org.chromium.base.JNINamespace; 11 import org.chromium.base.JNINamespace;
12 12
13 import java.io.IOException; 13 import java.io.IOException;
14 import java.net.MalformedURLException; 14 import java.net.MalformedURLException;
15 import java.net.URL; 15 import java.net.URL;
16 import java.net.UnknownHostException; 16 import java.net.UnknownHostException;
17 import java.nio.ByteBuffer; 17 import java.nio.ByteBuffer;
18 import java.nio.channels.ReadableByteChannel; 18 import java.nio.channels.ReadableByteChannel;
19 import java.nio.channels.WritableByteChannel; 19 import java.nio.channels.WritableByteChannel;
20 import java.util.ArrayList; 20 import java.util.ArrayList;
21 import java.util.HashMap; 21 import java.util.HashMap;
22 import java.util.List; 22 import java.util.List;
23 import java.util.Map; 23 import java.util.Map;
24 import java.util.Map.Entry; 24 import java.util.Map.Entry;
25 import java.util.concurrent.Semaphore;
25 26
26 /** 27 /**
27 * Network request using the native http stack implementation. 28 * Network request using the native http stack implementation.
28 */ 29 */
29 @JNINamespace("cronet") 30 @JNINamespace("cronet")
30 public class ChromiumUrlRequest implements HttpUrlRequest { 31 public class ChromiumUrlRequest implements HttpUrlRequest {
31 /** 32 /**
32 * Native adapter object, owned by UrlRequest. 33 * Native adapter object, owned by UrlRequest.
33 */ 34 */
34 private long mUrlRequestAdapter; 35 private long mUrlRequestAdapter;
35 private final ChromiumUrlRequestContext mRequestContext; 36 private final ChromiumUrlRequestContext mRequestContext;
36 private final String mUrl; 37 private final String mUrl;
37 private final int mPriority; 38 private final int mPriority;
38 private final Map<String, String> mHeaders; 39 private final Map<String, String> mHeaders;
39 private final WritableByteChannel mSink; 40 private final WritableByteChannel mSink;
40 private Map<String, String> mAdditionalHeaders; 41 private Map<String, String> mAdditionalHeaders;
41 private String mUploadContentType; 42 private String mUploadContentType;
42 private String mMethod; 43 private String mMethod;
43 private byte[] mUploadData; 44 private byte[] mUploadData;
45 private Semaphore mAppendChunkSemaphore;
44 private ReadableByteChannel mUploadChannel; 46 private ReadableByteChannel mUploadChannel;
45 private WritableByteChannel mOutputChannel; 47 private WritableByteChannel mOutputChannel;
46 private IOException mSinkException; 48 private IOException mSinkException;
47 private volatile boolean mStarted; 49 private volatile boolean mStarted;
48 private volatile boolean mCanceled; 50 private volatile boolean mCanceled;
49 private volatile boolean mRecycled; 51 private volatile boolean mRecycled;
50 private volatile boolean mFinished; 52 private volatile boolean mFinished;
51 private boolean mHeadersAvailable; 53 private boolean mHeadersAvailable;
52 private String mContentType; 54 private String mContentType;
53 private long mUploadContentLength; 55 private long mUploadContentLength;
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after
213 * @param contentType MIME type of the upload content or null if this is not 215 * @param contentType MIME type of the upload content or null if this is not
214 * an upload. 216 * an upload.
215 * @param data The content that needs to be uploaded. 217 * @param data The content that needs to be uploaded.
216 */ 218 */
217 public void setUploadData(String contentType, byte[] data) { 219 public void setUploadData(String contentType, byte[] data) {
218 synchronized (mLock) { 220 synchronized (mLock) {
219 validateNotStarted(); 221 validateNotStarted();
220 mUploadContentType = contentType; 222 mUploadContentType = contentType;
221 mUploadData = data; 223 mUploadData = data;
222 mUploadChannel = null; 224 mUploadChannel = null;
225 mAppendChunkSemaphore = null;
223 } 226 }
224 } 227 }
225 228
226 /** 229 /**
227 * Sets a readable byte channel to upload as part of a POST or PUT request. 230 * Sets a readable byte channel to upload as part of a POST or PUT request.
228 * 231 *
229 * @param contentType MIME type of the upload content or null if this is not 232 * @param contentType MIME type of the upload content or null if this is not
230 * an upload request. 233 * an upload request.
231 * @param channel The channel to read to read upload data from if this is an 234 * @param channel The channel to read to read upload data from if this is an
232 * upload request. 235 * upload request.
233 * @param contentLength The length of data to upload. 236 * @param contentLength The length of data to upload.
234 */ 237 */
235 public void setUploadChannel(String contentType, 238 public void setUploadChannel(String contentType,
236 ReadableByteChannel channel, long contentLength) { 239 ReadableByteChannel channel, long contentLength) {
237 synchronized (mLock) { 240 synchronized (mLock) {
238 validateNotStarted(); 241 validateNotStarted();
239 mUploadContentType = contentType; 242 mUploadContentType = contentType;
240 mUploadChannel = channel; 243 mUploadChannel = channel;
241 mUploadContentLength = contentLength; 244 mUploadContentLength = contentLength;
242 mUploadData = null; 245 mUploadData = null;
246 mAppendChunkSemaphore = null;
243 } 247 }
244 } 248 }
245 249
250 /**
251 * Creates writable byte channel for chunked uploads as part of a POST or
252 * PUT request.
253 *
254 * @param contentType MIME type of the upload content.
255 */
256 public WritableByteChannel createChunkedUploadChannel(String contentType) {
257 synchronized (mLock) {
258 validateNotStarted();
259 mUploadContentType = contentType;
260 mUploadChannel = null;
261 mUploadContentLength = 0;
262 mUploadData = null;
263 mAppendChunkSemaphore = new Semaphore(0);
264 }
265 return new UploadChannel();
266 }
267
246 /** 268 /**
247 * Sets HTTP method for upload request. Only PUT or POST are allowed. 269 * Sets HTTP method for upload request. Only PUT or POST are allowed.
248 */ 270 */
249 public void setHttpMethod(String method) { 271 public void setHttpMethod(String method) {
250 validateNotStarted(); 272 validateNotStarted();
251 if (!("PUT".equals(method) || "POST".equals(method))) { 273 if (!("PUT".equals(method) || "POST".equals(method))) {
252 throw new IllegalArgumentException("Only PUT or POST are allowed."); 274 throw new IllegalArgumentException("Only PUT or POST are allowed.");
253 } 275 }
254 mMethod = method; 276 mMethod = method;
255 } 277 }
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
296 entry.getValue()); 318 entry.getValue());
297 } 319 }
298 } 320 }
299 321
300 if (mUploadData != null && mUploadData.length > 0) { 322 if (mUploadData != null && mUploadData.length > 0) {
301 nativeSetUploadData(mUrlRequestAdapter, mUploadContentType, 323 nativeSetUploadData(mUrlRequestAdapter, mUploadContentType,
302 mUploadData); 324 mUploadData);
303 } else if (mUploadChannel != null) { 325 } else if (mUploadChannel != null) {
304 nativeSetUploadChannel(mUrlRequestAdapter, mUploadContentType, 326 nativeSetUploadChannel(mUrlRequestAdapter, mUploadContentType,
305 mUploadContentLength); 327 mUploadContentLength);
328 } else if (mUploadContentType != null) {
329 nativeEnableChunkedUpload(mUrlRequestAdapter,
330 mUploadContentType);
306 } 331 }
307 332
308 nativeStart(mUrlRequestAdapter); 333 nativeStart(mUrlRequestAdapter);
309 } 334 }
310 } 335 }
311 336
312 public void cancel() { 337 public void cancel() {
313 synchronized (mLock) { 338 synchronized (mLock) {
314 if (mCanceled) { 339 if (mCanceled) {
315 return; 340 return;
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
371 default: 396 default:
372 return ChromiumUrlRequestPriority.MEDIUM; 397 return ChromiumUrlRequestPriority.MEDIUM;
373 } 398 }
374 } 399 }
375 400
376 private void onContentLengthOverLimit() { 401 private void onContentLengthOverLimit() {
377 mContentLengthOverLimit = true; 402 mContentLengthOverLimit = true;
378 cancel(); 403 cancel();
379 } 404 }
380 405
406 /**
407 * Invokes {@link #nativeAppendChunk(long, ByteBuffer, int, boolean)} and
408 * waits for it to notify completion.
409 * @param chunk The data. It's position must be zero.
410 * @param isLastChunk Whether chunk is the last one.
411 */
412 void appendChunkBlocking(ByteBuffer chunk, boolean isLastChunk)
413 throws IOException {
414 if (chunk.position() != 0) {
415 throw new IllegalArgumentException("The position must be zero.");
416 }
417 synchronized (mLock) {
418 if (mUrlRequestAdapter == 0) {
419 throw new IOException("Native adapter is destroyed.");
420 }
421 nativeAppendChunkToUpload(mUrlRequestAdapter, chunk, chunk.limit(),
422 false);
423 // Wait for the data to be actually consumed.
424 try {
425 mAppendChunkSemaphore.acquire();
426 } catch (InterruptedException e) {
427 // We were interrupted before the data was uploaded. Recovering
428 // from this state is complicated so we cancel the upload
429 // operation and fail.
430 Thread.currentThread().interrupt();
431
432 // TODO(miloslav): Not sure why do we set mSinkException here.
433 mSinkException = new IOException("Upload interrupted", e);
434 cancel();
435 throw mSinkException;
436 }
437 }
438 }
439
381 /** 440 /**
382 * A callback invoked when the response has been fully consumed. 441 * A callback invoked when the response has been fully consumed.
383 */ 442 */
384 private void onRequestComplete() { 443 private void onRequestComplete() {
385 mListener.onRequestComplete(this); 444 mListener.onRequestComplete(this);
386 } 445 }
387 446
388 private void validateNotRecycled() { 447 private void validateNotRecycled() {
389 if (mRecycled) { 448 if (mRecycled) {
390 throw new IllegalStateException("Accessing recycled request"); 449 throw new IllegalStateException("Accessing recycled request");
391 } 450 }
392 } 451 }
393 452
394 private void validateNotStarted() { 453 private void validateNotStarted() {
395 if (mStarted) { 454 if (mStarted) {
396 throw new IllegalStateException("Request already started"); 455 throw new IllegalStateException("Request already started");
397 } 456 }
398 } 457 }
399 458
400 private void validateHeadersAvailable() { 459 private void validateHeadersAvailable() {
401 if (!mHeadersAvailable) { 460 if (!mHeadersAvailable) {
402 throw new IllegalStateException("Response headers not available"); 461 throw new IllegalStateException("Response headers not available");
403 } 462 }
404 } 463 }
405 464
465 class UploadChannel implements WritableByteChannel {
466 private static final int UPLOAD_BYTE_BUFFER_SIZE = 32768;
467
468 private boolean mOpen = true;
469 // Native wants a direct buffer.
470 private final ByteBuffer mBuffer =
471 ByteBuffer.allocateDirect(UPLOAD_BYTE_BUFFER_SIZE);
472
473 @Override
474 public synchronized boolean isOpen() {
475 return mOpen;
476 }
477
478 @Override
479 public synchronized void close() throws IOException {
480 Log.d(ChromiumUrlRequestContext.LOG_TAG,
481 "UploadChannel.close() url=" + getUrl());
482 if (!mOpen) {
483 return;
484 }
485
486 mOpen = false;
487 mBuffer.clear();
488
489 // NOOP If the native peer has been destroyed.
490 try {
491 Log.d(ChromiumUrlRequestContext.LOG_TAG,
492 "UploadChannel.close(): final chunk.");
493 appendChunkBlocking(mBuffer, true);
494 } catch (IOException e) {
495 Log.w(ChromiumUrlRequestContext.LOG_TAG,
496 "Ignoring exception during closing.", e);
497 }
498 Log.d(ChromiumUrlRequestContext.LOG_TAG,
499 "UploadChannel.close() done.");
500 }
501
502 @Override
503 public synchronized int write(ByteBuffer sourceBuffer)
504 throws IOException {
505 Log.d(ChromiumUrlRequestContext.LOG_TAG, "UploadChannel.write(" +
506 sourceBuffer.remaining() + " bytes) url=" + getUrl());
507 int written = 0;
508 while (sourceBuffer.hasRemaining()) {
509 mBuffer.clear();
510 int oldLimit = sourceBuffer.limit();
511 if (sourceBuffer.remaining() > mBuffer.remaining()) {
512 sourceBuffer.limit(sourceBuffer.position()
513 + mBuffer.remaining());
514 }
515 mBuffer.put(sourceBuffer);
516 mBuffer.flip();
517 written += mBuffer.limit();
518 appendChunkBlocking(mBuffer, false);
519 sourceBuffer.limit(oldLimit);
520 }
521 Log.d(ChromiumUrlRequestContext.LOG_TAG,
522 "UploadChannel.write() returning");
523 return written;
524 }
525 }
526
406 // Private methods called by native library. 527 // Private methods called by native library.
407 528
408 /** 529 /**
409 * If @CalledByNative method throws an exception, request gets cancelled 530 * If @CalledByNative method throws an exception, request gets cancelled
410 * and exception could be retrieved from using getException(). 531 * and exception could be retrieved from using getException().
411 */ 532 */
412 private void onCalledByNativeException(Exception e) { 533 private void onCalledByNativeException(Exception e) {
413 mSinkException = new IOException( 534 mSinkException = new IOException(
414 "CalledByNative method has thrown an exception", e); 535 "CalledByNative method has thrown an exception", e);
415 Log.e(ChromiumUrlRequestContext.LOG_TAG, 536 Log.e(ChromiumUrlRequestContext.LOG_TAG,
416 "Exception in CalledByNative method", e); 537 "Exception in CalledByNative method", e);
417 try { 538 try {
418 cancel(); 539 cancel();
419 } catch (Exception cancel_exception) { 540 } catch (Exception cancel_exception) {
420 Log.e(ChromiumUrlRequestContext.LOG_TAG, 541 Log.e(ChromiumUrlRequestContext.LOG_TAG,
421 "Exception trying to cancel request", cancel_exception); 542 "Exception trying to cancel request", cancel_exception);
422 } 543 }
423 } 544 }
424 545
425 /** 546 /**
547 * A callback invoked when appending a chunk to the request has completed.
548 */
549 @CalledByNative
550 protected void onAppendChunkCompleted() {
551 mAppendChunkSemaphore.release();
552 }
553
554 /**
426 * A callback invoked when the first chunk of the response has arrived. 555 * A callback invoked when the first chunk of the response has arrived.
427 */ 556 */
428 @CalledByNative 557 @CalledByNative
429 private void onResponseStarted() { 558 private void onResponseStarted() {
430 try { 559 try {
431 mContentType = nativeGetContentType(mUrlRequestAdapter); 560 mContentType = nativeGetContentType(mUrlRequestAdapter);
432 mContentLength = nativeGetContentLength(mUrlRequestAdapter); 561 mContentLength = nativeGetContentLength(mUrlRequestAdapter);
433 mHeadersAvailable = true; 562 mHeadersAvailable = true;
434 563
435 if (mContentLengthLimit > 0 && 564 if (mContentLengthLimit > 0 &&
(...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after
598 String value); 727 String value);
599 728
600 private native void nativeSetMethod(long urlRequestAdapter, String method); 729 private native void nativeSetMethod(long urlRequestAdapter, String method);
601 730
602 private native void nativeSetUploadData(long urlRequestAdapter, 731 private native void nativeSetUploadData(long urlRequestAdapter,
603 String contentType, byte[] content); 732 String contentType, byte[] content);
604 733
605 private native void nativeSetUploadChannel(long urlRequestAdapter, 734 private native void nativeSetUploadChannel(long urlRequestAdapter,
606 String contentType, long contentLength); 735 String contentType, long contentLength);
607 736
737 private native void nativeEnableChunkedUpload(long urlRequestAdapter,
738 String contentType);
739
740 private native void nativeAppendChunkToUpload(long urlRequestAdapter,
741 ByteBuffer chunk, int chunkSize, boolean isLastChunk);
742
608 private native void nativeStart(long urlRequestAdapter); 743 private native void nativeStart(long urlRequestAdapter);
609 744
610 private native void nativeCancel(long urlRequestAdapter); 745 private native void nativeCancel(long urlRequestAdapter);
611 746
612 private native void nativeDestroyRequestAdapter(long urlRequestAdapter); 747 private native void nativeDestroyRequestAdapter(long urlRequestAdapter);
613 748
614 private native int nativeGetErrorCode(long urlRequestAdapter); 749 private native int nativeGetErrorCode(long urlRequestAdapter);
615 750
616 private native int nativeGetHttpStatusCode(long urlRequestAdapter); 751 private native int nativeGetHttpStatusCode(long urlRequestAdapter);
617 752
618 private native String nativeGetErrorString(long urlRequestAdapter); 753 private native String nativeGetErrorString(long urlRequestAdapter);
619 754
620 private native String nativeGetContentType(long urlRequestAdapter); 755 private native String nativeGetContentType(long urlRequestAdapter);
621 756
622 private native long nativeGetContentLength(long urlRequestAdapter); 757 private native long nativeGetContentLength(long urlRequestAdapter);
623 758
624 private native String nativeGetHeader(long urlRequestAdapter, String name); 759 private native String nativeGetHeader(long urlRequestAdapter, String name);
625 760
626 private native void nativeGetAllHeaders(long urlRequestAdapter, 761 private native void nativeGetAllHeaders(long urlRequestAdapter,
627 ResponseHeadersMap headers); 762 ResponseHeadersMap headers);
628 763
629 // Explicit class to work around JNI-generator generics confusion. 764 // Explicit class to work around JNI-generator generics confusion.
630 private class ResponseHeadersMap extends HashMap<String, List<String>> { 765 private class ResponseHeadersMap extends HashMap<String, List<String>> {
631 } 766 }
632 } 767 }
OLDNEW
« no previous file with comments | « components/cronet/android/chromium_url_request.cc ('k') | components/cronet/android/url_request_adapter.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698