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/BidirectionalStreamTest.java

Issue 1412243012: Initial implementation of CronetBidirectionalStream. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Introducing RequestResponder! Created 4 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
1 // Copyright 2015 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.test.suitebuilder.annotation.SmallTest; 8 import android.test.suitebuilder.annotation.SmallTest;
8 9
9 import org.chromium.base.test.util.Feature; 10 import org.chromium.base.test.util.Feature;
11 import org.chromium.net.CronetTestBase.OnlyRunNativeCronet;
12 import org.chromium.net.TestBidirectionalStreamCallback.FailureType;
13 import org.chromium.net.TestBidirectionalStreamCallback.ResponseStep;
10 14
11 import java.nio.ByteBuffer; 15 import java.nio.ByteBuffer;
12 import java.util.concurrent.Executor; 16 import java.util.AbstractMap;
13 import java.util.concurrent.ExecutorService; 17 import java.util.ArrayList;
14 import java.util.concurrent.Executors; 18 import java.util.Arrays;
15 import java.util.concurrent.ThreadFactory; 19 import java.util.List;
20 import java.util.Map;
21 import java.util.regex.Matcher;
22 import java.util.regex.Pattern;
16 23
17 /** 24 /**
18 * Test functionality of BidirectionalStream interface. 25 * Test functionality of BidirectionalStream interface.
19 */ 26 */
20 public class BidirectionalStreamTest extends CronetTestBase { 27 public class BidirectionalStreamTest extends CronetTestBase {
21 private CronetTestFramework mTestFramework; 28 private CronetTestFramework mTestFramework;
22 29
23 @Override 30 @Override
24 protected void setUp() throws Exception { 31 protected void setUp() throws Exception {
25 super.setUp(); 32 super.setUp();
26 mTestFramework = startCronetTestFramework(); 33 // Load library first to create MockCertVerifier.
27 assertTrue(NativeTestServer.startNativeTestServer(getContext())); 34 System.loadLibrary("cronet_tests");
28 // Add url interceptors after native application context is initialized. 35 CronetEngine.Builder builder = new CronetEngine.Builder(getContext());
29 MockUrlRequestJobFactory.setUp(); 36 builder.setMockCertVerifierForTesting(QuicTestServer.createMockCertVerif ier());
37
38 mTestFramework = startCronetTestFrameworkWithUrlAndCronetEngineBuilder(n ull, builder);
39 assertTrue(Http2TestServer.startHttp2TestServer(
40 getContext(), QuicTestServer.getServerCert(), QuicTestServer.get ServerCertKey()));
30 } 41 }
31 42
32 @Override 43 @Override
33 protected void tearDown() throws Exception { 44 protected void tearDown() throws Exception {
34 NativeTestServer.shutdownNativeTestServer(); 45 assertTrue(Http2TestServer.shutdownHttp2TestServer());
35 mTestFramework.mCronetEngine.shutdown(); 46 if (mTestFramework.mCronetEngine != null) {
47 mTestFramework.mCronetEngine.shutdown();
48 }
36 super.tearDown(); 49 super.tearDown();
37 } 50 }
38 51
39 private class TestBidirectionalStreamCallback extends BidirectionalStream.Ca llback { 52 private static void checkResponseInfo(UrlResponseInfo responseInfo, String e xpectedUrl,
40 // Executor Service for Cronet callbacks. 53 int expectedHttpStatusCode, String expectedHttpStatusText) {
41 private final ExecutorService mExecutorService = 54 assertEquals(expectedUrl, responseInfo.getUrl());
42 Executors.newSingleThreadExecutor(new ExecutorThreadFactory()); 55 assertEquals(
43 private Thread mExecutorThread; 56 expectedUrl, responseInfo.getUrlChain().get(responseInfo.getUrlC hain().size() - 1));
57 assertEquals(expectedHttpStatusCode, responseInfo.getHttpStatusCode());
58 assertEquals(expectedHttpStatusText, responseInfo.getHttpStatusText());
59 assertFalse(responseInfo.wasCached());
60 assertTrue(responseInfo.toString().length() > 0);
61 }
44 62
45 private class ExecutorThreadFactory implements ThreadFactory { 63 private static String createLongString(String base, int repetition) {
46 public Thread newThread(Runnable r) { 64 StringBuilder builder = new StringBuilder(base.length() * repetition);
47 mExecutorThread = new Thread(r); 65 for (int i = 0; i < repetition; ++i) {
48 return mExecutorThread; 66 builder.append(i);
49 } 67 builder.append(base);
50 } 68 }
69 return builder.toString();
70 }
51 71
52 @Override 72 private static UrlResponseInfo createUrlResponseInfo(
53 public void onRequestHeadersSent(BidirectionalStream stream) {} 73 String[] urls, String message, int statusCode, int receivedBytes, St ring... headers) {
54 74 ArrayList<Map.Entry<String, String>> headersList = new ArrayList<>();
55 @Override 75 for (int i = 0; i < headers.length; i += 2) {
56 public void onResponseHeadersReceived(BidirectionalStream stream, UrlRes ponseInfo info) {} 76 headersList.add(new AbstractMap.SimpleImmutableEntry<String, String> (
57 77 headers[i], headers[i + 1]));
58 @Override
59 public void onReadCompleted(
60 BidirectionalStream stream, UrlResponseInfo info, ByteBuffer buf fer) {}
61
62 @Override
63 public void onWriteCompleted(
64 BidirectionalStream stream, UrlResponseInfo info, ByteBuffer buf fer) {}
65
66 @Override
67 public void onResponseTrailersReceived(BidirectionalStream stream, UrlRe sponseInfo info,
68 UrlResponseInfo.HeaderBlock trailers) {}
69
70 @Override
71 public void onSucceeded(BidirectionalStream stream, UrlResponseInfo info ) {}
72
73 @Override
74 public void onFailed(
75 BidirectionalStream stream, UrlResponseInfo info, CronetExceptio n error) {}
76
77 @Override
78 public void onCanceled(BidirectionalStream stream, UrlResponseInfo info) {}
79
80 Executor getExecutor() {
81 return mExecutorService;
82 } 78 }
79 UrlResponseInfo urlResponseInfo = new UrlResponseInfo(
80 Arrays.asList(urls), statusCode, message, headersList, false, "h 2", null);
81 urlResponseInfo.setReceivedBytesCount(receivedBytes);
82 return urlResponseInfo;
83 } 83 }
84 84
85 @SmallTest 85 @SmallTest
86 @Feature({"Cronet"}) 86 @Feature({"Cronet"})
87 public void testBuilderChecks() throws Exception { 87 public void testBuilderChecks() throws Exception {
88 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback(); 88 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback();
89 try { 89 try {
90 new BidirectionalStream.Builder( 90 new BidirectionalStream.Builder(
91 null, callback, callback.getExecutor(), mTestFramework.mCron etEngine); 91 null, callback, callback.getExecutor(), mTestFramework.mCron etEngine);
92 fail("URL not null-checked"); 92 fail("URL not null-checked");
93 } catch (NullPointerException e) { 93 } catch (NullPointerException e) {
94 assertEquals("URL is required.", e.getMessage()); 94 assertEquals("URL is required.", e.getMessage());
95 } 95 }
96 try { 96 try {
97 new BidirectionalStream.Builder(NativeTestServer.getRedirectURL(), n ull, 97 new BidirectionalStream.Builder(Http2TestServer.getServerUrl(), null ,
98 callback.getExecutor(), mTestFramework.mCronetEngine); 98 callback.getExecutor(), mTestFramework.mCronetEngine);
99 fail("Callback not null-checked"); 99 fail("Callback not null-checked");
100 } catch (NullPointerException e) { 100 } catch (NullPointerException e) {
101 assertEquals("Callback is required.", e.getMessage()); 101 assertEquals("Callback is required.", e.getMessage());
102 } 102 }
103 try { 103 try {
104 new BidirectionalStream.Builder(NativeTestServer.getRedirectURL(), c allback, null, 104 new BidirectionalStream.Builder(
105 mTestFramework.mCronetEngine); 105 Http2TestServer.getServerUrl(), callback, null, mTestFramewo rk.mCronetEngine);
106 fail("Executor not null-checked"); 106 fail("Executor not null-checked");
107 } catch (NullPointerException e) { 107 } catch (NullPointerException e) {
108 assertEquals("Executor is required.", e.getMessage()); 108 assertEquals("Executor is required.", e.getMessage());
109 } 109 }
110 try { 110 try {
111 new BidirectionalStream.Builder( 111 new BidirectionalStream.Builder(
112 NativeTestServer.getRedirectURL(), callback, callback.getExe cutor(), null); 112 Http2TestServer.getServerUrl(), callback, callback.getExecut or(), null);
113 fail("CronetEngine not null-checked"); 113 fail("CronetEngine not null-checked");
114 } catch (NullPointerException e) { 114 } catch (NullPointerException e) {
115 assertEquals("CronetEngine is required.", e.getMessage()); 115 assertEquals("CronetEngine is required.", e.getMessage());
116 } 116 }
117 // Verify successful creation doesn't throw. 117 // Verify successful creation doesn't throw.
118 new BidirectionalStream.Builder(NativeTestServer.getRedirectURL(), callb ack, 118 BidirectionalStream.Builder builder =
119 callback.getExecutor(), mTestFramework.mCronetEngine); 119 new BidirectionalStream.Builder(Http2TestServer.getServerUrl(), callback,
120 callback.getExecutor(), mTestFramework.mCronetEngine);
121 try {
122 builder.addHeader(null, "value");
123 fail("Header name is not null-checked");
124 } catch (NullPointerException e) {
125 assertEquals("Invalid header name.", e.getMessage());
126 }
127 try {
128 builder.addHeader("name", null);
129 fail("Header value is not null-checked");
130 } catch (NullPointerException e) {
131 assertEquals("Invalid header value.", e.getMessage());
132 }
133 try {
134 builder.setHttpMethod(null);
135 fail("Method name is not null-checked");
136 } catch (NullPointerException e) {
137 assertEquals("Method is required.", e.getMessage());
138 }
139 }
140
141 @SmallTest
142 @Feature({"Cronet"})
143 @OnlyRunNativeCronet
144 public void testFailPlainHttp() throws Exception {
145 String url = "http://example.com";
146 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback();
147 // Create stream.
148 BidirectionalStream stream = new BidirectionalStream
149 .Builder(url, callback, callback.ge tExecutor(),
150 mTestFramework.mCronetEngin e)
151 .build();
152 stream.start();
153 callback.blockForDone();
154 assertTrue(stream.isDone());
155 assertEquals("Exception in BidirectionalStream: net::ERR_DISALLOWED_URL_ SCHEME",
156 callback.mError.getMessage());
157 }
158
159 @SmallTest
160 @Feature({"Cronet"})
161 @OnlyRunNativeCronet
162 public void testSimpleGet() throws Exception {
163 String url = Http2TestServer.getEchoMethodUrl();
164 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback();
165 // Create stream.
166 BidirectionalStream stream = new BidirectionalStream
167 .Builder(url, callback, callback.ge tExecutor(),
168 mTestFramework.mCronetEngin e)
169 .setHttpMethod("GET")
170 .build();
171 stream.start();
172 callback.blockForDone();
173 assertTrue(stream.isDone());
174 assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
175 // Default method is 'GET'.
176 assertEquals("GET", callback.mResponseAsString);
177 UrlResponseInfo urlResponseInfo =
178 createUrlResponseInfo(new String[] {url}, "", 200, 27, ":status" , "200");
179 assertResponseEquals(urlResponseInfo, callback.mResponseInfo);
180 checkResponseInfo(callback.mResponseInfo, Http2TestServer.getEchoMethodU rl(), 200, "");
181 }
182
183 @SmallTest
184 @Feature({"Cronet"})
185 @OnlyRunNativeCronet
186 public void testSimpleHead() throws Exception {
187 String url = Http2TestServer.getEchoMethodUrl();
188 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback();
189 // Create stream.
190 BidirectionalStream stream = new BidirectionalStream
191 .Builder(url, callback, callback.ge tExecutor(),
192 mTestFramework.mCronetEngin e)
193 .setHttpMethod("HEAD")
194 .build();
195 stream.start();
196 callback.blockForDone();
197 assertTrue(stream.isDone());
198 assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
199 assertEquals("HEAD", callback.mResponseAsString);
200 UrlResponseInfo urlResponseInfo =
201 createUrlResponseInfo(new String[] {url}, "", 200, 28, ":status" , "200");
202 assertResponseEquals(urlResponseInfo, callback.mResponseInfo);
203 checkResponseInfo(callback.mResponseInfo, Http2TestServer.getEchoMethodU rl(), 200, "");
204 }
205
206 @SmallTest
207 @Feature({"Cronet"})
208 @OnlyRunNativeCronet
209 public void testSimplePost() throws Exception {
210 String url = Http2TestServer.getEchoStreamUrl();
211 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback();
212 callback.addWriteData("Test String".getBytes());
213 callback.addWriteData("1234567890".getBytes());
214 callback.addWriteData("woot!".getBytes());
215 // Create stream.
216 BidirectionalStream stream = new BidirectionalStream
217 .Builder(url, callback, callback.ge tExecutor(),
218 mTestFramework.mCronetEngin e)
219 .addHeader("foo", "bar")
220 .addHeader("empty", "")
221 .addHeader("Content-Type", "zebra")
222 .build();
223 stream.start();
224 callback.blockForDone();
225 assertTrue(stream.isDone());
226 assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
227 assertEquals("Test String1234567890woot!", callback.mResponseAsString);
228 assertEquals("bar", callback.mResponseInfo.getAllHeaders().get("echo-foo ").get(0));
229 assertEquals("", callback.mResponseInfo.getAllHeaders().get("echo-empty" ).get(0));
230 assertEquals(
231 "zebra", callback.mResponseInfo.getAllHeaders().get("echo-conten t-type").get(0));
232 }
233
234 @SmallTest
235 @Feature({"Cronet"})
236 @OnlyRunNativeCronet
237 public void testSimplePut() throws Exception {
238 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback();
239 callback.addWriteData("Put This Data!".getBytes());
240 String methodName = "PUT";
241 BidirectionalStream.Builder builder =
242 new BidirectionalStream.Builder(Http2TestServer.getServerUrl(), callback,
243 callback.getExecutor(), mTestFramework.mCronetEngine);
244 builder.setHttpMethod(methodName);
245 builder.build().start();
246 callback.blockForDone();
247 assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
248 assertEquals("Put This Data!", callback.mResponseAsString);
249 assertEquals(methodName, callback.mResponseInfo.getAllHeaders().get("ech o-method").get(0));
250 }
251
252 @SmallTest
253 @Feature({"Cronet"})
254 @OnlyRunNativeCronet
255 public void testBadMethod() throws Exception {
256 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback();
257 BidirectionalStream.Builder builder =
258 new BidirectionalStream.Builder(Http2TestServer.getServerUrl(), callback,
259 callback.getExecutor(), mTestFramework.mCronetEngine);
260 try {
261 builder.setHttpMethod("bad:method!");
262 builder.build().start();
263 fail("IllegalArgumentException not thrown.");
264 } catch (IllegalArgumentException e) {
265 assertEquals("Invalid http method bad:method!", e.getMessage());
266 }
267 }
268
269 @SmallTest
270 @Feature({"Cronet"})
271 @OnlyRunNativeCronet
272 public void testBadHeaderName() throws Exception {
273 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback();
274 BidirectionalStream.Builder builder =
275 new BidirectionalStream.Builder(Http2TestServer.getServerUrl(), callback,
276 callback.getExecutor(), mTestFramework.mCronetEngine);
277 try {
278 builder.addHeader("goodheader1", "headervalue");
279 builder.addHeader("header:name", "headervalue");
280 builder.addHeader("goodheader2", "headervalue");
281 builder.build().start();
282 fail("IllegalArgumentException not thrown.");
283 } catch (IllegalArgumentException e) {
284 assertEquals("Invalid header header:name=headervalue", e.getMessage( ));
285 }
286 }
287
288 @SmallTest
289 @Feature({"Cronet"})
290 @OnlyRunNativeCronet
291 public void testBadHeaderValue() throws Exception {
292 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback();
293 BidirectionalStream.Builder builder =
294 new BidirectionalStream.Builder(Http2TestServer.getServerUrl(), callback,
295 callback.getExecutor(), mTestFramework.mCronetEngine);
296 try {
297 builder.addHeader("headername", "bad header\r\nvalue");
298 builder.build().start();
299 fail("IllegalArgumentException not thrown.");
300 } catch (IllegalArgumentException e) {
301 assertEquals("Invalid header headername=bad header\r\nvalue", e.getM essage());
302 }
303 }
304
305 @SmallTest
306 @Feature({"Cronet"})
307 @OnlyRunNativeCronet
308 public void testAddHeader() throws Exception {
309 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback();
310 String headerName = "header-name";
311 String headerValue = "header-value";
312 BidirectionalStream.Builder builder =
313 new BidirectionalStream.Builder(Http2TestServer.getEchoHeaderUrl (headerName),
314 callback, callback.getExecutor(), mTestFramework.mCronet Engine);
315 builder.addHeader(headerName, headerValue);
316 builder.setHttpMethod("GET");
317 builder.build().start();
318 callback.blockForDone();
319 assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
320 assertEquals(headerValue, callback.mResponseAsString);
321 }
322
323 @SmallTest
324 @Feature({"Cronet"})
325 @OnlyRunNativeCronet
326 public void testMultiRequestHeaders() throws Exception {
327 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback();
328 String headerName = "header-name";
329 String headerValue1 = "header-value1";
330 String headerValue2 = "header-value2";
331 BidirectionalStream.Builder builder =
332 new BidirectionalStream.Builder(Http2TestServer.getEchoAllHeader sUrl(), callback,
333 callback.getExecutor(), mTestFramework.mCronetEngine);
334 builder.addHeader(headerName, headerValue1);
335 builder.addHeader(headerName, headerValue2);
336 builder.setHttpMethod("GET");
337 builder.build().start();
338 callback.blockForDone();
339 assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
340 String headers = callback.mResponseAsString;
341 Pattern pattern = Pattern.compile(headerName + ":\\s(.*)\\r\\n");
342 Matcher matcher = pattern.matcher(headers);
343 List<String> actualValues = new ArrayList<String>();
344 while (matcher.find()) {
345 actualValues.add(matcher.group(1));
346 }
347 assertEquals(1, actualValues.size());
348 assertEquals("header-value2", actualValues.get(0));
349 }
350
351 @SmallTest
352 @Feature({"Cronet"})
353 @OnlyRunNativeCronet
354 public void testEchoTrailers() throws Exception {
355 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback();
356 String headerName = "header-name";
357 String headerValue = "header-value";
358 BidirectionalStream.Builder builder =
359 new BidirectionalStream.Builder(Http2TestServer.getEchoTrailersU rl(), callback,
360 callback.getExecutor(), mTestFramework.mCronetEngine);
361 builder.addHeader(headerName, headerValue);
362 builder.setHttpMethod("GET");
363 builder.build().start();
364 callback.blockForDone();
365 assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
366 assertNotNull(callback.mTrailers);
367 // Verify that header value is properly echoed in trailers.
368 assertEquals(headerValue, callback.mTrailers.getAsMap().get("echo-" + he aderName).get(0));
369 }
370
371 @SmallTest
372 @Feature({"Cronet"})
373 @OnlyRunNativeCronet
374 public void testCustomUserAgent() throws Exception {
375 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback();
376 String userAgentName = "User-Agent";
377 String userAgentValue = "User-Agent-Value";
378 BidirectionalStream.Builder builder =
379 new BidirectionalStream.Builder(Http2TestServer.getEchoHeaderUrl (userAgentName),
380 callback, callback.getExecutor(), mTestFramework.mCronet Engine);
381 builder.setHttpMethod("GET");
382 builder.addHeader(userAgentName, userAgentValue);
383 builder.build().start();
384 callback.blockForDone();
385 assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
386 assertEquals(userAgentValue, callback.mResponseAsString);
387 }
388
389 @SmallTest
390 @Feature({"Cronet"})
391 @OnlyRunNativeCronet
392 public void testEchoStream() throws Exception {
393 String url = Http2TestServer.getEchoStreamUrl();
394 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback();
395 String[] testData = {"Test String", createLongString("1234567890", 50000 ), "woot!"};
396 StringBuilder stringData = new StringBuilder();
397 for (String writeData : testData) {
398 callback.addWriteData(writeData.getBytes());
399 stringData.append(writeData);
400 }
401 // Create stream.
402 BidirectionalStream stream = new BidirectionalStream
403 .Builder(url, callback, callback.ge tExecutor(),
404 mTestFramework.mCronetEngin e)
405 .addHeader("foo", "Value with Space s")
406 .addHeader("Content-Type", "zebra")
407 .build();
408 stream.start();
409 callback.blockForDone();
410 assertTrue(stream.isDone());
411 assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
412 assertEquals(stringData.toString(), callback.mResponseAsString);
413 assertEquals(
414 "Value with Spaces", callback.mResponseInfo.getAllHeaders().get( "echo-foo").get(0));
415 assertEquals(
416 "zebra", callback.mResponseInfo.getAllHeaders().get("echo-conten t-type").get(0));
417 }
418
419 @SmallTest
420 @Feature({"Cronet"})
421 @OnlyRunNativeCronet
422 public void testEchoStreamEmptyWrite() throws Exception {
423 String url = Http2TestServer.getEchoStreamUrl();
424 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback();
425 callback.addWriteData(new byte[0]);
426 // Create stream.
427 BidirectionalStream stream = new BidirectionalStream
428 .Builder(url, callback, callback.ge tExecutor(),
429 mTestFramework.mCronetEngin e)
430 .build();
431 stream.start();
432 callback.blockForDone();
433 assertTrue(stream.isDone());
434 assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
435 assertEquals("", callback.mResponseAsString);
436 }
437
438 @SmallTest
439 @Feature({"Cronet"})
440 @OnlyRunNativeCronet
441 public void testDoubleWrite() throws Exception {
442 String url = Http2TestServer.getEchoStreamUrl();
443 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback() {
444 @Override
445 public void onRequestHeadersSent(BidirectionalStream stream) {
446 startNextWrite(stream);
447 try {
448 // Second write from callback invoked on single-threaded exe cutor throws
449 // an exception because first write is still pending until i ts completion
450 // is handled on executor.
451 ByteBuffer writeBuffer = ByteBuffer.allocateDirect(5);
452 writeBuffer.put("abc".getBytes());
453 writeBuffer.flip();
454 stream.write(writeBuffer, false);
455 fail("Exception is not thrown.");
456 } catch (Exception e) {
457 assertEquals("Unexpected write attempt.", e.getMessage());
458 }
459 }
460 };
461 callback.addWriteData("1".getBytes());
462 callback.addWriteData("2".getBytes());
463 // Create stream.
464 BidirectionalStream stream = new BidirectionalStream
465 .Builder(url, callback, callback.ge tExecutor(),
466 mTestFramework.mCronetEngin e)
467 .build();
468 stream.start();
469 callback.blockForDone();
470 assertTrue(stream.isDone());
471 assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
472 assertEquals("12", callback.mResponseAsString);
473 }
474
475 @SmallTest
476 @Feature({"Cronet"})
477 @OnlyRunNativeCronet
478 public void testDoubleRead() throws Exception {
479 String url = Http2TestServer.getEchoStreamUrl();
480 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback() {
481 @Override
482 public void onResponseHeadersReceived(
483 BidirectionalStream stream, UrlResponseInfo info) {
484 startNextRead(stream);
485 try {
486 // Second read from callback invoked on single-threaded exec utor throws
487 // an exception because previous read is still pending until its completion
488 // is handled on executor.
489 stream.read(ByteBuffer.allocateDirect(5));
490 fail("Exception is not thrown.");
491 } catch (Exception e) {
492 assertEquals("Unexpected read attempt.", e.getMessage());
493 }
494 }
495 };
496 callback.addWriteData("1".getBytes());
497 callback.addWriteData("2".getBytes());
498 // Create stream.
499 BidirectionalStream stream = new BidirectionalStream
500 .Builder(url, callback, callback.ge tExecutor(),
501 mTestFramework.mCronetEngin e)
502 .build();
503 stream.start();
504 callback.blockForDone();
505 assertTrue(stream.isDone());
506 assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
507 assertEquals("12", callback.mResponseAsString);
508 }
509
510 @SmallTest
511 @Feature({"Cronet"})
512 @OnlyRunNativeCronet
513 public void testReadAndWrite() throws Exception {
514 String url = Http2TestServer.getEchoStreamUrl();
515 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback() {
516 @Override
517 public void onResponseHeadersReceived(
518 BidirectionalStream stream, UrlResponseInfo info) {
519 // Start the write, that will not complete until callback comple tion.
520 startNextWrite(stream);
521 // Start the read. It is allowed with write in flight.
522 super.onResponseHeadersReceived(stream, info);
523 }
524 };
525 callback.setAutoAdvance(false);
526 callback.addWriteData("1".getBytes());
527 callback.addWriteData("2".getBytes());
528 // Create stream.
529 BidirectionalStream stream = new BidirectionalStream
530 .Builder(url, callback, callback.ge tExecutor(),
531 mTestFramework.mCronetEngin e)
532 .build();
533 stream.start();
534 callback.waitForNextWriteStep();
535 callback.waitForNextReadStep();
536 callback.startNextRead(stream);
537 callback.setAutoAdvance(true);
538 callback.blockForDone();
539 assertTrue(stream.isDone());
540 assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
541 assertEquals("12", callback.mResponseAsString);
542 }
543
544 @SmallTest
545 @Feature({"Cronet"})
546 @OnlyRunNativeCronet
547 public void testEchoStreamWriteFirst() throws Exception {
548 String url = Http2TestServer.getEchoStreamUrl();
549 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback();
550 callback.setAutoAdvance(false);
551 String[] testData = {"a", "bb", "ccc", "Test String", "1234567890", "woo t!"};
552 StringBuilder stringData = new StringBuilder();
553 for (String writeData : testData) {
554 callback.addWriteData(writeData.getBytes());
555 stringData.append(writeData);
556 }
557 // Create stream.
558 BidirectionalStream stream = new BidirectionalStream
559 .Builder(url, callback, callback.ge tExecutor(),
560 mTestFramework.mCronetEngin e)
561 .build();
562 stream.start();
563 // Write first.
564 callback.waitForNextWriteStep();
565 for (String expected : testData) {
566 // Write next chunk of test data.
567 callback.startNextWrite(stream);
568 callback.waitForNextWriteStep();
569 }
570
571 // Read back.
572 callback.waitForNextReadStep();
573 assertEquals("", callback.mResponseAsString);
xunjieli 2016/01/29 15:08:04 This is a bit surprising. Will this be flaky if th
mef 2016/01/29 15:46:24 That's the point. waitForNextReadStep() just waits
574 callback.setAutoAdvance(true);
575 callback.startNextRead(stream);
576 callback.blockForDone();
577 assertTrue(stream.isDone());
578 assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
579 assertEquals(stringData.toString(), callback.mResponseAsString);
580 }
581
582 @SmallTest
583 @Feature({"Cronet"})
584 @OnlyRunNativeCronet
585 public void testEchoStreamStepByStep() throws Exception {
586 String url = Http2TestServer.getEchoStreamUrl();
587 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback();
588 callback.setAutoAdvance(false);
589 String[] testData = {"a", "bb", "ccc", "Test String", "1234567890", "woo t!"};
590 StringBuilder stringData = new StringBuilder();
591 for (String writeData : testData) {
592 callback.addWriteData(writeData.getBytes());
593 stringData.append(writeData);
594 }
595 // Create stream.
596 BidirectionalStream stream = new BidirectionalStream
597 .Builder(url, callback, callback.ge tExecutor(),
598 mTestFramework.mCronetEngin e)
599 .build();
600 stream.start();
601 callback.waitForNextWriteStep();
602 callback.waitForNextReadStep();
603
604 for (String expected : testData) {
605 // Write next chunk of test data.
606 callback.startNextWrite(stream);
607 callback.waitForNextWriteStep();
608
609 // Read next chunk of test data.
610 ByteBuffer readBuffer = ByteBuffer.allocateDirect(100);
611 callback.startNextRead(stream, readBuffer);
612 callback.waitForNextReadStep();
613 assertEquals(expected.length(), readBuffer.position());
614 assertFalse(stream.isDone());
615 }
616
617 callback.setAutoAdvance(true);
618 callback.startNextRead(stream);
619 callback.blockForDone();
620 assertTrue(stream.isDone());
621 assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
622 assertEquals(stringData.toString(), callback.mResponseAsString);
623 }
624
625 /**
626 * Checks that the buffer is updated correctly, when starting at an offset.
627 */
628 @SmallTest
629 @Feature({"Cronet"})
630 @OnlyRunNativeCronet
631 public void testSimpleGetBufferUpdates() throws Exception {
632 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback();
633 callback.setAutoAdvance(false);
634 // Since the method is "GET", the expected response body is also "GET".
635 BidirectionalStream.Builder builder =
636 new BidirectionalStream.Builder(Http2TestServer.getEchoMethodUrl (), callback,
637 callback.getExecutor(), mTestFramework.mCronetEngine);
638 BidirectionalStream stream = builder.setHttpMethod("GET").build();
639 stream.start();
640 callback.waitForNextReadStep();
641
642 assertEquals(null, callback.mError);
643 assertFalse(callback.isDone());
644 assertEquals(TestBidirectionalStreamCallback.ResponseStep.ON_RESPONSE_ST ARTED,
645 callback.mResponseStep);
646
647 ByteBuffer readBuffer = ByteBuffer.allocateDirect(5);
648 readBuffer.put("FOR".getBytes());
649 assertEquals(3, readBuffer.position());
650
651 // Read first two characters of the response ("GE"). It's theoretically
652 // possible to need one read per character, though in practice,
653 // shouldn't happen.
654 while (callback.mResponseAsString.length() < 2) {
655 assertFalse(callback.isDone());
656 callback.startNextRead(stream, readBuffer);
657 callback.waitForNextReadStep();
658 }
659
660 // Make sure the two characters were read.
661 assertEquals("GE", callback.mResponseAsString);
662
663 // Check the contents of the entire buffer. The first 3 characters
664 // should not have been changed, and the last two should be the first
665 // two characters from the response.
666 assertEquals("FORGE", bufferContentsToString(readBuffer, 0, 5));
667 // The limit and position should be 5.
668 assertEquals(5, readBuffer.limit());
669 assertEquals(5, readBuffer.position());
670
671 assertEquals(ResponseStep.ON_READ_COMPLETED, callback.mResponseStep);
672
673 // Start reading from position 3. Since the only remaining character
674 // from the response is a "T", when the read completes, the buffer
675 // should contain "FORTE", with a position() of 4 and a limit() of 5.
676 readBuffer.position(3);
677 callback.startNextRead(stream, readBuffer);
678 callback.waitForNextReadStep();
679
680 // Make sure all three characters of the response have now been read.
681 assertEquals("GET", callback.mResponseAsString);
682
683 // Check the entire contents of the buffer. Only the third character
684 // should have been modified.
685 assertEquals("FORTE", bufferContentsToString(readBuffer, 0, 5));
686
687 // Make sure position and limit were updated correctly.
688 assertEquals(4, readBuffer.position());
689 assertEquals(5, readBuffer.limit());
690
691 assertEquals(ResponseStep.ON_READ_COMPLETED, callback.mResponseStep);
692
693 // One more read attempt. The request should complete.
694 readBuffer.position(1);
695 readBuffer.limit(5);
696 callback.startNextRead(stream, readBuffer);
697 callback.waitForNextReadStep();
698
699 assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
700 assertEquals("GET", callback.mResponseAsString);
701 checkResponseInfo(callback.mResponseInfo, Http2TestServer.getEchoMethodU rl(), 200, "");
702
703 // Check that buffer contents were not modified.
704 assertEquals("FORTE", bufferContentsToString(readBuffer, 0, 5));
705
706 // Position should not have been modified, since nothing was read.
707 assertEquals(1, readBuffer.position());
708 // Limit should be unchanged as always.
709 assertEquals(5, readBuffer.limit());
710
711 assertEquals(ResponseStep.ON_SUCCEEDED, callback.mResponseStep);
712
713 // Make sure there are no other pending messages, which would trigger
714 // asserts in TestBidirectionalCallback.
715 testSimpleGet();
716 }
717
718 @SmallTest
719 @Feature({"Cronet"})
720 @OnlyRunNativeCronet
721 public void testBadBuffers() throws Exception {
722 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback();
723 callback.setAutoAdvance(false);
724 BidirectionalStream.Builder builder =
725 new BidirectionalStream.Builder(Http2TestServer.getEchoMethodUrl (), callback,
726 callback.getExecutor(), mTestFramework.mCronetEngine);
727 BidirectionalStream stream = builder.setHttpMethod("GET").build();
728 stream.start();
729 callback.waitForNextReadStep();
730
731 assertEquals(null, callback.mError);
732 assertFalse(callback.isDone());
733 assertEquals(TestBidirectionalStreamCallback.ResponseStep.ON_RESPONSE_ST ARTED,
734 callback.mResponseStep);
735
736 // Try to read using a full buffer.
737 try {
738 ByteBuffer readBuffer = ByteBuffer.allocateDirect(4);
739 readBuffer.put("full".getBytes());
740 stream.read(readBuffer);
741 fail("Exception not thrown");
742 } catch (IllegalArgumentException e) {
743 assertEquals("ByteBuffer is already full.", e.getMessage());
744 }
745
746 // Try to read using a non-direct buffer.
747 try {
748 ByteBuffer readBuffer = ByteBuffer.allocate(5);
749 stream.read(readBuffer);
750 fail("Exception not thrown");
751 } catch (Exception e) {
752 assertEquals("byteBuffer must be a direct ByteBuffer.", e.getMessage ());
753 }
754
755 // Finish the stream with a direct ByteBuffer.
756 callback.setAutoAdvance(true);
757 ByteBuffer readBuffer = ByteBuffer.allocateDirect(5);
758 stream.read(readBuffer);
759 callback.blockForDone();
760 assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
761 assertEquals("GET", callback.mResponseAsString);
762 }
763
764 private void throwOrCancel(FailureType failureType, ResponseStep failureStep ,
765 boolean expectResponseInfo, boolean expectError) {
766 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback();
767 callback.setFailure(failureType, failureStep);
768 BidirectionalStream.Builder builder =
769 new BidirectionalStream.Builder(Http2TestServer.getEchoMethodUrl (), callback,
770 callback.getExecutor(), mTestFramework.mCronetEngine);
771 BidirectionalStream stream = builder.setHttpMethod("GET").build();
772 stream.start();
773 callback.blockForDone();
774 // assertEquals(callback.mResponseStep, failureStep);
775 assertTrue(stream.isDone());
776 assertEquals(expectResponseInfo, callback.mResponseInfo != null);
777 assertEquals(expectError, callback.mError != null);
778 assertEquals(expectError, callback.mOnErrorCalled);
779 assertEquals(failureType == FailureType.CANCEL_SYNC
780 || failureType == FailureType.CANCEL_ASYNC
781 || failureType == FailureType.CANCEL_ASYNC_WITHOUT_PAUSE ,
782 callback.mOnCanceledCalled);
783 }
784
785 @SmallTest
786 @Feature({"Cronet"})
787 @OnlyRunNativeCronet
788 public void testFailures() throws Exception {
789 throwOrCancel(FailureType.CANCEL_SYNC, ResponseStep.ON_REQUEST_HEADERS_S ENT, false, false);
790 throwOrCancel(FailureType.CANCEL_ASYNC, ResponseStep.ON_REQUEST_HEADERS_ SENT, false, false);
791 throwOrCancel(FailureType.CANCEL_ASYNC_WITHOUT_PAUSE, ResponseStep.ON_RE QUEST_HEADERS_SENT,
792 false, false);
793 throwOrCancel(FailureType.THROW_SYNC, ResponseStep.ON_REQUEST_HEADERS_SE NT, false, true);
794
795 throwOrCancel(FailureType.CANCEL_SYNC, ResponseStep.ON_RESPONSE_STARTED, true, false);
796 throwOrCancel(FailureType.CANCEL_ASYNC, ResponseStep.ON_RESPONSE_STARTED , true, false);
797 throwOrCancel(FailureType.CANCEL_ASYNC_WITHOUT_PAUSE, ResponseStep.ON_RE SPONSE_STARTED,
798 true, false);
799 throwOrCancel(FailureType.THROW_SYNC, ResponseStep.ON_RESPONSE_STARTED, true, true);
800
801 throwOrCancel(FailureType.CANCEL_SYNC, ResponseStep.ON_READ_COMPLETED, t rue, false);
802 throwOrCancel(FailureType.CANCEL_ASYNC, ResponseStep.ON_READ_COMPLETED, true, false);
803 throwOrCancel(FailureType.CANCEL_ASYNC_WITHOUT_PAUSE, ResponseStep.ON_RE AD_COMPLETED, true,
804 false);
805 throwOrCancel(FailureType.THROW_SYNC, ResponseStep.ON_READ_COMPLETED, tr ue, true);
806 }
807
808 @SmallTest
809 @Feature({"Cronet"})
810 @OnlyRunNativeCronet
811 public void testThrowOnSucceeded() {
812 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback();
813 callback.setFailure(FailureType.THROW_SYNC, ResponseStep.ON_SUCCEEDED);
814 BidirectionalStream.Builder builder =
815 new BidirectionalStream.Builder(Http2TestServer.getEchoMethodUrl (), callback,
816 callback.getExecutor(), mTestFramework.mCronetEngine);
817 BidirectionalStream stream = builder.setHttpMethod("GET").build();
818 stream.start();
819 callback.blockForDone();
820 assertEquals(callback.mResponseStep, ResponseStep.ON_SUCCEEDED);
821 assertTrue(stream.isDone());
822 assertNotNull(callback.mResponseInfo);
823 // Check that error thrown from 'onSucceeded' callback is not reported.
824 assertNull(callback.mError);
825 assertFalse(callback.mOnErrorCalled);
826 }
827
828 @SmallTest
829 @Feature({"Cronet"})
830 @OnlyRunNativeCronet
831 public void testExecutorShutdownBeforeStreamIsDone() {
832 // Test that stream is destroyed even if executor is shut down and rejec ts posting tasks.
833 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa llback();
834 callback.setAutoAdvance(false);
835 BidirectionalStream.Builder builder =
836 new BidirectionalStream.Builder(Http2TestServer.getEchoMethodUrl (), callback,
837 callback.getExecutor(), mTestFramework.mCronetEngine);
838 CronetBidirectionalStream stream =
839 (CronetBidirectionalStream) builder.setHttpMethod("GET").build() ;
840 stream.start();
841 callback.waitForNextReadStep();
842 assertFalse(callback.isDone());
843 assertFalse(stream.isDone());
844
845 final ConditionVariable streamDestroyed = new ConditionVariable(false);
846 stream.setOnDestroyedCallbackForTesting(new Runnable() {
847 @Override
848 public void run() {
849 streamDestroyed.open();
850 }
851 });
852
853 // Shut down the executor, so posting the task will throw an exception.
854 callback.shutdownExecutor();
855 ByteBuffer readBuffer = ByteBuffer.allocateDirect(5);
856 stream.read(readBuffer);
857 // Callback will never be called again because executor is shut down,
858 // but stream will be destroyed from network thread.
859 streamDestroyed.block();
860
861 assertFalse(callback.isDone());
862 assertTrue(stream.isDone());
863 }
864
865 /**
866 * Callback that shuts down the engine when the stream has succeeded
867 * or failed.
868 */
869 private class ShutdownTestBidirectionalStreamCallback extends TestBidirectio nalStreamCallback {
870 @Override
871 public void onSucceeded(BidirectionalStream stream, UrlResponseInfo info ) {
872 mTestFramework.mCronetEngine.shutdown();
873 // Clear mCronetEngine so it doesn't get shut down second time in te arDown().
874 mTestFramework.mCronetEngine = null;
875 super.onSucceeded(stream, info);
876 }
877
878 @Override
879 public void onFailed(
880 BidirectionalStream stream, UrlResponseInfo info, CronetExceptio n error) {
881 mTestFramework.mCronetEngine.shutdown();
882 // Clear mCronetEngine so it doesn't get shut down second time in te arDown().
883 mTestFramework.mCronetEngine = null;
884 super.onFailed(stream, info, error);
885 }
886
887 @Override
888 public void onCanceled(BidirectionalStream stream, UrlResponseInfo info) {
889 mTestFramework.mCronetEngine.shutdown();
890 // Clear mCronetEngine so it doesn't get shut down second time in te arDown().
891 mTestFramework.mCronetEngine = null;
892 super.onCanceled(stream, info);
893 }
894 }
895
896 @SmallTest
897 @Feature({"Cronet"})
898 @OnlyRunNativeCronet
899 public void testCronetEngineShutdown() throws Exception {
900 // Test that CronetEngine cannot be shut down if there are any active st reams.
901 TestBidirectionalStreamCallback callback = new ShutdownTestBidirectional StreamCallback();
902 // Block callback when response starts to verify that shutdown fails
903 // if there are active streams.
904 callback.setAutoAdvance(false);
905 BidirectionalStream.Builder builder =
906 new BidirectionalStream.Builder(Http2TestServer.getEchoMethodUrl (), callback,
907 callback.getExecutor(), mTestFramework.mCronetEngine);
908 CronetBidirectionalStream stream =
909 (CronetBidirectionalStream) builder.setHttpMethod("GET").build() ;
910 stream.start();
911 try {
912 mTestFramework.mCronetEngine.shutdown();
913 fail("Should throw an exception");
914 } catch (Exception e) {
915 assertEquals("Cannot shutdown with active requests.", e.getMessage() );
916 }
917
918 callback.waitForNextReadStep();
919 assertEquals(ResponseStep.ON_RESPONSE_STARTED, callback.mResponseStep);
920 try {
921 mTestFramework.mCronetEngine.shutdown();
922 fail("Should throw an exception");
923 } catch (Exception e) {
924 assertEquals("Cannot shutdown with active requests.", e.getMessage() );
925 }
926 callback.startNextRead(stream);
927
928 callback.waitForNextReadStep();
929 assertEquals(ResponseStep.ON_READ_COMPLETED, callback.mResponseStep);
930 try {
931 mTestFramework.mCronetEngine.shutdown();
932 fail("Should throw an exception");
933 } catch (Exception e) {
934 assertEquals("Cannot shutdown with active requests.", e.getMessage() );
935 }
936
937 // May not have read all the data, in theory. Just enable auto-advance
938 // and finish the request.
939 callback.setAutoAdvance(true);
940 callback.startNextRead(stream);
941 callback.blockForDone();
942 }
943
944 @SmallTest
945 @Feature({"Cronet"})
946 @OnlyRunNativeCronet
947 public void testCronetEngineShutdownAfterStreamFailure() throws Exception {
948 // Test that CronetEngine can be shut down after stream reports a failur e.
949 TestBidirectionalStreamCallback callback = new ShutdownTestBidirectional StreamCallback();
950 BidirectionalStream.Builder builder =
951 new BidirectionalStream.Builder(Http2TestServer.getEchoMethodUrl (), callback,
952 callback.getExecutor(), mTestFramework.mCronetEngine);
953 CronetBidirectionalStream stream =
954 (CronetBidirectionalStream) builder.setHttpMethod("GET").build() ;
955 stream.start();
956 callback.setFailure(FailureType.THROW_SYNC, ResponseStep.ON_READ_COMPLET ED);
957 callback.blockForDone();
958 assertTrue(callback.mOnErrorCalled);
959 assertNull(mTestFramework.mCronetEngine);
960 }
961
962 @SmallTest
963 @Feature({"Cronet"})
964 @OnlyRunNativeCronet
965 public void testCronetEngineShutdownAfterStreamCancel() throws Exception {
966 // Test that CronetEngine can be shut down after stream is canceled.
967 TestBidirectionalStreamCallback callback = new ShutdownTestBidirectional StreamCallback();
968 BidirectionalStream.Builder builder =
969 new BidirectionalStream.Builder(Http2TestServer.getEchoMethodUrl (), callback,
970 callback.getExecutor(), mTestFramework.mCronetEngine);
971 CronetBidirectionalStream stream =
972 (CronetBidirectionalStream) builder.setHttpMethod("GET").build() ;
973
974 // Block callback when response starts to verify that shutdown fails
975 // if there are active requests.
976 callback.setAutoAdvance(false);
977 stream.start();
978 try {
979 mTestFramework.mCronetEngine.shutdown();
980 fail("Should throw an exception");
981 } catch (Exception e) {
982 assertEquals("Cannot shutdown with active requests.", e.getMessage() );
983 }
984 callback.waitForNextReadStep();
985 assertEquals(ResponseStep.ON_RESPONSE_STARTED, callback.mResponseStep);
986 stream.cancel();
987 callback.blockForDone();
988 assertTrue(callback.mOnCanceledCalled);
989 assertNull(mTestFramework.mCronetEngine);
990 }
991
992 // Returns the contents of byteBuffer, from its position() to its limit(),
993 // as a String. Does not modify byteBuffer's position().
994 private static String bufferContentsToString(ByteBuffer byteBuffer, int star t, int end) {
995 // Use a duplicate to avoid modifying byteBuffer.
996 ByteBuffer duplicate = byteBuffer.duplicate();
997 duplicate.position(start);
998 duplicate.limit(end);
999 byte[] contents = new byte[duplicate.remaining()];
1000 duplicate.get(contents);
1001 return new String(contents);
120 } 1002 }
121 } 1003 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698