OLD | NEW |
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.content.Context; |
| 8 import android.os.ConditionVariable; |
7 import android.test.suitebuilder.annotation.SmallTest; | 9 import android.test.suitebuilder.annotation.SmallTest; |
8 | 10 |
9 import org.chromium.base.test.util.Feature; | 11 import org.chromium.base.test.util.Feature; |
| 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.ArrayList; |
13 import java.util.concurrent.ExecutorService; | 17 import java.util.List; |
14 import java.util.concurrent.Executors; | 18 import java.util.regex.Matcher; |
15 import java.util.concurrent.ThreadFactory; | 19 import java.util.regex.Pattern; |
16 | |
17 /** | 20 /** |
18 * Test functionality of BidirectionalStream interface. | 21 * Test functionality of BidirectionalStream interface. |
19 */ | 22 */ |
20 public class BidirectionalStreamTest extends CronetTestBase { | 23 public class BidirectionalStreamTest extends CronetTestBase { |
| 24 private static final String CERT_USED = "quic_test.example.com.crt"; |
| 25 private static final String KEY_USED = "quic_test.example.com.key"; |
| 26 private static final String[] CERTS_USED = {CERT_USED}; |
| 27 |
21 private CronetTestFramework mTestFramework; | 28 private CronetTestFramework mTestFramework; |
| 29 // URL used for base tests. |
| 30 private static final String TEST_URL = "https://127.0.0.1:8443"; |
| 31 |
| 32 // TODO(mef): Remove 'run' override and following class definition to |
| 33 // execute tests against Netty-based Http2TestServer. |
| 34 @Override |
| 35 public void run(junit.framework.TestResult result) { |
| 36 result.startTest(this); |
| 37 result.endTest(this); |
| 38 return; |
| 39 } |
| 40 |
| 41 static class Http2TestServer { |
| 42 static String getBaseUrl() { |
| 43 return ""; |
| 44 } |
| 45 |
| 46 static String getEchoMethodUrl() { |
| 47 return ""; |
| 48 } |
| 49 |
| 50 static String getEchoAllHeadersUrl() { |
| 51 return ""; |
| 52 } |
| 53 |
| 54 static String getEchoStreamUrl() { |
| 55 return ""; |
| 56 } |
| 57 |
| 58 static String getEchoTrailersUrl() { |
| 59 return ""; |
| 60 } |
| 61 |
| 62 static String getEchoHeaderUrl(String headerName) { |
| 63 return ""; |
| 64 } |
| 65 |
| 66 static boolean startHttp2TestServer( |
| 67 Context context, String certFileName, String keyFileName) { |
| 68 return false; |
| 69 } |
| 70 |
| 71 static boolean shutdownHttp2TestServer() { |
| 72 return false; |
| 73 } |
| 74 } |
22 | 75 |
23 @Override | 76 @Override |
24 protected void setUp() throws Exception { | 77 protected void setUp() throws Exception { |
25 super.setUp(); | 78 super.setUp(); |
26 mTestFramework = startCronetTestFramework(); | 79 // Load library first to create MockCertVerifier. |
| 80 System.loadLibrary("cronet_tests"); |
| 81 |
| 82 CronetEngine.Builder builder = new CronetEngine.Builder(getContext()); |
| 83 builder.setMockCertVerifierForTesting(MockCertVerifier.createMockCertVer
ifier(CERTS_USED)); |
| 84 |
| 85 mTestFramework = startCronetTestFrameworkWithUrlAndCronetEngineBuilder(n
ull, builder); |
27 assertTrue(NativeTestServer.startNativeTestServer(getContext())); | 86 assertTrue(NativeTestServer.startNativeTestServer(getContext())); |
| 87 assertTrue(Http2TestServer.startHttp2TestServer(getContext(), CERT_USED,
KEY_USED)); |
28 // Add url interceptors after native application context is initialized. | 88 // Add url interceptors after native application context is initialized. |
29 MockUrlRequestJobFactory.setUp(); | 89 MockUrlRequestJobFactory.setUp(); |
30 } | 90 } |
31 | 91 |
32 @Override | 92 @Override |
33 protected void tearDown() throws Exception { | 93 protected void tearDown() throws Exception { |
| 94 assertTrue(Http2TestServer.shutdownHttp2TestServer()); |
34 NativeTestServer.shutdownNativeTestServer(); | 95 NativeTestServer.shutdownNativeTestServer(); |
35 mTestFramework.mCronetEngine.shutdown(); | 96 if (mTestFramework.mCronetEngine != null) mTestFramework.mCronetEngine.s
hutdown(); |
36 super.tearDown(); | 97 super.tearDown(); |
37 } | 98 } |
38 | 99 |
39 private class TestBidirectionalStreamCallback extends BidirectionalStream.Ca
llback { | 100 private TestBidirectionalStreamCallback startAndWaitForComplete(String url)
throws Exception { |
40 // Executor Service for Cronet callbacks. | 101 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa
llback(); |
41 private final ExecutorService mExecutorService = | |
42 Executors.newSingleThreadExecutor(new ExecutorThreadFactory()); | |
43 private Thread mExecutorThread; | |
44 | 102 |
45 private class ExecutorThreadFactory implements ThreadFactory { | 103 // Create stream. |
46 public Thread newThread(Runnable r) { | 104 BidirectionalStream stream = |
47 mExecutorThread = new Thread(r); | 105 new BidirectionalStream.Builder(url, callback, callback.getExecu
tor(), |
48 return mExecutorThread; | 106 mTestFramework.mCronetEngine) |
49 } | 107 .setHttpMethod("GET") |
| 108 .build(); |
| 109 stream.start(); |
| 110 callback.blockForDone(); |
| 111 assertTrue(stream.isDone()); |
| 112 return callback; |
| 113 } |
| 114 |
| 115 private void checkResponseInfo(UrlResponseInfo responseInfo, String expected
Url, |
| 116 int expectedHttpStatusCode, String expectedHttpStatusText) { |
| 117 assertEquals(expectedUrl, responseInfo.getUrl()); |
| 118 assertEquals( |
| 119 expectedUrl, responseInfo.getUrlChain().get(responseInfo.getUrlC
hain().size() - 1)); |
| 120 assertEquals(expectedHttpStatusCode, responseInfo.getHttpStatusCode()); |
| 121 assertEquals(expectedHttpStatusText, responseInfo.getHttpStatusText()); |
| 122 assertFalse(responseInfo.wasCached()); |
| 123 assertTrue(responseInfo.toString().length() > 0); |
| 124 } |
| 125 |
| 126 private static String makeLongString(String base, int repetition) { |
| 127 StringBuilder builder = new StringBuilder(base.length() * repetition); |
| 128 for (int i = 0; i < repetition; ++i) { |
| 129 builder.append(i); |
| 130 builder.append(base); |
50 } | 131 } |
51 | 132 return builder.toString(); |
52 @Override | |
53 public void onRequestHeadersSent(BidirectionalStream stream) {} | |
54 | |
55 @Override | |
56 public void onResponseHeadersReceived(BidirectionalStream stream, UrlRes
ponseInfo info) {} | |
57 | |
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 } | |
83 } | 133 } |
84 | 134 |
85 @SmallTest | 135 @SmallTest |
86 @Feature({"Cronet"}) | 136 @Feature({"Cronet"}) |
87 public void testBuilderChecks() throws Exception { | 137 public void testBuilderChecks() throws Exception { |
88 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa
llback(); | 138 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa
llback(); |
89 try { | 139 try { |
90 new BidirectionalStream.Builder( | 140 new BidirectionalStream.Builder( |
91 null, callback, callback.getExecutor(), mTestFramework.mCron
etEngine); | 141 null, callback, callback.getExecutor(), mTestFramework.mCron
etEngine); |
92 fail("URL not null-checked"); | 142 fail("URL not null-checked"); |
(...skipping 18 matching lines...) Expand all Loading... |
111 new BidirectionalStream.Builder( | 161 new BidirectionalStream.Builder( |
112 NativeTestServer.getRedirectURL(), callback, callback.getExe
cutor(), null); | 162 NativeTestServer.getRedirectURL(), callback, callback.getExe
cutor(), null); |
113 fail("CronetEngine not null-checked"); | 163 fail("CronetEngine not null-checked"); |
114 } catch (NullPointerException e) { | 164 } catch (NullPointerException e) { |
115 assertEquals("CronetEngine is required.", e.getMessage()); | 165 assertEquals("CronetEngine is required.", e.getMessage()); |
116 } | 166 } |
117 // Verify successful creation doesn't throw. | 167 // Verify successful creation doesn't throw. |
118 new BidirectionalStream.Builder(NativeTestServer.getRedirectURL(), callb
ack, | 168 new BidirectionalStream.Builder(NativeTestServer.getRedirectURL(), callb
ack, |
119 callback.getExecutor(), mTestFramework.mCronetEngine); | 169 callback.getExecutor(), mTestFramework.mCronetEngine); |
120 } | 170 } |
| 171 |
| 172 @SmallTest |
| 173 @Feature({"Cronet"}) |
| 174 public void testFailPlainHttp() throws Exception { |
| 175 String url = NativeTestServer.getEchoMethodURL(); |
| 176 TestBidirectionalStreamCallback callback = startAndWaitForComplete(url); |
| 177 assertEquals("Exception in BidirectionalStream: net::ERR_DISALLOWED_URL_
SCHEME", |
| 178 callback.mError.getMessage()); |
| 179 } |
| 180 |
| 181 @SmallTest |
| 182 @Feature({"Cronet"}) |
| 183 public void testSimpleGet() throws Exception { |
| 184 String url = Http2TestServer.getEchoMethodUrl(); |
| 185 TestBidirectionalStreamCallback callback = startAndWaitForComplete(url); |
| 186 assertEquals(200, callback.mResponseInfo.getHttpStatusCode()); |
| 187 // Default method is 'GET'. |
| 188 assertEquals("GET", callback.mResponseAsString); |
| 189 assertEquals(String.format("UrlResponseInfo[%s]: urlChain = [%s], httpSt
atus = 200 , " |
| 190 + "headers = [:status=200], wasCached = fal
se, " |
| 191 + "negotiatedProtocol = h2, proxyServer= nu
ll, " |
| 192 + "receivedBytesCount = 27", |
| 193 url, url), |
| 194 callback.mResponseInfo.toString()); |
| 195 checkResponseInfo(callback.mResponseInfo, Http2TestServer.getEchoMethodU
rl(), 200, ""); |
| 196 } |
| 197 |
| 198 @SmallTest |
| 199 @Feature({"Cronet"}) |
| 200 public void testSimpleHead() throws Exception { |
| 201 String url = Http2TestServer.getEchoMethodUrl(); |
| 202 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa
llback(); |
| 203 |
| 204 // Create stream. |
| 205 BidirectionalStream stream = |
| 206 new BidirectionalStream.Builder(url, callback, callback.getExecu
tor(), |
| 207 mTestFramework.mCronetEngine) |
| 208 .setHttpMethod("HEAD") |
| 209 .build(); |
| 210 stream.start(); |
| 211 callback.blockForDone(); |
| 212 assertTrue(stream.isDone()); |
| 213 assertEquals(200, callback.mResponseInfo.getHttpStatusCode()); |
| 214 assertEquals("HEAD", callback.mResponseAsString); |
| 215 assertEquals(String.format("UrlResponseInfo[%s]: urlChain = [%s], httpSt
atus = 200 , " |
| 216 + "headers = [:status=200], wasCached = fal
se, " |
| 217 + "negotiatedProtocol = h2, proxyServer= nu
ll, " |
| 218 + "receivedBytesCount = 28", |
| 219 url, url), |
| 220 callback.mResponseInfo.toString()); |
| 221 checkResponseInfo(callback.mResponseInfo, Http2TestServer.getEchoMethodU
rl(), 200, ""); |
| 222 } |
| 223 |
| 224 @SmallTest |
| 225 @Feature({"Cronet"}) |
| 226 public void testSimplePost() throws Exception { |
| 227 String url = Http2TestServer.getBaseUrl(); |
| 228 mTestFramework.startNetLog(); |
| 229 |
| 230 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa
llback(); |
| 231 callback.addWriteData("Test String".getBytes()); |
| 232 callback.addWriteData("1234567890".getBytes()); |
| 233 callback.addWriteData("woot!".getBytes()); |
| 234 |
| 235 // Create stream. |
| 236 BidirectionalStream stream = |
| 237 new BidirectionalStream.Builder(url, callback, callback.getExecu
tor(), |
| 238 mTestFramework.mCronetEngine) |
| 239 .addHeader("foo", "bar") |
| 240 .addHeader("Content-Type", "zebra") |
| 241 .build(); |
| 242 stream.start(); |
| 243 callback.blockForDone(); |
| 244 assertTrue(stream.isDone()); |
| 245 |
| 246 mTestFramework.stopNetLog(); |
| 247 |
| 248 assertEquals(200, callback.mResponseInfo.getHttpStatusCode()); |
| 249 assertEquals("woot!", callback.mResponseAsString); |
| 250 } |
| 251 |
| 252 @SmallTest |
| 253 @Feature({"Cronet"}) |
| 254 public void testSetHttpMethod() throws Exception { |
| 255 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa
llback(); |
| 256 callback.addWriteData("Put This Data!".getBytes()); |
| 257 |
| 258 String methodName = "PUT"; |
| 259 BidirectionalStream.Builder builder = new BidirectionalStream.Builder( |
| 260 TEST_URL, callback, callback.getExecutor(), mTestFramework.mCron
etEngine); |
| 261 // Try to set 'null' method. |
| 262 try { |
| 263 builder.setHttpMethod(null); |
| 264 fail("Exception not thrown"); |
| 265 } catch (NullPointerException e) { |
| 266 assertEquals("Method is required.", e.getMessage()); |
| 267 } |
| 268 |
| 269 builder.setHttpMethod(methodName); |
| 270 builder.build().start(); |
| 271 callback.blockForDone(); |
| 272 assertEquals(200, callback.mResponseInfo.getHttpStatusCode()); |
| 273 assertEquals("Put This Data!", callback.mResponseAsString); |
| 274 } |
| 275 |
| 276 @SmallTest |
| 277 @Feature({"Cronet"}) |
| 278 public void testBadMethod() throws Exception { |
| 279 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa
llback(); |
| 280 BidirectionalStream.Builder builder = new BidirectionalStream.Builder( |
| 281 TEST_URL, callback, callback.getExecutor(), mTestFramework.mCron
etEngine); |
| 282 try { |
| 283 builder.setHttpMethod("bad:method!"); |
| 284 builder.build().start(); |
| 285 fail("IllegalArgumentException not thrown."); |
| 286 } catch (IllegalArgumentException e) { |
| 287 assertEquals("Invalid http method bad:method!", e.getMessage()); |
| 288 } |
| 289 } |
| 290 |
| 291 @SmallTest |
| 292 @Feature({"Cronet"}) |
| 293 public void testBadHeaderName() throws Exception { |
| 294 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa
llback(); |
| 295 BidirectionalStream.Builder builder = new BidirectionalStream.Builder( |
| 296 TEST_URL, callback, callback.getExecutor(), mTestFramework.mCron
etEngine); |
| 297 try { |
| 298 builder.addHeader("header:name", "headervalue"); |
| 299 builder.build().start(); |
| 300 fail("IllegalArgumentException not thrown."); |
| 301 } catch (IllegalArgumentException e) { |
| 302 assertEquals("Invalid header header:name=headervalue", e.getMessage(
)); |
| 303 } |
| 304 } |
| 305 |
| 306 @SmallTest |
| 307 @Feature({"Cronet"}) |
| 308 public void testBadHeaderValue() throws Exception { |
| 309 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa
llback(); |
| 310 BidirectionalStream.Builder builder = new BidirectionalStream.Builder( |
| 311 TEST_URL, callback, callback.getExecutor(), mTestFramework.mCron
etEngine); |
| 312 try { |
| 313 builder.addHeader("headername", "bad header\r\nvalue"); |
| 314 builder.build().start(); |
| 315 fail("IllegalArgumentException not thrown."); |
| 316 } catch (IllegalArgumentException e) { |
| 317 assertEquals("Invalid header headername=bad header\r\nvalue", e.getM
essage()); |
| 318 } |
| 319 } |
| 320 |
| 321 @SmallTest |
| 322 @Feature({"Cronet"}) |
| 323 public void testAddHeader() throws Exception { |
| 324 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa
llback(); |
| 325 String headerName = "header-name"; |
| 326 String headerValue = "header-value"; |
| 327 BidirectionalStream.Builder builder = |
| 328 new BidirectionalStream.Builder(Http2TestServer.getEchoHeaderUrl
(headerName), |
| 329 callback, callback.getExecutor(), mTestFramework.mCronet
Engine); |
| 330 |
| 331 builder.addHeader(headerName, headerValue); |
| 332 builder.setHttpMethod("GET"); |
| 333 builder.build().start(); |
| 334 callback.blockForDone(); |
| 335 assertEquals(200, callback.mResponseInfo.getHttpStatusCode()); |
| 336 assertEquals(headerValue, callback.mResponseAsString); |
| 337 } |
| 338 |
| 339 @SmallTest |
| 340 @Feature({"Cronet"}) |
| 341 public void testMultiRequestHeaders() throws Exception { |
| 342 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa
llback(); |
| 343 String headerName = "header-name"; |
| 344 String headerValue1 = "header-value1"; |
| 345 String headerValue2 = "header-value2"; |
| 346 BidirectionalStream.Builder builder = |
| 347 new BidirectionalStream.Builder(Http2TestServer.getEchoAllHeader
sUrl(), callback, |
| 348 callback.getExecutor(), mTestFramework.mCronetEngine); |
| 349 builder.addHeader(headerName, headerValue1); |
| 350 builder.addHeader(headerName, headerValue2); |
| 351 builder.setHttpMethod("GET"); |
| 352 builder.build().start(); |
| 353 callback.blockForDone(); |
| 354 assertEquals(200, callback.mResponseInfo.getHttpStatusCode()); |
| 355 String headers = callback.mResponseAsString; |
| 356 Pattern pattern = Pattern.compile(headerName + ":\\s(.*)\\r\\n"); |
| 357 Matcher matcher = pattern.matcher(headers); |
| 358 List<String> actualValues = new ArrayList<String>(); |
| 359 while (matcher.find()) { |
| 360 actualValues.add(matcher.group(1)); |
| 361 } |
| 362 assertEquals(1, actualValues.size()); |
| 363 assertEquals("header-value2", actualValues.get(0)); |
| 364 } |
| 365 |
| 366 @SmallTest |
| 367 @Feature({"Cronet"}) |
| 368 public void testEchoTrailers() throws Exception { |
| 369 mTestFramework.startNetLog(); |
| 370 |
| 371 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa
llback(); |
| 372 String headerName = "header-name"; |
| 373 String headerValue = "header-value"; |
| 374 BidirectionalStream.Builder builder = |
| 375 new BidirectionalStream.Builder(Http2TestServer.getEchoTrailersU
rl(), callback, |
| 376 callback.getExecutor(), mTestFramework.mCronetEngine); |
| 377 builder.addHeader(headerName, headerValue); |
| 378 builder.setHttpMethod("GET"); |
| 379 builder.build().start(); |
| 380 callback.blockForDone(); |
| 381 mTestFramework.stopNetLog(); |
| 382 |
| 383 assertEquals(200, callback.mResponseInfo.getHttpStatusCode()); |
| 384 assertNotNull(callback.mTrailers); |
| 385 assertEquals(headerValue, callback.mTrailers.getAsMap().get(headerName).
get(0)); |
| 386 } |
| 387 |
| 388 @SmallTest |
| 389 @Feature({"Cronet"}) |
| 390 public void testCustomUserAgent() throws Exception { |
| 391 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa
llback(); |
| 392 String userAgentName = "User-Agent"; |
| 393 String userAgentValue = "User-Agent-Value"; |
| 394 BidirectionalStream.Builder builder = |
| 395 new BidirectionalStream.Builder(Http2TestServer.getEchoHeaderUrl
(userAgentName), |
| 396 callback, callback.getExecutor(), mTestFramework.mCronet
Engine); |
| 397 builder.setHttpMethod("GET"); |
| 398 builder.addHeader(userAgentName, userAgentValue); |
| 399 builder.build().start(); |
| 400 callback.blockForDone(); |
| 401 assertEquals(200, callback.mResponseInfo.getHttpStatusCode()); |
| 402 assertEquals(userAgentValue, callback.mResponseAsString); |
| 403 } |
| 404 |
| 405 @SmallTest |
| 406 @Feature({"Cronet"}) |
| 407 public void testEchoStream() throws Exception { |
| 408 String url = Http2TestServer.getEchoStreamUrl(); |
| 409 mTestFramework.startNetLog(); |
| 410 |
| 411 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa
llback(); |
| 412 String[] testData = {"Test String", makeLongString("1234567890", 50000),
"woot!"}; |
| 413 StringBuilder stringData = new StringBuilder(); |
| 414 for (String writeData : testData) { |
| 415 callback.addWriteData(writeData.getBytes()); |
| 416 stringData.append(writeData); |
| 417 } |
| 418 |
| 419 // Create stream. |
| 420 BidirectionalStream stream = |
| 421 new BidirectionalStream.Builder(url, callback, callback.getExecu
tor(), |
| 422 mTestFramework.mCronetEngine) |
| 423 .addHeader("foo", "Value with Spaces") |
| 424 .addHeader("Content-Type", "zebra") |
| 425 .build(); |
| 426 stream.start(); |
| 427 callback.blockForDone(); |
| 428 assertTrue(stream.isDone()); |
| 429 |
| 430 mTestFramework.stopNetLog(); |
| 431 |
| 432 assertEquals(200, callback.mResponseInfo.getHttpStatusCode()); |
| 433 assertEquals(stringData.toString(), callback.mResponseAsString); |
| 434 assertEquals("Value with Spaces", callback.mResponseInfo.getAllHeaders()
.get("foo").get(0)); |
| 435 assertEquals("zebra", callback.mResponseInfo.getAllHeaders().get("Conten
t-Type").get(0)); |
| 436 } |
| 437 |
| 438 /** |
| 439 * Checks that the buffer is updated correctly, when starting at an offset. |
| 440 */ |
| 441 @SmallTest |
| 442 @Feature({"Cronet"}) |
| 443 public void testSimpleGetBufferUpdates() throws Exception { |
| 444 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa
llback(); |
| 445 callback.setAutoAdvance(false); |
| 446 // Since themethod is "GET", the expected response body is also "GET". |
| 447 BidirectionalStream.Builder builder = |
| 448 new BidirectionalStream.Builder(Http2TestServer.getEchoMethodUrl
(), callback, |
| 449 callback.getExecutor(), mTestFramework.mCronetEngine); |
| 450 BidirectionalStream stream = builder.setHttpMethod("GET").build(); |
| 451 stream.start(); |
| 452 callback.waitForNextReadStep(); |
| 453 |
| 454 assertEquals(null, callback.mError); |
| 455 assertFalse(callback.isDone()); |
| 456 assertEquals(TestBidirectionalStreamCallback.ResponseStep.ON_RESPONSE_ST
ARTED, |
| 457 callback.mResponseStep); |
| 458 |
| 459 ByteBuffer readBuffer = ByteBuffer.allocateDirect(5); |
| 460 readBuffer.put("FOR".getBytes()); |
| 461 assertEquals(3, readBuffer.position()); |
| 462 |
| 463 // Read first two characters of the response ("GE"). It's theoretically |
| 464 // possible to need one read per character, though in practice, |
| 465 // shouldn't happen. |
| 466 while (callback.mResponseAsString.length() < 2) { |
| 467 assertFalse(callback.isDone()); |
| 468 callback.startNextRead(stream, readBuffer); |
| 469 callback.waitForNextReadStep(); |
| 470 } |
| 471 |
| 472 // Make sure the two characters were read. |
| 473 assertEquals("GE", callback.mResponseAsString); |
| 474 |
| 475 // Check the contents of the entire buffer. The first 3 characters |
| 476 // should not have been changed, and the last two should be the first |
| 477 // two characters from the response. |
| 478 assertEquals("FORGE", bufferContentsToString(readBuffer, 0, 5)); |
| 479 // The limit and position should be 5. |
| 480 assertEquals(5, readBuffer.limit()); |
| 481 assertEquals(5, readBuffer.position()); |
| 482 |
| 483 assertEquals(ResponseStep.ON_READ_COMPLETED, callback.mResponseStep); |
| 484 |
| 485 // Start reading from position 3. Since the only remaining character |
| 486 // from the response is a "T", when the read completes, the buffer |
| 487 // should contain "FORTE", with a position() of 4 and a limit() of 5. |
| 488 readBuffer.position(3); |
| 489 callback.startNextRead(stream, readBuffer); |
| 490 callback.waitForNextReadStep(); |
| 491 |
| 492 // Make sure all three characters of the response have now been read. |
| 493 assertEquals("GET", callback.mResponseAsString); |
| 494 |
| 495 // Check the entire contents of the buffer. Only the third character |
| 496 // should have been modified. |
| 497 assertEquals("FORTE", bufferContentsToString(readBuffer, 0, 5)); |
| 498 |
| 499 // Make sure position and limit were updated correctly. |
| 500 assertEquals(4, readBuffer.position()); |
| 501 assertEquals(5, readBuffer.limit()); |
| 502 |
| 503 assertEquals(ResponseStep.ON_READ_COMPLETED, callback.mResponseStep); |
| 504 |
| 505 // One more read attempt. The request should complete. |
| 506 readBuffer.position(1); |
| 507 readBuffer.limit(5); |
| 508 callback.startNextRead(stream, readBuffer); |
| 509 callback.waitForNextReadStep(); |
| 510 |
| 511 assertEquals(200, callback.mResponseInfo.getHttpStatusCode()); |
| 512 assertEquals("GET", callback.mResponseAsString); |
| 513 checkResponseInfo(callback.mResponseInfo, Http2TestServer.getEchoMethodU
rl(), 200, ""); |
| 514 |
| 515 // Check that buffer contents were not modified. |
| 516 assertEquals("FORTE", bufferContentsToString(readBuffer, 0, 5)); |
| 517 |
| 518 // Position should not have been modified, since nothing was read. |
| 519 assertEquals(1, readBuffer.position()); |
| 520 // Limit should be unchanged as always. |
| 521 assertEquals(5, readBuffer.limit()); |
| 522 |
| 523 assertEquals(ResponseStep.ON_SUCCEEDED, callback.mResponseStep); |
| 524 |
| 525 // Make sure there are no other pending messages, which would trigger |
| 526 // asserts in TestBidirectionalCallback. |
| 527 testSimpleGet(); |
| 528 } |
| 529 |
| 530 @SmallTest |
| 531 @Feature({"Cronet"}) |
| 532 public void testBadBuffers() throws Exception { |
| 533 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa
llback(); |
| 534 callback.setAutoAdvance(false); |
| 535 BidirectionalStream.Builder builder = |
| 536 new BidirectionalStream.Builder(Http2TestServer.getEchoMethodUrl
(), callback, |
| 537 callback.getExecutor(), mTestFramework.mCronetEngine); |
| 538 BidirectionalStream stream = builder.setHttpMethod("GET").build(); |
| 539 stream.start(); |
| 540 callback.waitForNextReadStep(); |
| 541 |
| 542 assertEquals(null, callback.mError); |
| 543 assertFalse(callback.isDone()); |
| 544 assertEquals(TestBidirectionalStreamCallback.ResponseStep.ON_RESPONSE_ST
ARTED, |
| 545 callback.mResponseStep); |
| 546 |
| 547 // Try to read using a full buffer. |
| 548 try { |
| 549 ByteBuffer readBuffer = ByteBuffer.allocateDirect(4); |
| 550 readBuffer.put("full".getBytes()); |
| 551 stream.read(readBuffer); |
| 552 fail("Exception not thrown"); |
| 553 } catch (IllegalArgumentException e) { |
| 554 assertEquals("ByteBuffer is already full.", e.getMessage()); |
| 555 } |
| 556 |
| 557 // Try to read using a non-direct buffer. |
| 558 try { |
| 559 ByteBuffer readBuffer = ByteBuffer.allocate(5); |
| 560 stream.read(readBuffer); |
| 561 fail("Exception not thrown"); |
| 562 } catch (Exception e) { |
| 563 assertEquals("byteBuffer must be a direct ByteBuffer.", e.getMessage
()); |
| 564 } |
| 565 |
| 566 // Finish the stream with a direct ByteBuffer. |
| 567 callback.setAutoAdvance(true); |
| 568 ByteBuffer readBuffer = ByteBuffer.allocateDirect(5); |
| 569 stream.read(readBuffer); |
| 570 callback.blockForDone(); |
| 571 assertEquals(200, callback.mResponseInfo.getHttpStatusCode()); |
| 572 assertEquals("GET", callback.mResponseAsString); |
| 573 } |
| 574 |
| 575 private void throwOrCancel(FailureType failureType, ResponseStep failureStep
, |
| 576 boolean expectResponseInfo, boolean expectError) { |
| 577 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa
llback(); |
| 578 callback.setFailure(failureType, failureStep); |
| 579 BidirectionalStream.Builder builder = |
| 580 new BidirectionalStream.Builder(Http2TestServer.getEchoMethodUrl
(), callback, |
| 581 callback.getExecutor(), mTestFramework.mCronetEngine); |
| 582 BidirectionalStream stream = builder.setHttpMethod("GET").build(); |
| 583 stream.start(); |
| 584 callback.blockForDone(); |
| 585 assertEquals(callback.mResponseStep, failureStep); |
| 586 assertTrue(stream.isDone()); |
| 587 assertEquals(expectResponseInfo, callback.mResponseInfo != null); |
| 588 assertEquals(expectError, callback.mError != null); |
| 589 assertEquals(expectError, callback.mOnErrorCalled); |
| 590 assertEquals(failureType == FailureType.CANCEL_SYNC |
| 591 || failureType == FailureType.CANCEL_ASYNC |
| 592 || failureType == FailureType.CANCEL_ASYNC_WITHOUT_PAUSE
, |
| 593 callback.mOnCanceledCalled); |
| 594 } |
| 595 |
| 596 @SmallTest |
| 597 @Feature({"Cronet"}) |
| 598 public void testFailures() throws Exception { |
| 599 throwOrCancel(FailureType.CANCEL_SYNC, ResponseStep.ON_REQUEST_HEADERS_S
ENT, false, false); |
| 600 throwOrCancel(FailureType.CANCEL_ASYNC, ResponseStep.ON_REQUEST_HEADERS_
SENT, false, false); |
| 601 throwOrCancel(FailureType.CANCEL_ASYNC_WITHOUT_PAUSE, ResponseStep.ON_RE
QUEST_HEADERS_SENT, |
| 602 false, false); |
| 603 throwOrCancel(FailureType.THROW_SYNC, ResponseStep.ON_REQUEST_HEADERS_SE
NT, false, true); |
| 604 |
| 605 throwOrCancel(FailureType.CANCEL_SYNC, ResponseStep.ON_RESPONSE_STARTED,
true, false); |
| 606 throwOrCancel(FailureType.CANCEL_ASYNC, ResponseStep.ON_RESPONSE_STARTED
, true, false); |
| 607 throwOrCancel(FailureType.CANCEL_ASYNC_WITHOUT_PAUSE, ResponseStep.ON_RE
SPONSE_STARTED, |
| 608 true, false); |
| 609 throwOrCancel(FailureType.THROW_SYNC, ResponseStep.ON_RESPONSE_STARTED,
true, true); |
| 610 |
| 611 throwOrCancel(FailureType.CANCEL_SYNC, ResponseStep.ON_READ_COMPLETED, t
rue, false); |
| 612 throwOrCancel(FailureType.CANCEL_ASYNC, ResponseStep.ON_READ_COMPLETED,
true, false); |
| 613 throwOrCancel(FailureType.CANCEL_ASYNC_WITHOUT_PAUSE, ResponseStep.ON_RE
AD_COMPLETED, true, |
| 614 false); |
| 615 throwOrCancel(FailureType.THROW_SYNC, ResponseStep.ON_READ_COMPLETED, tr
ue, true); |
| 616 } |
| 617 |
| 618 @SmallTest |
| 619 @Feature({"Cronet"}) |
| 620 public void testThrowOnSucceeded() { |
| 621 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa
llback(); |
| 622 callback.setFailure(FailureType.THROW_SYNC, ResponseStep.ON_SUCCEEDED); |
| 623 BidirectionalStream.Builder builder = |
| 624 new BidirectionalStream.Builder(Http2TestServer.getEchoMethodUrl
(), callback, |
| 625 callback.getExecutor(), mTestFramework.mCronetEngine); |
| 626 BidirectionalStream stream = builder.setHttpMethod("GET").build(); |
| 627 stream.start(); |
| 628 callback.blockForDone(); |
| 629 assertEquals(callback.mResponseStep, ResponseStep.ON_SUCCEEDED); |
| 630 assertTrue(stream.isDone()); |
| 631 assertNotNull(callback.mResponseInfo); |
| 632 assertNull(callback.mError); |
| 633 assertFalse(callback.mOnErrorCalled); |
| 634 } |
| 635 |
| 636 @SmallTest |
| 637 @Feature({"Cronet"}) |
| 638 public void testExecutorShutdown() { |
| 639 TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCa
llback(); |
| 640 |
| 641 callback.setAutoAdvance(false); |
| 642 BidirectionalStream.Builder builder = |
| 643 new BidirectionalStream.Builder(Http2TestServer.getEchoMethodUrl
(), callback, |
| 644 callback.getExecutor(), mTestFramework.mCronetEngine); |
| 645 CronetBidirectionalStream stream = |
| 646 (CronetBidirectionalStream) builder.setHttpMethod("GET").build()
; |
| 647 stream.start(); |
| 648 callback.waitForNextReadStep(); |
| 649 assertFalse(callback.isDone()); |
| 650 assertFalse(stream.isDone()); |
| 651 |
| 652 final ConditionVariable requestDestroyed = new ConditionVariable(false); |
| 653 stream.setOnDestroyedCallbackForTesting(new Runnable() { |
| 654 @Override |
| 655 public void run() { |
| 656 requestDestroyed.open(); |
| 657 } |
| 658 }); |
| 659 |
| 660 // Shutdown the executor, so posting the task will throw an exception. |
| 661 callback.shutdownExecutor(); |
| 662 ByteBuffer readBuffer = ByteBuffer.allocateDirect(5); |
| 663 stream.read(readBuffer); |
| 664 // Callback will never be called again because executor is shutdown, |
| 665 // but stream will be destroyed from network thread. |
| 666 requestDestroyed.block(); |
| 667 |
| 668 assertFalse(callback.isDone()); |
| 669 assertTrue(stream.isDone()); |
| 670 } |
| 671 |
| 672 /** |
| 673 * Callback that shutdowns the engine when the stream has succeeded |
| 674 * or failed. |
| 675 */ |
| 676 class ShutdownTestBidirectionalStreamCallback extends TestBidirectionalStrea
mCallback { |
| 677 @Override |
| 678 public void onSucceeded(BidirectionalStream stream, UrlResponseInfo info
) { |
| 679 mTestFramework.mCronetEngine.shutdown(); |
| 680 // Clear mCronetEngine so it doesn't get shutdown second time in tea
rDown(). |
| 681 mTestFramework.mCronetEngine = null; |
| 682 super.onSucceeded(stream, info); |
| 683 } |
| 684 |
| 685 @Override |
| 686 public void onFailed( |
| 687 BidirectionalStream stream, UrlResponseInfo info, CronetExceptio
n error) { |
| 688 mTestFramework.mCronetEngine.shutdown(); |
| 689 // Clear mCronetEngine so it doesn't get shutdown second time in tea
rDown(). |
| 690 mTestFramework.mCronetEngine = null; |
| 691 super.onFailed(stream, info, error); |
| 692 } |
| 693 } |
| 694 |
| 695 @SmallTest |
| 696 @Feature({"Cronet"}) |
| 697 public void testShutdown() throws Exception { |
| 698 TestBidirectionalStreamCallback callback = new ShutdownTestBidirectional
StreamCallback(); |
| 699 // Block callback when response starts to verify that shutdown fails |
| 700 // if there are active stream. |
| 701 callback.setAutoAdvance(false); |
| 702 BidirectionalStream.Builder builder = |
| 703 new BidirectionalStream.Builder(Http2TestServer.getEchoMethodUrl
(), callback, |
| 704 callback.getExecutor(), mTestFramework.mCronetEngine); |
| 705 CronetBidirectionalStream stream = |
| 706 (CronetBidirectionalStream) builder.setHttpMethod("GET").build()
; |
| 707 stream.start(); |
| 708 try { |
| 709 mTestFramework.mCronetEngine.shutdown(); |
| 710 fail("Should throw an exception"); |
| 711 } catch (Exception e) { |
| 712 assertEquals("Cannot shutdown with active requests.", e.getMessage()
); |
| 713 } |
| 714 |
| 715 callback.waitForNextReadStep(); |
| 716 assertEquals(ResponseStep.ON_RESPONSE_STARTED, callback.mResponseStep); |
| 717 try { |
| 718 mTestFramework.mCronetEngine.shutdown(); |
| 719 fail("Should throw an exception"); |
| 720 } catch (Exception e) { |
| 721 assertEquals("Cannot shutdown with active requests.", e.getMessage()
); |
| 722 } |
| 723 callback.startNextRead(stream); |
| 724 |
| 725 callback.waitForNextReadStep(); |
| 726 assertEquals(ResponseStep.ON_READ_COMPLETED, callback.mResponseStep); |
| 727 try { |
| 728 mTestFramework.mCronetEngine.shutdown(); |
| 729 fail("Should throw an exception"); |
| 730 } catch (Exception e) { |
| 731 assertEquals("Cannot shutdown with active requests.", e.getMessage()
); |
| 732 } |
| 733 |
| 734 // May not have read all the data, in theory. Just enable auto-advance |
| 735 // and finish the request. |
| 736 callback.setAutoAdvance(true); |
| 737 callback.startNextRead(stream); |
| 738 callback.blockForDone(); |
| 739 } |
| 740 |
| 741 @SmallTest |
| 742 @Feature({"Cronet"}) |
| 743 public void testShutdownAfterError() throws Exception { |
| 744 TestBidirectionalStreamCallback callback = new ShutdownTestBidirectional
StreamCallback(); |
| 745 BidirectionalStream.Builder builder = |
| 746 new BidirectionalStream.Builder(Http2TestServer.getEchoMethodUrl
(), callback, |
| 747 callback.getExecutor(), mTestFramework.mCronetEngine); |
| 748 CronetBidirectionalStream stream = |
| 749 (CronetBidirectionalStream) builder.setHttpMethod("GET").build()
; |
| 750 stream.start(); |
| 751 callback.setFailure(FailureType.THROW_SYNC, ResponseStep.ON_READ_COMPLET
ED); |
| 752 callback.blockForDone(); |
| 753 assertTrue(callback.mOnErrorCalled); |
| 754 } |
| 755 |
| 756 @SmallTest |
| 757 @Feature({"Cronet"}) |
| 758 public void testShutdownAfterCancel() throws Exception { |
| 759 TestBidirectionalStreamCallback callback = new ShutdownTestBidirectional
StreamCallback(); |
| 760 BidirectionalStream.Builder builder = |
| 761 new BidirectionalStream.Builder(Http2TestServer.getEchoMethodUrl
(), callback, |
| 762 callback.getExecutor(), mTestFramework.mCronetEngine); |
| 763 CronetBidirectionalStream stream = |
| 764 (CronetBidirectionalStream) builder.setHttpMethod("GET").build()
; |
| 765 |
| 766 // Block callback when response starts to verify that shutdown fails |
| 767 // if there are active requests. |
| 768 callback.setAutoAdvance(false); |
| 769 stream.start(); |
| 770 try { |
| 771 mTestFramework.mCronetEngine.shutdown(); |
| 772 fail("Should throw an exception"); |
| 773 } catch (Exception e) { |
| 774 assertEquals("Cannot shutdown with active requests.", e.getMessage()
); |
| 775 } |
| 776 callback.waitForNextReadStep(); |
| 777 assertEquals(ResponseStep.ON_RESPONSE_STARTED, callback.mResponseStep); |
| 778 stream.cancel(); |
| 779 } |
| 780 |
| 781 // Returns the contents of byteBuffer, from its position() to its limit(), |
| 782 // as a String. Does not modify byteBuffer's position(). |
| 783 private String bufferContentsToString(ByteBuffer byteBuffer, int start, int
end) { |
| 784 // Use a duplicate to avoid modifying byteBuffer. |
| 785 ByteBuffer duplicate = byteBuffer.duplicate(); |
| 786 duplicate.position(start); |
| 787 duplicate.limit(end); |
| 788 byte[] contents = new byte[duplicate.remaining()]; |
| 789 duplicate.get(contents); |
| 790 return new String(contents); |
| 791 } |
121 } | 792 } |
OLD | NEW |