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

Side by Side Diff: components/cronet/android/test/javatests/src/org/chromium/net/TestBidirectionalStreamCallback.java

Issue 1412243012: Initial implementation of CronetBidirectionalStream. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address Helen's comments. Created 4 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 unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 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.os.ConditionVariable; 7 import android.os.ConditionVariable;
8 8
9 import static junit.framework.Assert.assertEquals; 9 import static junit.framework.Assert.assertEquals;
10 import static junit.framework.Assert.assertFalse; 10 import static junit.framework.Assert.assertFalse;
11 import static junit.framework.Assert.assertNull; 11 import static junit.framework.Assert.assertNull;
12 import static junit.framework.Assert.assertTrue; 12 import static junit.framework.Assert.assertTrue;
13 13
14 import java.nio.ByteBuffer; 14 import java.nio.ByteBuffer;
15 import java.util.ArrayList; 15 import java.util.ArrayList;
16 import java.util.concurrent.Executor; 16 import java.util.concurrent.Executor;
17 import java.util.concurrent.ExecutorService; 17 import java.util.concurrent.ExecutorService;
18 import java.util.concurrent.Executors; 18 import java.util.concurrent.Executors;
19 import java.util.concurrent.ThreadFactory; 19 import java.util.concurrent.ThreadFactory;
20 20
21 /** 21 /**
22 * Callback that tracks information from different callbacks and and has a 22 * Callback that tracks information from different callbacks and and has a
23 * method to block thread until the request completes on another thread. 23 * method to block thread until the stream completes on another thread.
24 * Allows to cancel, block request or throw an exception from an arbitrary step. 24 * Allows to cancel, block stream or throw an exception from an arbitrary step.
25 */ 25 */
26 class TestUrlRequestCallback extends UrlRequest.Callback { 26 class TestBidirectionalStreamCallback extends BidirectionalStream.Callback {
27 public ArrayList<UrlResponseInfo> mRedirectResponseInfoList = new ArrayList< UrlResponseInfo>();
28 public ArrayList<String> mRedirectUrlList = new ArrayList<String>();
29 public UrlResponseInfo mResponseInfo; 27 public UrlResponseInfo mResponseInfo;
30 public UrlRequestException mError; 28 public CronetException mError;
31 29
32 public ResponseStep mResponseStep = ResponseStep.NOTHING; 30 public ResponseStep mResponseStep = ResponseStep.NOTHING;
33 31
34 public int mRedirectCount = 0;
35 public boolean mOnErrorCalled = false; 32 public boolean mOnErrorCalled = false;
36 public boolean mOnCanceledCalled = false; 33 public boolean mOnCanceledCalled = false;
37 34
38 public int mHttpResponseDataLength = 0; 35 public int mHttpResponseDataLength = 0;
39 public String mResponseAsString = ""; 36 public String mResponseAsString = "";
40 37
41 // Expect legacy read() API to be used on UrlRequest. 38 public UrlResponseInfo.HeaderBlock mTrailers;
42 // TODO(pauljensen): Remove when all callers of UrlRequest.read() are
43 // transitioned to UrlRequest.readNew();
44 public boolean mLegacyReadByteBufferAdjustment = false;
45 39
46 private static final int READ_BUFFER_SIZE = 32 * 1024; 40 private static final int READ_BUFFER_SIZE = 32 * 1024;
47 41
48 // When false, the consumer is responsible for all calls into the request 42 // When false, the consumer is responsible for all calls into the request
xunjieli 2016/01/22 16:12:19 nit: s/request/stream
mef 2016/01/22 17:36:07 Done.
49 // that advance it. 43 // that advance it.
50 private boolean mAutoAdvance = true; 44 private boolean mAutoAdvance = true;
51 45
52 // Conditionally fail on certain steps. 46 // Conditionally fail on certain steps.
53 private FailureType mFailureType = FailureType.NONE; 47 private FailureType mFailureType = FailureType.NONE;
54 private ResponseStep mFailureStep = ResponseStep.NOTHING; 48 private ResponseStep mFailureStep = ResponseStep.NOTHING;
55 49
56 // Signals when request is done either successfully or not. 50 // Signals when request is done either successfully or not.
xunjieli 2016/01/22 16:12:19 nit: s/request/stream
mef 2016/01/22 17:36:07 Done.
57 private final ConditionVariable mDone = new ConditionVariable(); 51 private final ConditionVariable mDone = new ConditionVariable();
58 52
59 // Signaled on each step when mAutoAdvance is false. 53 // Signaled on each step when mAutoAdvance is false.
60 private final ConditionVariable mStepBlock = new ConditionVariable(); 54 private final ConditionVariable mReadStepBlock = new ConditionVariable();
55 private final ConditionVariable mWriteStepBlock = new ConditionVariable();
61 56
62 // Executor Service for Cronet callbacks. 57 // Executor Service for Cronet callbacks.
63 private final ExecutorService mExecutorService = 58 private final ExecutorService mExecutorService =
64 Executors.newSingleThreadExecutor(new ExecutorThreadFactory()); 59 Executors.newSingleThreadExecutor(new ExecutorThreadFactory());
65 private Thread mExecutorThread; 60 private Thread mExecutorThread;
66 61
67 // position() of ByteBuffer prior to readNew() call. 62 // position() of ByteBuffer prior to read() call.
68 private int mBufferPositionBeforeRead; 63 private int mBufferPositionBeforeRead;
69 64
65 // Data to write.
66 private ArrayList<ByteBuffer> mWriteBuffers = new ArrayList<ByteBuffer>();
67
70 private class ExecutorThreadFactory implements ThreadFactory { 68 private class ExecutorThreadFactory implements ThreadFactory {
71 public Thread newThread(Runnable r) { 69 public Thread newThread(Runnable r) {
72 mExecutorThread = new Thread(r); 70 mExecutorThread = new Thread(r);
73 return mExecutorThread; 71 return mExecutorThread;
74 } 72 }
75 } 73 }
76 74
77 public enum ResponseStep { 75 public enum ResponseStep {
78 NOTHING, 76 NOTHING,
79 ON_RECEIVED_REDIRECT, 77 ON_REQUEST_HEADERS_SENT,
80 ON_RESPONSE_STARTED, 78 ON_RESPONSE_STARTED,
81 ON_READ_COMPLETED, 79 ON_READ_COMPLETED,
80 ON_WRITE_COMPLETED,
81 ON_TRAILERS,
82 ON_CANCELED,
83 ON_FAILED,
82 ON_SUCCEEDED 84 ON_SUCCEEDED
83 } 85 }
84 86
85 public enum FailureType { 87 public enum FailureType {
86 NONE, 88 NONE,
87 CANCEL_SYNC, 89 CANCEL_SYNC,
88 CANCEL_ASYNC, 90 CANCEL_ASYNC,
89 // Same as above, but continues to advance the request after posting 91 // Same as above, but continues to advance the request after posting
xunjieli 2016/01/22 16:12:19 nit: s/request/stream
mef 2016/01/22 17:36:07 Done.
90 // the cancellation task. 92 // the cancellation task.
91 CANCEL_ASYNC_WITHOUT_PAUSE, 93 CANCEL_ASYNC_WITHOUT_PAUSE,
92 THROW_SYNC 94 THROW_SYNC
93 } 95 }
94 96
95 public void setAutoAdvance(boolean autoAdvance) { 97 public void setAutoAdvance(boolean autoAdvance) {
96 mAutoAdvance = autoAdvance; 98 mAutoAdvance = autoAdvance;
97 } 99 }
98 100
99 public void setFailure(FailureType failureType, ResponseStep failureStep) { 101 public void setFailure(FailureType failureType, ResponseStep failureStep) {
100 mFailureStep = failureStep; 102 mFailureStep = failureStep;
101 mFailureType = failureType; 103 mFailureType = failureType;
102 } 104 }
103 105
104 public void blockForDone() { 106 public void blockForDone() {
105 mDone.block(); 107 mDone.block();
106 } 108 }
107 109
108 public void waitForNextStep() { 110 public void waitForNextReadStep() {
109 mStepBlock.block(); 111 mReadStepBlock.block();
110 mStepBlock.close(); 112 mReadStepBlock.close();
113 }
114
115 public void waitForNextWriteStep() {
116 mWriteStepBlock.block();
117 mWriteStepBlock.close();
111 } 118 }
112 119
113 public Executor getExecutor() { 120 public Executor getExecutor() {
114 return mExecutorService; 121 return mExecutorService;
115 } 122 }
116 123
117 public void shutdownExecutor() { 124 public void shutdownExecutor() {
118 mExecutorService.shutdown(); 125 mExecutorService.shutdown();
119 } 126 }
120 127
121 @Override 128 public void addWriteData(byte[] data) {
122 public void onRedirectReceived( 129 ByteBuffer writeBuffer = ByteBuffer.allocateDirect(data.length);
123 UrlRequest request, UrlResponseInfo info, String newLocationUrl) { 130 writeBuffer.put(data);
124 assertEquals(mExecutorThread, Thread.currentThread()); 131 writeBuffer.flip();
125 assertFalse(request.isDone()); 132 mWriteBuffers.add(writeBuffer);
126 assertTrue(mResponseStep == ResponseStep.NOTHING
127 || mResponseStep == ResponseStep.ON_RECEIVED_REDIRECT);
128 assertNull(mError);
129
130 mResponseStep = ResponseStep.ON_RECEIVED_REDIRECT;
131 mRedirectUrlList.add(newLocationUrl);
132 mRedirectResponseInfoList.add(info);
133 ++mRedirectCount;
134 if (maybeThrowCancelOrPause(request)) {
135 return;
136 }
137 request.followRedirect();
138 } 133 }
139 134
140 @Override 135 @Override
141 public void onResponseStarted(UrlRequest request, UrlResponseInfo info) { 136 public void onRequestHeadersSent(BidirectionalStream stream) {
142 assertEquals(mExecutorThread, Thread.currentThread()); 137 assertEquals(mExecutorThread, Thread.currentThread());
143 assertFalse(request.isDone()); 138 assertFalse(stream.isDone());
139 assertEquals(ResponseStep.NOTHING, mResponseStep);
140 assertNull(mError);
141
142 mResponseStep = ResponseStep.ON_REQUEST_HEADERS_SENT;
143 if (maybeThrowCancelOrPause(stream, mWriteStepBlock)) {
144 return;
145 }
146 startNextWrite(stream);
147 }
148
149 @Override
150 public void onResponseHeadersReceived(BidirectionalStream stream, UrlRespons eInfo info) {
151 assertEquals(mExecutorThread, Thread.currentThread());
152 assertFalse(stream.isDone());
144 assertTrue(mResponseStep == ResponseStep.NOTHING 153 assertTrue(mResponseStep == ResponseStep.NOTHING
145 || mResponseStep == ResponseStep.ON_RECEIVED_REDIRECT); 154 || mResponseStep == ResponseStep.ON_REQUEST_HEADERS_SENT);
146 assertNull(mError); 155 assertNull(mError);
147 156
148 mResponseStep = ResponseStep.ON_RESPONSE_STARTED; 157 mResponseStep = ResponseStep.ON_RESPONSE_STARTED;
149 mResponseInfo = info; 158 mResponseInfo = info;
150 if (maybeThrowCancelOrPause(request)) { 159 if (maybeThrowCancelOrPause(stream, mReadStepBlock)) {
151 return; 160 return;
152 } 161 }
153 startNextRead(request); 162 startNextRead(stream);
154 } 163 }
155 164
156 @Override 165 @Override
157 public void onReadCompleted(UrlRequest request, UrlResponseInfo info, ByteBu ffer byteBuffer) { 166 public void onReadCompleted(
167 BidirectionalStream stream, UrlResponseInfo info, ByteBuffer byteBuf fer) {
158 assertEquals(mExecutorThread, Thread.currentThread()); 168 assertEquals(mExecutorThread, Thread.currentThread());
159 assertFalse(request.isDone()); 169 assertFalse(stream.isDone());
160 assertTrue(mResponseStep == ResponseStep.ON_RESPONSE_STARTED 170 assertTrue(mResponseStep == ResponseStep.ON_RESPONSE_STARTED
161 || mResponseStep == ResponseStep.ON_READ_COMPLETED); 171 || mResponseStep == ResponseStep.ON_READ_COMPLETED
172 || mResponseStep == ResponseStep.ON_WRITE_COMPLETED);
162 assertNull(mError); 173 assertNull(mError);
163 174
164 mResponseStep = ResponseStep.ON_READ_COMPLETED; 175 mResponseStep = ResponseStep.ON_READ_COMPLETED;
165 176
166 final byte[] lastDataReceivedAsBytes; 177 final byte[] lastDataReceivedAsBytes;
167 if (mLegacyReadByteBufferAdjustment) { 178 final int bytesRead = byteBuffer.position() - mBufferPositionBeforeRead;
168 // Make a slice of ByteBuffer, so can read from it without affecting 179 mHttpResponseDataLength += bytesRead;
169 // position, which allows tests to check the state of the buffer. 180 lastDataReceivedAsBytes = new byte[bytesRead];
170 ByteBuffer slice = byteBuffer.slice(); 181 // Rewind |byteBuffer.position()| to pre-read() position.
171 mHttpResponseDataLength += slice.remaining(); 182 byteBuffer.position(mBufferPositionBeforeRead);
172 lastDataReceivedAsBytes = new byte[slice.remaining()]; 183 // This restores |byteBuffer.position()| to its value on entrance to
xunjieli 2016/01/22 16:12:19 nit: maybe remove pipes on 181 and 183.
mef 2016/01/22 17:36:07 Done.
173 slice.get(lastDataReceivedAsBytes); 184 // this function.
174 } else { 185 byteBuffer.get(lastDataReceivedAsBytes);
175 final int bytesRead = byteBuffer.position() - mBufferPositionBeforeR ead; 186
176 mHttpResponseDataLength += bytesRead;
177 lastDataReceivedAsBytes = new byte[bytesRead];
178 // Rewind |byteBuffer.position()| to pre-read() position.
179 byteBuffer.position(mBufferPositionBeforeRead);
180 // This restores |byteBuffer.position()| to its value on entrance to
181 // this function.
182 byteBuffer.get(lastDataReceivedAsBytes);
183 }
184 mResponseAsString += new String(lastDataReceivedAsBytes); 187 mResponseAsString += new String(lastDataReceivedAsBytes);
185 188
186 if (maybeThrowCancelOrPause(request)) { 189 if (maybeThrowCancelOrPause(stream, mReadStepBlock)) {
187 return; 190 return;
188 } 191 }
189 startNextRead(request); 192 startNextRead(stream);
190 } 193 }
191 194
192 @Override 195 @Override
193 public void onSucceeded(UrlRequest request, UrlResponseInfo info) { 196 public void onWriteCompleted(
197 BidirectionalStream stream, UrlResponseInfo info, ByteBuffer buffer) {
194 assertEquals(mExecutorThread, Thread.currentThread()); 198 assertEquals(mExecutorThread, Thread.currentThread());
195 assertTrue(request.isDone()); 199 assertFalse(stream.isDone());
200 assertNull(mError);
201 mResponseStep = ResponseStep.ON_WRITE_COMPLETED;
202 if (!mWriteBuffers.isEmpty()) {
203 assertEquals(buffer, mWriteBuffers.get(0));
204 mWriteBuffers.remove(0);
205 }
206 if (maybeThrowCancelOrPause(stream, mWriteStepBlock)) {
207 return;
208 }
209 startNextWrite(stream);
210 }
211
212 @Override
213 public void onResponseTrailersReceived(BidirectionalStream stream, UrlRespon seInfo info,
214 UrlResponseInfo.HeaderBlock trailers) {
215 assertEquals(mExecutorThread, Thread.currentThread());
216 assertFalse(stream.isDone());
217 assertNull(mError);
218 mTrailers = trailers;
xunjieli 2016/01/22 16:12:19 need to update mResponseStep.
mef 2016/01/22 17:36:07 Done.
219 if (maybeThrowCancelOrPause(stream, mReadStepBlock)) {
220 return;
221 }
222 }
223
224 @Override
225 public void onSucceeded(BidirectionalStream stream, UrlResponseInfo info) {
226 assertEquals(mExecutorThread, Thread.currentThread());
227 assertTrue(stream.isDone());
196 assertTrue(mResponseStep == ResponseStep.ON_RESPONSE_STARTED 228 assertTrue(mResponseStep == ResponseStep.ON_RESPONSE_STARTED
197 || mResponseStep == ResponseStep.ON_READ_COMPLETED); 229 || mResponseStep == ResponseStep.ON_READ_COMPLETED
230 || mResponseStep == ResponseStep.ON_WRITE_COMPLETED);
198 assertFalse(mOnErrorCalled); 231 assertFalse(mOnErrorCalled);
199 assertFalse(mOnCanceledCalled); 232 assertFalse(mOnCanceledCalled);
200 assertNull(mError); 233 assertNull(mError);
201 234
202 mResponseStep = ResponseStep.ON_SUCCEEDED; 235 mResponseStep = ResponseStep.ON_SUCCEEDED;
203 mResponseInfo = info; 236 mResponseInfo = info;
204 openDone(); 237 openDone();
205 maybeThrowCancelOrPause(request); 238 maybeThrowCancelOrPause(stream, mReadStepBlock);
206 } 239 }
207 240
208 @Override 241 @Override
209 public void onFailed(UrlRequest request, UrlResponseInfo info, UrlRequestExc eption error) { 242 public void onFailed(BidirectionalStream stream, UrlResponseInfo info, Crone tException error) {
210 assertEquals(mExecutorThread, Thread.currentThread()); 243 assertEquals(mExecutorThread, Thread.currentThread());
211 assertTrue(request.isDone()); 244 assertTrue(stream.isDone());
212 // Shouldn't happen after success. 245 // Shouldn't happen after success.
213 assertTrue(mResponseStep != ResponseStep.ON_SUCCEEDED); 246 assertTrue(mResponseStep != ResponseStep.ON_SUCCEEDED);
214 // Should happen at most once for a single request. 247 // Should happen at most once for a single request.
xunjieli 2016/01/22 16:12:19 nit: s/request/stream
mef 2016/01/22 17:36:07 Done.
215 assertFalse(mOnErrorCalled); 248 assertFalse(mOnErrorCalled);
216 assertFalse(mOnCanceledCalled); 249 assertFalse(mOnCanceledCalled);
217 assertNull(mError); 250 assertNull(mError);
251 mResponseStep = ResponseStep.ON_FAILED;
218 252
219 mOnErrorCalled = true; 253 mOnErrorCalled = true;
220 mError = error; 254 mError = error;
221 openDone(); 255 openDone();
222 maybeThrowCancelOrPause(request); 256 maybeThrowCancelOrPause(stream, mReadStepBlock);
223 } 257 }
224 258
225 @Override 259 @Override
226 public void onCanceled(UrlRequest request, UrlResponseInfo info) { 260 public void onCanceled(BidirectionalStream stream, UrlResponseInfo info) {
227 assertEquals(mExecutorThread, Thread.currentThread()); 261 assertEquals(mExecutorThread, Thread.currentThread());
228 assertTrue(request.isDone()); 262 assertTrue(stream.isDone());
229 // Should happen at most once for a single request. 263 // Should happen at most once for a single request.
xunjieli 2016/01/22 16:12:19 nit: s/request/stream
mef 2016/01/22 17:36:07 Done.
230 assertFalse(mOnCanceledCalled); 264 assertFalse(mOnCanceledCalled);
231 assertFalse(mOnErrorCalled); 265 assertFalse(mOnErrorCalled);
232 assertNull(mError); 266 assertNull(mError);
267 mResponseStep = ResponseStep.ON_CANCELED;
233 268
234 mOnCanceledCalled = true; 269 mOnCanceledCalled = true;
235 openDone(); 270 openDone();
236 maybeThrowCancelOrPause(request); 271 maybeThrowCancelOrPause(stream, mReadStepBlock);
237 } 272 }
238 273
239 public void startNextRead(UrlRequest request) { 274 public void startNextRead(BidirectionalStream stream) {
240 startNextRead(request, ByteBuffer.allocateDirect(READ_BUFFER_SIZE)); 275 startNextRead(stream, ByteBuffer.allocateDirect(READ_BUFFER_SIZE));
241 } 276 }
242 277
243 public void startNextRead(UrlRequest request, ByteBuffer buffer) { 278 public void startNextRead(BidirectionalStream stream, ByteBuffer buffer) {
244 mBufferPositionBeforeRead = buffer.position(); 279 mBufferPositionBeforeRead = buffer.position();
245 request.readNew(buffer); 280 stream.read(buffer);
281 }
282
283 public void startNextWrite(BidirectionalStream stream) {
284 if (!mWriteBuffers.isEmpty()) {
285 boolean isLastBuffer = mWriteBuffers.size() == 1;
286 stream.write(mWriteBuffers.get(0), isLastBuffer);
287 }
246 } 288 }
247 289
248 public boolean isDone() { 290 public boolean isDone() {
249 // It's not mentioned by the Android docs, but block(0) seems to block 291 // It's not mentioned by the Android docs, but block(0) seems to block
250 // indefinitely, so have to block for one millisecond to get state 292 // indefinitely, so have to block for one millisecond to get state
251 // without blocking. 293 // without blocking.
252 return mDone.block(1); 294 return mDone.block(1);
253 } 295 }
254 296
255 protected void openDone() { 297 protected void openDone() {
256 mDone.open(); 298 mDone.open();
257 } 299 }
258 300
259 /** 301 /**
260 * Returns {@code false} if the listener should continue to advance the 302 * Returns {@code false} if the listener should continue to advance the
261 * request. 303 * request.
xunjieli 2016/01/22 16:12:19 nit: s/request/stream
mef 2016/01/22 17:36:07 Done.
262 */ 304 */
263 private boolean maybeThrowCancelOrPause(final UrlRequest request) { 305 private boolean maybeThrowCancelOrPause(
306 final BidirectionalStream stream, ConditionVariable stepBlock) {
264 if (mResponseStep != mFailureStep || mFailureType == FailureType.NONE) { 307 if (mResponseStep != mFailureStep || mFailureType == FailureType.NONE) {
265 if (!mAutoAdvance) { 308 if (!mAutoAdvance) {
266 mStepBlock.open(); 309 stepBlock.open();
267 return true; 310 return true;
268 } 311 }
269 return false; 312 return false;
270 } 313 }
271 314
272 if (mFailureType == FailureType.THROW_SYNC) { 315 if (mFailureType == FailureType.THROW_SYNC) {
273 throw new IllegalStateException("Listener Exception."); 316 throw new IllegalStateException("Callback Exception.");
274 } 317 }
275 Runnable task = new Runnable() { 318 Runnable task = new Runnable() {
276 public void run() { 319 public void run() {
277 request.cancel(); 320 stream.cancel();
278 } 321 }
279 }; 322 };
280 if (mFailureType == FailureType.CANCEL_ASYNC 323 if (mFailureType == FailureType.CANCEL_ASYNC
281 || mFailureType == FailureType.CANCEL_ASYNC_WITHOUT_PAUSE) { 324 || mFailureType == FailureType.CANCEL_ASYNC_WITHOUT_PAUSE) {
282 getExecutor().execute(task); 325 getExecutor().execute(task);
283 } else { 326 } else {
284 task.run(); 327 task.run();
285 } 328 }
286 return mFailureType != FailureType.CANCEL_ASYNC_WITHOUT_PAUSE; 329 return mFailureType != FailureType.CANCEL_ASYNC_WITHOUT_PAUSE;
287 } 330 }
288 } 331 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698