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

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: Removed unnecessary argument 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;
mef 2015/02/06 21:55:21 I think it is worth commenting that mBuffer is pas
xunjieli 2015/02/09 15:59:36 Done.
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();
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 bytesRead = mByteBuffer.position();
150
151 mByteBuffer = null;
152 mReading = false;
153
154 destroyAdapterIfPostponed();
155 // Request may been canceled already.
156 if (mUploadDataStreamDelegate == 0) {
157 return;
158 }
159 nativeOnReadSucceeded(mUploadDataStreamDelegate, bytesRead,
160 lastChunk);
161 }
162 }
163
164 public void onReadError(Exception exception) {
165 synchronized (mLock) {
166 if (!mReading) {
167 throw new IllegalStateException("Non-existent read failed.");
168 }
169 // Request may been canceled already.
170 if (mUploadDataStreamDelegate == 0) {
171 return;
172 }
173 onError(exception);
174 }
175 }
176
177 public void onRewindSucceeded() {
178 synchronized (mLock) {
179 if (!mRewinding) {
180 throw new IllegalStateException(
181 "Non-existent rewind succeeded.");
182 }
183 mRewinding = false;
184 // Request may been canceled already.
185 if (mUploadDataStreamDelegate == 0) {
186 return;
187 }
188 nativeOnRewindSucceeded(mUploadDataStreamDelegate);
189 }
190 }
191
192 public void onRewindError(Exception exception) {
193 synchronized (mLock) {
194 if (!mRewinding) {
195 throw new IllegalStateException("Non-existent rewind failed.");
196 }
197 // Request may been canceled already.
198 if (mUploadDataStreamDelegate == 0) {
199 return;
200 }
201 onError(exception);
202 }
203 }
204
205 /**
206 * The adapter is owned by the CronetUploadDataStream, so it can be
207 * destroyed safely when there is no pending read; however, destruction is
208 * initiated by the destruction of the native UploadDataStream.
209 */
210 private void destroyAdapter() {
211 synchronized (mLock) {
212 if (mReading) {
213 // Wait for the read to complete before destroy the adapter.
214 mDestroyAdapterPostponed = true;
215 return;
216 }
217 if (mUploadDataStreamDelegate == 0) {
218 return;
219 }
220 nativeDestroyDelegate(mUploadDataStreamDelegate);
221 mUploadDataStreamDelegate = 0;
222 }
223 }
224
225 /**
226 * Destroy the native adapter if the destruction is postponed due to a
227 * pending read, which has since completed. Caller needs to be on executor
228 * thread and acquire mLock.
229 */
230 private void destroyAdapterIfPostponed() {
231 if (mReading) {
232 throw new IllegalStateException(
233 "Method should not be called when read has not completed.");
234 }
235 if (mDestroyAdapterPostponed) {
236 destroyAdapter();
237 }
238 }
239
240 /**
241 * Creates native objects and attaches them to the underlying request
242 * adapter object.
243 * TODO(mmenke): If more types of native upload streams are needed, create
244 * an interface with just this method, to minimize CronetURLRequest's
245 * dependencies on each upload stream type.
246 */
247 void attachToRequest(CronetUrlRequest request, long requestAdapter) {
248 if (mLength < 0) {
249 // TODO(mmenke): Add tests and remove this line.
mef 2015/02/06 21:55:21 do we still need it?
xunjieli 2015/02/09 15:59:36 Yes. Although chunked uploads are supported, but o
250 throw new IllegalArgumentException(
251 "Chunked uploads not supported.");
252 }
253 mRequest = request;
254 mUploadDataStreamDelegate =
255 nativeAttachUploadDataToRequest(requestAdapter, mLength);
256 }
257
258 /**
259 * Creates a native UploadDataStreamDelegate and UploadDataStreamAdapter
260 * for testing.
261 * @return the address of the native CronetUploadDataStreamAdapter object
262 */
263 public long createAdapterForTesting() {
264 mUploadDataStreamDelegate = nativeCreateDelegateForTesting();
265 return nativeCreateAdapterForTesting(mLength, mUploadDataStreamDelegate) ;
266 }
267
268 // Native methods are implemented in upload_data_stream_adapter.cc.
269
270 private native long nativeAttachUploadDataToRequest(long urlRequestAdapter,
271 long length);
272
273 private native long nativeCreateDelegateForTesting();
274
275 private native long nativeCreateAdapterForTesting(long length,
276 long delegate);
277
278 private native void nativeOnReadSucceeded(long uploadDataStreamDelegate,
279 int bytesRead, boolean finalChunk);
280
281 private native void nativeOnRewindSucceeded(long uploadDataStreamDelegate);
282
283 private static native void nativeDestroyDelegate(
284 long uploadDataStreamDelegate);
285 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698