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

Side by Side 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: Addressed Misha's comments Created 5 years, 10 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 2015 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 org.chromium.base.CalledByNative;
8 import org.chromium.base.JNINamespace;
9
10 import java.nio.ByteBuffer;
11 import java.util.concurrent.Executor;
12
13 /**
14 * Pass an upload body to a UrlRequest using an UploadDataProvider.
15 */
16 @JNINamespace("cronet")
17 public class CronetUploadDataStream implements UploadDataSink {
18 // These are never changed, once a request starts.
19 private final Executor mExecutor;
20 private final UploadDataProvider mDataProvider;
21 private final long mLength;
22 private CronetUrlRequest mRequest;
23
24 // Reusable read task, to reduce redundant memory allocation.
25 private final Runnable mReadTask = new Runnable() {
26 @Override
27 public void run() {
28 synchronized (mLock) {
29 if (mReading || mRewinding || mByteBuffer == null
30 || mUploadDataStreamDelegate == 0) {
31 throw new IllegalStateException(
32 "Unexpected readData call.");
33 }
34 mReading = true;
35 }
36
37 try {
38 mDataProvider.read(CronetUploadDataStream.this, mByteBuffer);
39 } catch (Exception exception) {
40 onError(exception);
41 }
42 }
43 };
44
45 private ByteBuffer mByteBuffer = null;
46
47 // Lock that protects all subsequent variables. The delegate has to be
48 // protected to ensure safe shutdown, mReading and mRewinding are protected
49 // to robustly detect getting read/rewind results more often than expected.
50 private final Object mLock = new Object();
51
52 // Native adapter object, owned by the CronetUploadDataStream. It's only
53 // deleted after the native UploadDataStream object is destroyed. All
54 // access to the adapter is synchronized, for safe usage and cleanup.
55 private long mUploadDataStreamDelegate;
56
57 private boolean mReading = false;
58 private boolean mRewinding = false;
59 private boolean mDestroyAdapterPostponed = false;
60
61 public CronetUploadDataStream(UploadDataProvider dataProvider,
62 Executor executor) {
63 mExecutor = executor;
64 mDataProvider = dataProvider;
65 mLength = mDataProvider.getLength();
pauljensen 2015/02/05 15:53:39 While using the upload API I found it rather unexp
xunjieli 2015/02/05 18:11:29 I tried doing #1. but I think getLength() is nice
66 }
67
68 @SuppressWarnings("unused")
69 @CalledByNative
70 void readData(ByteBuffer byteBuffer) {
71 mByteBuffer = byteBuffer;
72 try {
73 mExecutor.execute(mReadTask);
74 } catch (Exception exception) {
75 onError(exception);
76 }
77 }
78
79 // TODO(mmenke): Consider implementing a cancel method.
80 // currently wait for any pending read to complete.
81
82 @SuppressWarnings("unused")
83 @CalledByNative
84 void rewind() {
85 Runnable task = new Runnable() {
86 @Override
87 public void run() {
88 synchronized (mLock) {
89 if (mReading || mRewinding
90 || mUploadDataStreamDelegate == 0) {
91 throw new IllegalStateException(
92 "Unexpected rewind call.");
93 }
94 mRewinding = true;
95 try {
96 mDataProvider.rewind(CronetUploadDataStream.this);
97 } catch (Exception exception) {
98 onError(exception);
99 }
100 }
101 }
102 };
103
104 mExecutor.execute(task);
105 }
106
107 @SuppressWarnings("unused")
108 @CalledByNative
109 void onAdapterDestroyed() {
110 Runnable task = new Runnable() {
111 @Override
112 public void run() {
113 destroyAdapter();
114 }
115 };
116
117 mExecutor.execute(task);
118 }
119
120 private void onError(Exception exception) {
121 synchronized (mLock) {
122 if (!mReading && !mRewinding) {
123 throw new IllegalStateException(
124 "There is no read or rewind in progress.");
125 }
126 mReading = false;
127 mRewinding = false;
128 mByteBuffer = null;
129 destroyAdapterIfPostponed();
130 }
131
132 // Just fail the request - simpler to fail directly, and
133 // UploadDataStream only supports failing during initialization, not
134 // while reading. This should be safe, even if we deleted the adapter,
135 // because in that case, the request has already been cancelled.
136 mRequest.onUploadException(exception);
137 }
138
139 @Override
140 public void onReadSucceeded(boolean lastChunk) {
141 synchronized (mLock) {
142 if (!mReading) {
143 throw new IllegalStateException("Non-existent read succeeded.");
144 }
145 if (lastChunk && mLength >= 0) {
146 throw new IllegalArgumentException(
147 "Non-chunked upload can't have last chunk");
148 }
149 int position = mByteBuffer.position();
150 int limit = mByteBuffer.limit();
mmenke 2015/02/05 15:52:29 Don't need the limit any more, and suggest renamin
xunjieli 2015/02/05 18:11:29 Done.
151
152 mByteBuffer = null;
153 mReading = false;
154
155 destroyAdapterIfPostponed();
156 // Request may been canceled already.
157 if (mUploadDataStreamDelegate == 0) {
158 return;
159 }
160 nativeOnReadSucceeded(mUploadDataStreamDelegate, position, limit,
161 lastChunk);
162 }
163 }
164
165 public void onReadError(Exception exception) {
166 synchronized (mLock) {
167 if (!mReading) {
168 throw new IllegalStateException("Non-existent read failed.");
169 }
170 // Request may been canceled already.
171 if (mUploadDataStreamDelegate == 0) {
172 return;
173 }
174 onError(exception);
175 }
176 }
177
178 public void onRewindSucceeded() {
179 synchronized (mLock) {
180 if (!mRewinding) {
181 throw new IllegalStateException(
182 "Non-existent rewind succeeded.");
183 }
184 mRewinding = false;
185 // Request may been canceled already.
186 if (mUploadDataStreamDelegate == 0) {
187 return;
188 }
189 nativeOnRewindSucceeded(mUploadDataStreamDelegate);
190 }
191 }
192
193 public void onRewindError(Exception exception) {
194 synchronized (mLock) {
195 if (!mRewinding) {
196 throw new IllegalStateException("Non-existent rewind failed.");
197 }
198 // Request may been canceled already.
199 if (mUploadDataStreamDelegate == 0) {
200 return;
201 }
202 onError(exception);
203 }
204 }
205
206 /**
207 * The adapter is owned by the CronetUploadDataStream, so it can be
208 * destroyed safely when there is no pending read; however, destruction is
209 * initiated by the destruction of the native UploadDataStream.
210 */
211 private void destroyAdapter() {
212 synchronized (mLock) {
213 if (mReading) {
214 // Wait for the read to complete before destroy the adapter.
215 mDestroyAdapterPostponed = true;
216 return;
217 }
218 if (mUploadDataStreamDelegate == 0) {
219 return;
220 }
221 nativeDestroyDelegate(mUploadDataStreamDelegate);
222 mUploadDataStreamDelegate = 0;
223 }
224 }
225
226 /**
227 * Destroy the native adapter if the destruction is postponed due to a
228 * pending read, which has since completed. Caller needs to be on executor
229 * thread and acquire mLock.
230 */
231 private void destroyAdapterIfPostponed() {
232 if (mReading) {
233 throw new IllegalStateException(
234 "Method should not be called when read has not completed.");
235 }
236 if (mDestroyAdapterPostponed) {
237 destroyAdapter();
238 }
239 }
240
241 /**
242 * Creates native objects and attaches them to the underlying request
243 * adapter object.
244 * TODO(mmenke): If more types of native upload streams are needed, create
245 * an interface with just this method, to minimize CronetURLRequest's
246 * dependencies on each upload stream type.
247 */
248 void attachToRequest(CronetUrlRequest request, long requestAdapter) {
249 if (mLength < 0) {
250 // TODO(mmenke): Add tests and remove this line.
251 throw new IllegalArgumentException(
252 "Chunked uploads not supported.");
253 }
254 mRequest = request;
255 mUploadDataStreamDelegate =
256 nativeAttachUploadDataToRequest(requestAdapter, mLength);
257 }
258
259 /**
260 * Creates a native UploadDataStreamDelegate and UploadDataStreamAdapter
261 * for testing.
262 * @return the address of the native CronetUploadDataStreamAdapter object
263 */
264 public long createAdapterForTesting() {
265 mUploadDataStreamDelegate = nativeCreateDelegateForTesting();
266 return nativeCreateAdapterForTesting(mLength, mUploadDataStreamDelegate) ;
267 }
268
269 // Native methods are implemented in upload_data_stream_adapter.cc.
270
271 private native long nativeAttachUploadDataToRequest(long urlRequestAdapter,
272 long length);
273
274 private native long nativeCreateDelegateForTesting();
275
276 private native long nativeCreateAdapterForTesting(long length,
277 long delegate);
278
279 private native void nativeOnReadSucceeded(long uploadDataStreamDelegate,
280 int position, int limit, boolean finalChunk);
281
282 private native void nativeOnRewindSucceeded(long uploadDataStreamDelegate);
283
284 private static native void nativeDestroyDelegate(
285 long uploadDataStreamDelegate);
286 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698