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

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

Powered by Google App Engine
This is Rietveld 408576698