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

Unified Diff: components/cronet/android/java/src/org/chromium/net/CronetUploadDataStream.java

Issue 849903002: [Cronet] Upload support for async APIs (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Combined with Matt's CL Created 5 years, 11 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 side-by-side diff with in-line comments
Download patch
Index: components/cronet/android/java/src/org/chromium/net/CronetUploadDataStream.java
diff --git a/components/cronet/android/java/src/org/chromium/net/CronetUploadDataStream.java b/components/cronet/android/java/src/org/chromium/net/CronetUploadDataStream.java
new file mode 100644
index 0000000000000000000000000000000000000000..117b6afc18bc1070e991fd3ef7e9833929a1497a
--- /dev/null
+++ b/components/cronet/android/java/src/org/chromium/net/CronetUploadDataStream.java
@@ -0,0 +1,256 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.net;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.Executor;
+
+/**
+ * Pass an upload body to a UrlRequest using an UploadDataProvider.
+ */
+@JNINamespace("cronet")
+public class CronetUploadDataStream implements UploadDataSink {
+ // These are never changed, once a request starts.
+ private final Executor mExecutor;
+ private final UploadDataProvider mDataProvider;
+ private final long mLength;
+ private CronetUrlRequest mRequest;
+
+ // Reusable read task, to reduce redundant memory allocation.
+ private final Runnable mReadTask = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mLock) {
+ if (mReading || mRewinding || mByteBuffer == null
+ || mUploadDataStreamDelegate == 0) {
+ throw new IllegalStateException(
+ "Unexpected readData call.");
+ }
+ mReading = true;
+ }
+
+ try {
+ mDataProvider.read(CronetUploadDataStream.this, mByteBuffer);
+ } catch (Exception exception) {
+ onError(exception);
+ }
+ }
+ };
+
+ private ByteBuffer mByteBuffer = null;
+
+ // Lock that protects all subsequent variables. The delegate has to be
+ // protected to ensure safe shutdown, mReading and mRewinding are protected
+ // to robustly detect getting read/rewind results more often than expected.
+ private final Object mLock = new Object();
+
+ // Native adapter object, owned by the CronetUploadDataStream. It's only
+ // deleted after the native UploadDataStream object is destroyed. All
+ // access to the adapter is synchronized, for safe usage and cleanup.
+ private long mUploadDataStreamDelegate;
+
+ private boolean mReading = false;
+ private boolean mRewinding = false;
+
+ public CronetUploadDataStream(UploadDataProvider dataProvider,
+ Executor executor) {
+ mExecutor = executor;
+ mDataProvider = dataProvider;
+ mLength = mDataProvider.getLength();
+ }
+
+ @SuppressWarnings("unused")
+ @CalledByNative
+ void readData(ByteBuffer byteBuffer) {
+ mByteBuffer = byteBuffer;
+ try {
+ mExecutor.execute(mReadTask);
+ // nativeOnReadSucceeded(mUploadDataStreamDelegate, 0, 0, true);
+ } catch (Exception exception) {
+ onError(exception);
+ }
+ }
+
+ // TODO(mmenke): Consider implementing a cancel method.
+ // currently wait for any pending read to complete.
+
+ @SuppressWarnings("unused")
+ @CalledByNative
+ void rewind() {
+ Runnable task = new Runnable() {
+ // @Override
+ public void run() {
+ synchronized (mLock) {
+ if (mReading || mRewinding
+ || mUploadDataStreamDelegate == 0) {
+ throw new IllegalStateException(
+ "Unexpected rewind call.");
+ }
+ mRewinding = true;
+ try {
+ mDataProvider.rewind(CronetUploadDataStream.this);
+ } catch (Exception exception) {
+ onError(exception);
+ }
+ }
+ }
+ };
+
+ mExecutor.execute(task);
+ }
+
+ @SuppressWarnings("unused")
+ @CalledByNative
+ void onAdapterDestroyed() {
+ Runnable task = new Runnable() {
+ // @Override
+ public void run() {
+ destroyAdapter();
+ }
+ };
+
+ mExecutor.execute(task);
+ }
+
+ private void onError(Exception exception) {
+ synchronized (mLock) {
+ mReading = false;
+ mRewinding = false;
+ mByteBuffer = null;
+ }
+
+ // Just fail the request - simpler to fail directly, and
+ // UploadDataStream only supports failing during initialization, not
+ // while reading.
+ mRequest.onUploadException(exception);
+ }
+
+ @Override
+ public void onReadSucceeded(boolean lastChunk) {
+ synchronized (mLock) {
+ if (!mReading) {
+ throw new IllegalStateException("Non-existent read succeeded.");
+ }
+ if (lastChunk && mLength >= 0) {
+ throw new IllegalArgumentException(
+ "Non-chunked upload can't have last chunk");
+ }
+ int position = mByteBuffer.position();
+ int limit = mByteBuffer.limit();
+
+ mByteBuffer = null;
+ mReading = false;
+
+ // Request may been canceled already.
+ if (mUploadDataStreamDelegate == 0) {
+ return;
+ }
+ nativeOnReadSucceeded(mUploadDataStreamDelegate, position, limit,
+ lastChunk);
+ }
+ }
+
+ public void onReadError(Exception exception) {
+ synchronized (mLock) {
+ if (!mReading) {
+ throw new IllegalStateException("Non-existent read failed.");
+ }
+ // Request may been canceled already.
+ if (mUploadDataStreamDelegate == 0) {
+ return;
+ }
+ onError(exception);
+ }
+ }
+
+ public void onRewindSucceeded() {
+ synchronized (mLock) {
+ if (!mRewinding) {
+ throw new IllegalStateException(
+ "Non-existent rewind succeeded.");
+ }
+ mRewinding = false;
+ // Request may been canceled already.
+ if (mUploadDataStreamDelegate == 0) {
+ return;
+ }
+ nativeOnRewindSucceeded(mUploadDataStreamDelegate);
+ }
+ }
+
+ public void onRewindError(Exception exception) {
+ synchronized (mLock) {
+ if (!mRewinding) {
+ throw new IllegalStateException("Non-existent rewind failed.");
+ }
+ // Request may been canceled already.
+ if (mUploadDataStreamDelegate == 0) {
+ return;
+ }
+ onError(exception);
+ }
+ }
+
+ /**
+ * The adapter is owned by the CronetUploadDataStream, so it can be
+ * destroyed safely; however, destruction is initiated by the destruction of
+ * the C++ URLUploadStream.
+ */
+ void destroyAdapter() {
+ synchronized (mLock) {
+ if (mUploadDataStreamDelegate == 0)
+ return;
+ nativeDestroyDelegate(mUploadDataStreamDelegate);
+ mUploadDataStreamDelegate = 0;
+ }
+ }
+
+ /**
+ * Creates native objects and attaches them to the underlying request
+ * adapter object.
+ * TODO(mmenke): If more types of native upload streams are needed, create
+ * an interface with just this method, to minimize CronetURLRequest's
+ * dependencies on each upload stream type.
+ */
+ void attachToRequest(CronetUrlRequest request, long requestAdapter) {
+ if (mLength < 0) {
+ // TODO(mmenke): Add tests and remove this line.
+ throw new IllegalArgumentException(
+ "Chunked uploads not supported.");
+ }
+ mRequest = request;
+ mUploadDataStreamDelegate =
+ nativeAttachUploadDataToRequest(requestAdapter, mLength);
+ }
+
+ /**
+ * Creates a native UploadDataStreamDelegate and UploadDataStreamAdapter
+ * for testing.
+ */
+ public long createAdapter() {
+ mUploadDataStreamDelegate = nativeCreateDelegate();
+ return nativeCreateAdapter(mLength, mUploadDataStreamDelegate);
+ }
+
+ // Native methods are implemented in upload_data_stream_adapter.cc.
+
+ private native long nativeAttachUploadDataToRequest(long urlRequestAdapter,
+ long length);
+
+ private native long nativeCreateDelegate();
+
+ private native long nativeCreateAdapter(long length, long delegate);
+
+ private native void nativeOnReadSucceeded(long uploadDataStreamDelegate,
+ int position, int limit, boolean finalChunk);
+
+ private native void nativeOnRewindSucceeded(long uploadDataStreamDelegate);
+
+ private static native void nativeDestroyDelegate(
+ long uploadDataStreamDelegate);
+}

Powered by Google App Engine
This is Rietveld 408576698