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