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

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: More Helen's and Andrei'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 stream
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 the stream is done either successfully or not.
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 stream after posting
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
155 || mResponseStep == ResponseStep.ON_WRITE_COMPLETED);
146 assertNull(mError); 156 assertNull(mError);
147 157
148 mResponseStep = ResponseStep.ON_RESPONSE_STARTED; 158 mResponseStep = ResponseStep.ON_RESPONSE_STARTED;
149 mResponseInfo = info; 159 mResponseInfo = info;
150 if (maybeThrowCancelOrPause(request)) { 160 if (maybeThrowCancelOrPause(stream, mReadStepBlock)) {
151 return; 161 return;
152 } 162 }
153 startNextRead(request); 163 startNextRead(stream);
154 } 164 }
155 165
156 @Override 166 @Override
157 public void onReadCompleted(UrlRequest request, UrlResponseInfo info, ByteBu ffer byteBuffer) { 167 public void onReadCompleted(
168 BidirectionalStream stream, UrlResponseInfo info, ByteBuffer byteBuf fer) {
158 assertEquals(mExecutorThread, Thread.currentThread()); 169 assertEquals(mExecutorThread, Thread.currentThread());
159 assertFalse(request.isDone()); 170 assertFalse(stream.isDone());
160 assertTrue(mResponseStep == ResponseStep.ON_RESPONSE_STARTED 171 assertTrue(mResponseStep == ResponseStep.ON_RESPONSE_STARTED
161 || mResponseStep == ResponseStep.ON_READ_COMPLETED); 172 || mResponseStep == ResponseStep.ON_READ_COMPLETED
173 || mResponseStep == ResponseStep.ON_WRITE_COMPLETED);
162 assertNull(mError); 174 assertNull(mError);
163 175
164 mResponseStep = ResponseStep.ON_READ_COMPLETED; 176 mResponseStep = ResponseStep.ON_READ_COMPLETED;
165 177
166 final byte[] lastDataReceivedAsBytes; 178 final byte[] lastDataReceivedAsBytes;
167 if (mLegacyReadByteBufferAdjustment) { 179 final int bytesRead = byteBuffer.position() - mBufferPositionBeforeRead;
168 // Make a slice of ByteBuffer, so can read from it without affecting 180 mHttpResponseDataLength += bytesRead;
169 // position, which allows tests to check the state of the buffer. 181 lastDataReceivedAsBytes = new byte[bytesRead];
170 ByteBuffer slice = byteBuffer.slice(); 182 // Rewind byteBuffer.position() to pre-read() position.
171 mHttpResponseDataLength += slice.remaining(); 183 byteBuffer.position(mBufferPositionBeforeRead);
172 lastDataReceivedAsBytes = new byte[slice.remaining()]; 184 // This restores byteBuffer.position() to its value on entrance to
173 slice.get(lastDataReceivedAsBytes); 185 // this function.
174 } else { 186 byteBuffer.get(lastDataReceivedAsBytes);
175 final int bytesRead = byteBuffer.position() - mBufferPositionBeforeR ead; 187
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); 188 mResponseAsString += new String(lastDataReceivedAsBytes);
185 189
186 if (maybeThrowCancelOrPause(request)) { 190 if (maybeThrowCancelOrPause(stream, mReadStepBlock)) {
187 return; 191 return;
188 } 192 }
189 startNextRead(request); 193 startNextRead(stream);
190 } 194 }
191 195
192 @Override 196 @Override
193 public void onSucceeded(UrlRequest request, UrlResponseInfo info) { 197 public void onWriteCompleted(
198 BidirectionalStream stream, UrlResponseInfo info, ByteBuffer buffer) {
194 assertEquals(mExecutorThread, Thread.currentThread()); 199 assertEquals(mExecutorThread, Thread.currentThread());
195 assertTrue(request.isDone()); 200 assertFalse(stream.isDone());
201 assertNull(mError);
202 mResponseStep = ResponseStep.ON_WRITE_COMPLETED;
203 if (!mWriteBuffers.isEmpty()) {
204 assertEquals(buffer, mWriteBuffers.get(0));
205 mWriteBuffers.remove(0);
206 }
207 if (maybeThrowCancelOrPause(stream, mWriteStepBlock)) {
208 return;
209 }
210 startNextWrite(stream);
211 }
212
213 @Override
214 public void onResponseTrailersReceived(BidirectionalStream stream, UrlRespon seInfo info,
215 UrlResponseInfo.HeaderBlock trailers) {
216 assertEquals(mExecutorThread, Thread.currentThread());
217 assertFalse(stream.isDone());
218 assertNull(mError);
219 mResponseStep = ResponseStep.ON_TRAILERS;
220 mTrailers = trailers;
221 if (maybeThrowCancelOrPause(stream, mReadStepBlock)) {
222 return;
223 }
224 }
225
226 @Override
227 public void onSucceeded(BidirectionalStream stream, UrlResponseInfo info) {
228 assertEquals(mExecutorThread, Thread.currentThread());
229 assertTrue(stream.isDone());
196 assertTrue(mResponseStep == ResponseStep.ON_RESPONSE_STARTED 230 assertTrue(mResponseStep == ResponseStep.ON_RESPONSE_STARTED
197 || mResponseStep == ResponseStep.ON_READ_COMPLETED); 231 || mResponseStep == ResponseStep.ON_READ_COMPLETED
232 || mResponseStep == ResponseStep.ON_WRITE_COMPLETED);
198 assertFalse(mOnErrorCalled); 233 assertFalse(mOnErrorCalled);
199 assertFalse(mOnCanceledCalled); 234 assertFalse(mOnCanceledCalled);
200 assertNull(mError); 235 assertNull(mError);
201 236
202 mResponseStep = ResponseStep.ON_SUCCEEDED; 237 mResponseStep = ResponseStep.ON_SUCCEEDED;
203 mResponseInfo = info; 238 mResponseInfo = info;
204 openDone(); 239 openDone();
205 maybeThrowCancelOrPause(request); 240 maybeThrowCancelOrPause(stream, mReadStepBlock);
206 } 241 }
207 242
208 @Override 243 @Override
209 public void onFailed(UrlRequest request, UrlResponseInfo info, UrlRequestExc eption error) { 244 public void onFailed(BidirectionalStream stream, UrlResponseInfo info, Crone tException error) {
210 assertEquals(mExecutorThread, Thread.currentThread()); 245 assertEquals(mExecutorThread, Thread.currentThread());
211 assertTrue(request.isDone()); 246 assertTrue(stream.isDone());
212 // Shouldn't happen after success. 247 // Shouldn't happen after success.
213 assertTrue(mResponseStep != ResponseStep.ON_SUCCEEDED); 248 assertTrue(mResponseStep != ResponseStep.ON_SUCCEEDED);
214 // Should happen at most once for a single request. 249 // Should happen at most once for a single stream.
215 assertFalse(mOnErrorCalled); 250 assertFalse(mOnErrorCalled);
216 assertFalse(mOnCanceledCalled); 251 assertFalse(mOnCanceledCalled);
217 assertNull(mError); 252 assertNull(mError);
253 mResponseStep = ResponseStep.ON_FAILED;
218 254
219 mOnErrorCalled = true; 255 mOnErrorCalled = true;
220 mError = error; 256 mError = error;
221 openDone(); 257 openDone();
222 maybeThrowCancelOrPause(request); 258 maybeThrowCancelOrPause(stream, mReadStepBlock);
223 } 259 }
224 260
225 @Override 261 @Override
226 public void onCanceled(UrlRequest request, UrlResponseInfo info) { 262 public void onCanceled(BidirectionalStream stream, UrlResponseInfo info) {
227 assertEquals(mExecutorThread, Thread.currentThread()); 263 assertEquals(mExecutorThread, Thread.currentThread());
228 assertTrue(request.isDone()); 264 assertTrue(stream.isDone());
229 // Should happen at most once for a single request. 265 // Should happen at most once for a single stream.
230 assertFalse(mOnCanceledCalled); 266 assertFalse(mOnCanceledCalled);
231 assertFalse(mOnErrorCalled); 267 assertFalse(mOnErrorCalled);
232 assertNull(mError); 268 assertNull(mError);
269 mResponseStep = ResponseStep.ON_CANCELED;
233 270
234 mOnCanceledCalled = true; 271 mOnCanceledCalled = true;
235 openDone(); 272 openDone();
236 maybeThrowCancelOrPause(request); 273 maybeThrowCancelOrPause(stream, mReadStepBlock);
237 } 274 }
238 275
239 public void startNextRead(UrlRequest request) { 276 public void startNextRead(BidirectionalStream stream) {
240 startNextRead(request, ByteBuffer.allocateDirect(READ_BUFFER_SIZE)); 277 startNextRead(stream, ByteBuffer.allocateDirect(READ_BUFFER_SIZE));
241 } 278 }
242 279
243 public void startNextRead(UrlRequest request, ByteBuffer buffer) { 280 public void startNextRead(BidirectionalStream stream, ByteBuffer buffer) {
244 mBufferPositionBeforeRead = buffer.position(); 281 mBufferPositionBeforeRead = buffer.position();
245 request.readNew(buffer); 282 stream.read(buffer);
283 }
284
285 public void startNextWrite(BidirectionalStream stream) {
286 if (!mWriteBuffers.isEmpty()) {
287 boolean isLastBuffer = mWriteBuffers.size() == 1;
288 stream.write(mWriteBuffers.get(0), isLastBuffer);
289 }
246 } 290 }
247 291
248 public boolean isDone() { 292 public boolean isDone() {
249 // It's not mentioned by the Android docs, but block(0) seems to block 293 // 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 294 // indefinitely, so have to block for one millisecond to get state
251 // without blocking. 295 // without blocking.
252 return mDone.block(1); 296 return mDone.block(1);
253 } 297 }
254 298
255 protected void openDone() { 299 protected void openDone() {
256 mDone.open(); 300 mDone.open();
257 } 301 }
258 302
259 /** 303 /**
260 * Returns {@code false} if the listener should continue to advance the 304 * Returns {@code false} if the listener should continue to advance the
261 * request. 305 * stream.
262 */ 306 */
263 private boolean maybeThrowCancelOrPause(final UrlRequest request) { 307 private boolean maybeThrowCancelOrPause(
308 final BidirectionalStream stream, ConditionVariable stepBlock) {
264 if (mResponseStep != mFailureStep || mFailureType == FailureType.NONE) { 309 if (mResponseStep != mFailureStep || mFailureType == FailureType.NONE) {
265 if (!mAutoAdvance) { 310 if (!mAutoAdvance) {
266 mStepBlock.open(); 311 stepBlock.open();
267 return true; 312 return true;
268 } 313 }
269 return false; 314 return false;
270 } 315 }
271 316
272 if (mFailureType == FailureType.THROW_SYNC) { 317 if (mFailureType == FailureType.THROW_SYNC) {
273 throw new IllegalStateException("Listener Exception."); 318 throw new IllegalStateException("Callback Exception.");
274 } 319 }
275 Runnable task = new Runnable() { 320 Runnable task = new Runnable() {
276 public void run() { 321 public void run() {
277 request.cancel(); 322 stream.cancel();
278 } 323 }
279 }; 324 };
280 if (mFailureType == FailureType.CANCEL_ASYNC 325 if (mFailureType == FailureType.CANCEL_ASYNC
281 || mFailureType == FailureType.CANCEL_ASYNC_WITHOUT_PAUSE) { 326 || mFailureType == FailureType.CANCEL_ASYNC_WITHOUT_PAUSE) {
282 getExecutor().execute(task); 327 getExecutor().execute(task);
283 } else { 328 } else {
284 task.run(); 329 task.run();
285 } 330 }
286 return mFailureType != FailureType.CANCEL_ASYNC_WITHOUT_PAUSE; 331 return mFailureType != FailureType.CANCEL_ASYNC_WITHOUT_PAUSE;
287 } 332 }
288 } 333 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698