CallTest.java revision a2cab72aa5ff730ba2ae987b45398faafffeb505
1/* 2 * Copyright (C) 2013 Square, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package com.squareup.okhttp; 17 18import com.squareup.okhttp.internal.DoubleInetAddressNetwork; 19import com.squareup.okhttp.internal.Internal; 20import com.squareup.okhttp.internal.RecordingHostnameVerifier; 21import com.squareup.okhttp.internal.RecordingOkAuthenticator; 22import com.squareup.okhttp.internal.SingleInetAddressNetwork; 23import com.squareup.okhttp.internal.SslContextBuilder; 24import com.squareup.okhttp.internal.Version; 25import com.squareup.okhttp.mockwebserver.Dispatcher; 26import com.squareup.okhttp.mockwebserver.MockResponse; 27import com.squareup.okhttp.mockwebserver.RecordedRequest; 28import com.squareup.okhttp.mockwebserver.SocketPolicy; 29import com.squareup.okhttp.mockwebserver.rule.MockWebServerRule; 30import java.io.IOException; 31import java.io.InputStream; 32import java.io.InterruptedIOException; 33import java.net.CookieManager; 34import java.net.HttpCookie; 35import java.net.HttpURLConnection; 36import java.net.URL; 37import java.net.UnknownServiceException; 38import java.security.cert.Certificate; 39import java.util.ArrayList; 40import java.util.Arrays; 41import java.util.Collection; 42import java.util.List; 43import java.util.concurrent.BlockingQueue; 44import java.util.concurrent.Callable; 45import java.util.concurrent.CountDownLatch; 46import java.util.concurrent.ExecutorService; 47import java.util.concurrent.Executors; 48import java.util.concurrent.Future; 49import java.util.concurrent.SynchronousQueue; 50import java.util.concurrent.TimeUnit; 51import java.util.concurrent.atomic.AtomicBoolean; 52import java.util.concurrent.atomic.AtomicReference; 53import javax.net.ssl.SSLContext; 54import javax.net.ssl.SSLHandshakeException; 55import javax.net.ssl.SSLPeerUnverifiedException; 56import javax.net.ssl.SSLProtocolException; 57import javax.net.ssl.SSLSocket; 58import javax.net.ssl.SSLSocketFactory; 59import okio.Buffer; 60import okio.BufferedSink; 61import okio.BufferedSource; 62import okio.GzipSink; 63import okio.Okio; 64import org.junit.After; 65import org.junit.Before; 66import org.junit.Ignore; 67import org.junit.Rule; 68import org.junit.Test; 69import org.junit.rules.TemporaryFolder; 70import org.junit.rules.TestRule; 71import org.junit.rules.Timeout; 72 73import static com.squareup.okhttp.internal.Internal.logger; 74import static java.net.CookiePolicy.ACCEPT_ORIGINAL_SERVER; 75import static org.junit.Assert.assertEquals; 76import static org.junit.Assert.assertFalse; 77import static org.junit.Assert.assertNotSame; 78import static org.junit.Assert.assertNull; 79import static org.junit.Assert.assertTrue; 80import static org.junit.Assert.fail; 81 82public final class CallTest { 83 private static final SSLContext sslContext = SslContextBuilder.localhost(); 84 85 @Rule public final TemporaryFolder tempDir = new TemporaryFolder(); 86 @Rule public final TestRule timeout = new Timeout(30_000); 87 88 @Rule public final MockWebServerRule server = new MockWebServerRule(); 89 @Rule public final MockWebServerRule server2 = new MockWebServerRule(); 90 private OkHttpClient client = new OkHttpClient(); 91 private RecordingCallback callback = new RecordingCallback(); 92 private TestLogHandler logHandler = new TestLogHandler(); 93 private Cache cache; 94 95 @Before public void setUp() throws Exception { 96 client = new OkHttpClient(); 97 callback = new RecordingCallback(); 98 logHandler = new TestLogHandler(); 99 100 cache = new Cache(tempDir.getRoot(), Integer.MAX_VALUE); 101 logger.addHandler(logHandler); 102 } 103 104 @After public void tearDown() throws Exception { 105 cache.delete(); 106 logger.removeHandler(logHandler); 107 } 108 109 @Test public void get() throws Exception { 110 server.enqueue(new MockResponse().setBody("abc").addHeader("Content-Type: text/plain")); 111 112 Request request = new Request.Builder() 113 .url(server.getUrl("/")) 114 .header("User-Agent", "SyncApiTest") 115 .build(); 116 117 executeSynchronously(request) 118 .assertCode(200) 119 .assertSuccessful() 120 .assertHeader("Content-Type", "text/plain") 121 .assertBody("abc"); 122 123 RecordedRequest recordedRequest = server.takeRequest(); 124 assertEquals("GET", recordedRequest.getMethod()); 125 assertEquals("SyncApiTest", recordedRequest.getHeader("User-Agent")); 126 assertEquals(0, recordedRequest.getBody().size()); 127 assertNull(recordedRequest.getHeader("Content-Length")); 128 } 129 130 @Test public void lazilyEvaluateRequestUrl() throws Exception { 131 server.enqueue(new MockResponse().setBody("abc")); 132 133 Request request1 = new Request.Builder() 134 .url("foo://bar?baz") 135 .build(); 136 Request request2 = request1.newBuilder() 137 .url(server.getUrl("/")) 138 .build(); 139 executeSynchronously(request2) 140 .assertCode(200) 141 .assertSuccessful() 142 .assertBody("abc"); 143 } 144 145 @Ignore // TODO(jwilson): fix. 146 @Test public void invalidScheme() throws Exception { 147 try { 148 Request request = new Request.Builder() 149 .url("ftp://hostname/path") 150 .build(); 151 executeSynchronously(request); 152 fail(); 153 } catch (IllegalArgumentException expected) { 154 } 155 } 156 157 @Test public void invalidPort() throws Exception { 158 Request request = new Request.Builder() 159 .url("http://localhost:65536/") 160 .build(); 161 client.newCall(request).enqueue(callback); 162 callback.await(request.url()) 163 .assertFailure("No route to localhost:65536; port is out of range"); 164 } 165 166 @Test public void getReturns500() throws Exception { 167 server.enqueue(new MockResponse().setResponseCode(500)); 168 169 Request request = new Request.Builder() 170 .url(server.getUrl("/")) 171 .build(); 172 173 executeSynchronously(request) 174 .assertCode(500) 175 .assertNotSuccessful(); 176 } 177 178 @Test public void get_SPDY_3() throws Exception { 179 enableProtocol(Protocol.SPDY_3); 180 get(); 181 } 182 183 @Test public void get_HTTP_2() throws Exception { 184 enableProtocol(Protocol.HTTP_2); 185 get(); 186 } 187 188 @Test public void getWithRequestBody() throws Exception { 189 server.enqueue(new MockResponse()); 190 191 try { 192 new Request.Builder().method("GET", RequestBody.create(MediaType.parse("text/plain"), "abc")); 193 fail(); 194 } catch (IllegalArgumentException expected) { 195 } 196 } 197 198 @Test public void head() throws Exception { 199 server.enqueue(new MockResponse().addHeader("Content-Type: text/plain")); 200 201 Request request = new Request.Builder() 202 .url(server.getUrl("/")) 203 .head() 204 .header("User-Agent", "SyncApiTest") 205 .build(); 206 207 executeSynchronously(request) 208 .assertCode(200) 209 .assertHeader("Content-Type", "text/plain"); 210 211 RecordedRequest recordedRequest = server.takeRequest(); 212 assertEquals("HEAD", recordedRequest.getMethod()); 213 assertEquals("SyncApiTest", recordedRequest.getHeader("User-Agent")); 214 assertEquals(0, recordedRequest.getBody().size()); 215 assertNull(recordedRequest.getHeader("Content-Length")); 216 } 217 218 @Test public void head_SPDY_3() throws Exception { 219 enableProtocol(Protocol.SPDY_3); 220 head(); 221 } 222 223 @Test public void head_HTTP_2() throws Exception { 224 enableProtocol(Protocol.HTTP_2); 225 head(); 226 } 227 228 @Test public void post() throws Exception { 229 server.enqueue(new MockResponse().setBody("abc")); 230 231 Request request = new Request.Builder() 232 .url(server.getUrl("/")) 233 .post(RequestBody.create(MediaType.parse("text/plain"), "def")) 234 .build(); 235 236 executeSynchronously(request) 237 .assertCode(200) 238 .assertBody("abc"); 239 240 RecordedRequest recordedRequest = server.takeRequest(); 241 assertEquals("POST", recordedRequest.getMethod()); 242 assertEquals("def", recordedRequest.getBody().readUtf8()); 243 assertEquals("3", recordedRequest.getHeader("Content-Length")); 244 assertEquals("text/plain; charset=utf-8", recordedRequest.getHeader("Content-Type")); 245 } 246 247 @Test public void post_SPDY_3() throws Exception { 248 enableProtocol(Protocol.SPDY_3); 249 post(); 250 } 251 252 @Test public void post_HTTP_2() throws Exception { 253 enableProtocol(Protocol.HTTP_2); 254 post(); 255 } 256 257 @Test public void postZeroLength() throws Exception { 258 server.enqueue(new MockResponse().setBody("abc")); 259 260 Request request = new Request.Builder() 261 .url(server.getUrl("/")) 262 .method("POST", null) 263 .build(); 264 265 executeSynchronously(request) 266 .assertCode(200) 267 .assertBody("abc"); 268 269 RecordedRequest recordedRequest = server.takeRequest(); 270 assertEquals("POST", recordedRequest.getMethod()); 271 assertEquals(0, recordedRequest.getBody().size()); 272 assertEquals("0", recordedRequest.getHeader("Content-Length")); 273 assertEquals(null, recordedRequest.getHeader("Content-Type")); 274 } 275 276 @Test public void postZeroLength_SPDY_3() throws Exception { 277 enableProtocol(Protocol.SPDY_3); 278 postZeroLength(); 279 } 280 281 @Test public void postZerolength_HTTP_2() throws Exception { 282 enableProtocol(Protocol.HTTP_2); 283 postZeroLength(); 284 } 285 286 @Test public void postBodyRetransmittedAfterAuthorizationFail() throws Exception { 287 postBodyRetransmittedAfterAuthorizationFail("abc"); 288 } 289 290 @Test public void postBodyRetransmittedAfterAuthorizationFail_SPDY_3() throws Exception { 291 enableProtocol(Protocol.SPDY_3); 292 postBodyRetransmittedAfterAuthorizationFail("abc"); 293 } 294 295 @Test public void postBodyRetransmittedAfterAuthorizationFail_HTTP_2() throws Exception { 296 enableProtocol(Protocol.HTTP_2); 297 postBodyRetransmittedAfterAuthorizationFail("abc"); 298 } 299 300 /** Don't explode when resending an empty post. https://github.com/square/okhttp/issues/1131 */ 301 @Test public void postEmptyBodyRetransmittedAfterAuthorizationFail() throws Exception { 302 postBodyRetransmittedAfterAuthorizationFail(""); 303 } 304 305 @Test public void postEmptyBodyRetransmittedAfterAuthorizationFail_SPDY_3() throws Exception { 306 enableProtocol(Protocol.SPDY_3); 307 postBodyRetransmittedAfterAuthorizationFail(""); 308 } 309 310 @Test public void postEmptyBodyRetransmittedAfterAuthorizationFail_HTTP_2() throws Exception { 311 enableProtocol(Protocol.HTTP_2); 312 postBodyRetransmittedAfterAuthorizationFail(""); 313 } 314 315 private void postBodyRetransmittedAfterAuthorizationFail(String body) throws Exception { 316 server.enqueue(new MockResponse().setResponseCode(401)); 317 server.enqueue(new MockResponse()); 318 319 Request request = new Request.Builder() 320 .url(server.getUrl("/")) 321 .method("POST", RequestBody.create(null, body)) 322 .build(); 323 324 String credential = Credentials.basic("jesse", "secret"); 325 client.setAuthenticator(new RecordingOkAuthenticator(credential)); 326 327 Response response = client.newCall(request).execute(); 328 assertEquals(200, response.code()); 329 330 RecordedRequest recordedRequest1 = server.takeRequest(); 331 assertEquals("POST", recordedRequest1.getMethod()); 332 assertEquals(body, recordedRequest1.getBody().readUtf8()); 333 assertNull(recordedRequest1.getHeader("Authorization")); 334 335 RecordedRequest recordedRequest2 = server.takeRequest(); 336 assertEquals("POST", recordedRequest2.getMethod()); 337 assertEquals(body, recordedRequest2.getBody().readUtf8()); 338 assertEquals(credential, recordedRequest2.getHeader("Authorization")); 339 } 340 341 @Test public void attemptAuthorization20Times() throws Exception { 342 for (int i = 0; i < 20; i++) { 343 server.enqueue(new MockResponse().setResponseCode(401)); 344 } 345 server.enqueue(new MockResponse().setBody("Success!")); 346 347 String credential = Credentials.basic("jesse", "secret"); 348 client.setAuthenticator(new RecordingOkAuthenticator(credential)); 349 350 Request request = new Request.Builder().url(server.getUrl("/")).build(); 351 executeSynchronously(request) 352 .assertCode(200) 353 .assertBody("Success!"); 354 } 355 356 @Test public void doesNotAttemptAuthorization21Times() throws Exception { 357 for (int i = 0; i < 21; i++) { 358 server.enqueue(new MockResponse().setResponseCode(401)); 359 } 360 361 String credential = Credentials.basic("jesse", "secret"); 362 client.setAuthenticator(new RecordingOkAuthenticator(credential)); 363 364 try { 365 client.newCall(new Request.Builder().url(server.getUrl("/0")).build()).execute(); 366 fail(); 367 } catch (IOException expected) { 368 assertEquals("Too many follow-up requests: 21", expected.getMessage()); 369 } 370 } 371 372 @Test public void delete() throws Exception { 373 server.enqueue(new MockResponse().setBody("abc")); 374 375 Request request = new Request.Builder() 376 .url(server.getUrl("/")) 377 .delete() 378 .build(); 379 380 executeSynchronously(request) 381 .assertCode(200) 382 .assertBody("abc"); 383 384 RecordedRequest recordedRequest = server.takeRequest(); 385 assertEquals("DELETE", recordedRequest.getMethod()); 386 assertEquals(0, recordedRequest.getBody().size()); 387 assertEquals("0", recordedRequest.getHeader("Content-Length")); 388 assertEquals(null, recordedRequest.getHeader("Content-Type")); 389 } 390 391 @Test public void delete_SPDY_3() throws Exception { 392 enableProtocol(Protocol.SPDY_3); 393 delete(); 394 } 395 396 @Test public void delete_HTTP_2() throws Exception { 397 enableProtocol(Protocol.HTTP_2); 398 delete(); 399 } 400 401 @Test public void deleteWithRequestBody() throws Exception { 402 server.enqueue(new MockResponse().setBody("abc")); 403 404 Request request = new Request.Builder() 405 .url(server.getUrl("/")) 406 .method("DELETE", RequestBody.create(MediaType.parse("text/plain"), "def")) 407 .build(); 408 409 executeSynchronously(request) 410 .assertCode(200) 411 .assertBody("abc"); 412 413 RecordedRequest recordedRequest = server.takeRequest(); 414 assertEquals("DELETE", recordedRequest.getMethod()); 415 assertEquals("def", recordedRequest.getBody().readUtf8()); 416 } 417 418 @Test public void put() throws Exception { 419 server.enqueue(new MockResponse().setBody("abc")); 420 421 Request request = new Request.Builder() 422 .url(server.getUrl("/")) 423 .put(RequestBody.create(MediaType.parse("text/plain"), "def")) 424 .build(); 425 426 executeSynchronously(request) 427 .assertCode(200) 428 .assertBody("abc"); 429 430 RecordedRequest recordedRequest = server.takeRequest(); 431 assertEquals("PUT", recordedRequest.getMethod()); 432 assertEquals("def", recordedRequest.getBody().readUtf8()); 433 assertEquals("3", recordedRequest.getHeader("Content-Length")); 434 assertEquals("text/plain; charset=utf-8", recordedRequest.getHeader("Content-Type")); 435 } 436 437 @Test public void put_SPDY_3() throws Exception { 438 enableProtocol(Protocol.SPDY_3); 439 put(); 440 } 441 442 @Test public void put_HTTP_2() throws Exception { 443 enableProtocol(Protocol.HTTP_2); 444 put(); 445 } 446 447 @Test public void patch() throws Exception { 448 server.enqueue(new MockResponse().setBody("abc")); 449 450 Request request = new Request.Builder() 451 .url(server.getUrl("/")) 452 .patch(RequestBody.create(MediaType.parse("text/plain"), "def")) 453 .build(); 454 455 executeSynchronously(request) 456 .assertCode(200) 457 .assertBody("abc"); 458 459 RecordedRequest recordedRequest = server.takeRequest(); 460 assertEquals("PATCH", recordedRequest.getMethod()); 461 assertEquals("def", recordedRequest.getBody().readUtf8()); 462 assertEquals("3", recordedRequest.getHeader("Content-Length")); 463 assertEquals("text/plain; charset=utf-8", recordedRequest.getHeader("Content-Type")); 464 } 465 466 @Test public void patch_SPDY_3() throws Exception { 467 enableProtocol(Protocol.SPDY_3); 468 patch(); 469 } 470 471 @Test public void patch_HTTP_2() throws Exception { 472 enableProtocol(Protocol.HTTP_2); 473 patch(); 474 } 475 476 @Test public void unspecifiedRequestBodyContentTypeDoesNotGetDefault() throws Exception { 477 server.enqueue(new MockResponse()); 478 479 Request request = new Request.Builder() 480 .url(server.getUrl("/")) 481 .method("POST", RequestBody.create(null, "abc")) 482 .build(); 483 484 executeSynchronously(request).assertCode(200); 485 486 RecordedRequest recordedRequest = server.takeRequest(); 487 assertEquals(null, recordedRequest.getHeader("Content-Type")); 488 assertEquals("3", recordedRequest.getHeader("Content-Length")); 489 assertEquals("abc", recordedRequest.getBody().readUtf8()); 490 } 491 492 @Test public void illegalToExecuteTwice() throws Exception { 493 server.enqueue(new MockResponse() 494 .setBody("abc") 495 .addHeader("Content-Type: text/plain")); 496 497 Request request = new Request.Builder() 498 .url(server.getUrl("/")) 499 .header("User-Agent", "SyncApiTest") 500 .build(); 501 502 Call call = client.newCall(request); 503 call.execute(); 504 505 try { 506 call.execute(); 507 fail(); 508 } catch (IllegalStateException e){ 509 assertEquals("Already Executed", e.getMessage()); 510 } 511 512 try { 513 call.enqueue(callback); 514 fail(); 515 } catch (IllegalStateException e){ 516 assertEquals("Already Executed", e.getMessage()); 517 } 518 519 assertEquals("SyncApiTest", server.takeRequest().getHeader("User-Agent")); 520 } 521 522 @Test public void illegalToExecuteTwice_Async() throws Exception { 523 server.enqueue(new MockResponse() 524 .setBody("abc") 525 .addHeader("Content-Type: text/plain")); 526 527 Request request = new Request.Builder() 528 .url(server.getUrl("/")) 529 .header("User-Agent", "SyncApiTest") 530 .build(); 531 532 Call call = client.newCall(request); 533 call.enqueue(callback); 534 535 try { 536 call.execute(); 537 fail(); 538 } catch (IllegalStateException e){ 539 assertEquals("Already Executed", e.getMessage()); 540 } 541 542 try { 543 call.enqueue(callback); 544 fail(); 545 } catch (IllegalStateException e){ 546 assertEquals("Already Executed", e.getMessage()); 547 } 548 549 assertEquals("SyncApiTest", server.takeRequest().getHeader("User-Agent")); 550 } 551 552 @Test public void get_Async() throws Exception { 553 server.enqueue(new MockResponse() 554 .setBody("abc") 555 .addHeader("Content-Type: text/plain")); 556 557 Request request = new Request.Builder() 558 .url(server.getUrl("/")) 559 .header("User-Agent", "AsyncApiTest") 560 .build(); 561 client.newCall(request).enqueue(callback); 562 563 callback.await(request.url()) 564 .assertCode(200) 565 .assertHeader("Content-Type", "text/plain") 566 .assertBody("abc"); 567 568 assertEquals("AsyncApiTest", server.takeRequest().getHeader("User-Agent")); 569 } 570 571 @Test public void exceptionThrownByOnResponseIsRedactedAndLogged() throws Exception { 572 server.enqueue(new MockResponse()); 573 574 Request request = new Request.Builder() 575 .url(server.getUrl("/secret")) 576 .build(); 577 578 client.newCall(request).enqueue(new Callback() { 579 @Override public void onFailure(Request request, IOException e) { 580 fail(); 581 } 582 583 @Override public void onResponse(Response response) throws IOException { 584 throw new IOException("a"); 585 } 586 }); 587 588 assertEquals("INFO: Callback failure for call to " + server.getUrl("/") + "...", 589 logHandler.take()); 590 } 591 592 @Test public void connectionPooling() throws Exception { 593 server.enqueue(new MockResponse().setBody("abc")); 594 server.enqueue(new MockResponse().setBody("def")); 595 server.enqueue(new MockResponse().setBody("ghi")); 596 597 executeSynchronously(new Request.Builder().url(server.getUrl("/a")).build()) 598 .assertBody("abc"); 599 600 executeSynchronously(new Request.Builder().url(server.getUrl("/b")).build()) 601 .assertBody("def"); 602 603 executeSynchronously(new Request.Builder().url(server.getUrl("/c")).build()) 604 .assertBody("ghi"); 605 606 assertEquals(0, server.takeRequest().getSequenceNumber()); 607 assertEquals(1, server.takeRequest().getSequenceNumber()); 608 assertEquals(2, server.takeRequest().getSequenceNumber()); 609 } 610 611 @Test public void connectionPooling_Async() throws Exception { 612 server.enqueue(new MockResponse().setBody("abc")); 613 server.enqueue(new MockResponse().setBody("def")); 614 server.enqueue(new MockResponse().setBody("ghi")); 615 616 client.newCall(new Request.Builder().url(server.getUrl("/a")).build()).enqueue(callback); 617 callback.await(server.getUrl("/a")).assertBody("abc"); 618 619 client.newCall(new Request.Builder().url(server.getUrl("/b")).build()).enqueue(callback); 620 callback.await(server.getUrl("/b")).assertBody("def"); 621 622 client.newCall(new Request.Builder().url(server.getUrl("/c")).build()).enqueue(callback); 623 callback.await(server.getUrl("/c")).assertBody("ghi"); 624 625 assertEquals(0, server.takeRequest().getSequenceNumber()); 626 assertEquals(1, server.takeRequest().getSequenceNumber()); 627 assertEquals(2, server.takeRequest().getSequenceNumber()); 628 } 629 630 @Test public void connectionReuseWhenResponseBodyConsumed_Async() throws Exception { 631 server.enqueue(new MockResponse().setBody("abc")); 632 server.enqueue(new MockResponse().setBody("def")); 633 634 Request request = new Request.Builder().url(server.getUrl("/a")).build(); 635 client.newCall(request).enqueue(new Callback() { 636 @Override public void onFailure(Request request, IOException e) { 637 throw new AssertionError(); 638 } 639 640 @Override public void onResponse(Response response) throws IOException { 641 InputStream bytes = response.body().byteStream(); 642 assertEquals('a', bytes.read()); 643 assertEquals('b', bytes.read()); 644 assertEquals('c', bytes.read()); 645 646 // This request will share a connection with 'A' cause it's all done. 647 client.newCall(new Request.Builder().url(server.getUrl("/b")).build()).enqueue(callback); 648 } 649 }); 650 651 callback.await(server.getUrl("/b")).assertCode(200).assertBody("def"); 652 assertEquals(0, server.takeRequest().getSequenceNumber()); // New connection. 653 assertEquals(1, server.takeRequest().getSequenceNumber()); // Connection reuse! 654 } 655 656 @Test public void timeoutsUpdatedOnReusedConnections() throws Exception { 657 server.enqueue(new MockResponse().setBody("abc")); 658 server.enqueue(new MockResponse().setBody("def").throttleBody(1, 750, TimeUnit.MILLISECONDS)); 659 660 // First request: time out after 1000ms. 661 client.setReadTimeout(1000, TimeUnit.MILLISECONDS); 662 executeSynchronously(new Request.Builder().url(server.getUrl("/a")).build()).assertBody("abc"); 663 664 // Second request: time out after 250ms. 665 client.setReadTimeout(250, TimeUnit.MILLISECONDS); 666 Request request = new Request.Builder().url(server.getUrl("/b")).build(); 667 Response response = client.newCall(request).execute(); 668 BufferedSource bodySource = response.body().source(); 669 assertEquals('d', bodySource.readByte()); 670 671 // The second byte of this request will be delayed by 750ms so we should time out after 250ms. 672 long startNanos = System.nanoTime(); 673 try { 674 bodySource.readByte(); 675 fail(); 676 } catch (IOException expected) { 677 // Timed out as expected. 678 long elapsedNanos = System.nanoTime() - startNanos; 679 long elapsedMillis = TimeUnit.NANOSECONDS.toMillis(elapsedNanos); 680 assertTrue(String.format("Timed out: %sms", elapsedMillis), elapsedMillis < 500); 681 } 682 } 683 684 @Test public void timeoutsNotRetried() throws Exception { 685 server.enqueue(new MockResponse() 686 .setSocketPolicy(SocketPolicy.NO_RESPONSE)); 687 server.enqueue(new MockResponse() 688 .setBody("unreachable!")); 689 690 Internal.instance.setNetwork(client, new DoubleInetAddressNetwork()); 691 client.setReadTimeout(100, TimeUnit.MILLISECONDS); 692 693 Request request = new Request.Builder().url(server.getUrl("/")).build(); 694 try { 695 // If this succeeds, too many requests were made. 696 client.newCall(request).execute(); 697 fail(); 698 } catch (InterruptedIOException expected) { 699 } 700 } 701 702 @Test public void tls() throws Exception { 703 server.get().useHttps(sslContext.getSocketFactory(), false); 704 server.enqueue(new MockResponse() 705 .setBody("abc") 706 .addHeader("Content-Type: text/plain")); 707 708 client.setSslSocketFactory(sslContext.getSocketFactory()); 709 client.setHostnameVerifier(new RecordingHostnameVerifier()); 710 711 executeSynchronously(new Request.Builder().url(server.getUrl("/")).build()) 712 .assertHandshake(); 713 } 714 715 @Test public void tls_Async() throws Exception { 716 server.get().useHttps(sslContext.getSocketFactory(), false); 717 server.enqueue(new MockResponse() 718 .setBody("abc") 719 .addHeader("Content-Type: text/plain")); 720 721 client.setSslSocketFactory(sslContext.getSocketFactory()); 722 client.setHostnameVerifier(new RecordingHostnameVerifier()); 723 724 Request request = new Request.Builder() 725 .url(server.getUrl("/")) 726 .build(); 727 client.newCall(request).enqueue(callback); 728 729 callback.await(request.url()).assertHandshake(); 730 } 731 732 @Test public void recoverWhenRetryOnConnectionFailureIsTrue() throws Exception { 733 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AT_START)); 734 server.enqueue(new MockResponse().setBody("retry success")); 735 736 Internal.instance.setNetwork(client, new DoubleInetAddressNetwork()); 737 assertTrue(client.getRetryOnConnectionFailure()); 738 739 Request request = new Request.Builder().url(server.getUrl("/")).build(); 740 Response response = client.newCall(request).execute(); 741 assertEquals("retry success", response.body().string()); 742 } 743 744 @Test public void noRecoverWhenRetryOnConnectionFailureIsFalse() throws Exception { 745 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AT_START)); 746 server.enqueue(new MockResponse().setBody("unreachable!")); 747 748 Internal.instance.setNetwork(client, new DoubleInetAddressNetwork()); 749 client.setRetryOnConnectionFailure(false); 750 751 Request request = new Request.Builder().url(server.getUrl("/")).build(); 752 try { 753 // If this succeeds, too many requests were made. 754 client.newCall(request).execute(); 755 fail(); 756 } catch (IOException expected) { 757 } 758 } 759 760 @Test public void recoverFromTlsHandshakeFailure() throws Exception { 761 server.get().useHttps(sslContext.getSocketFactory(), false); 762 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE)); 763 server.enqueue(new MockResponse().setBody("abc")); 764 765 suppressTlsFallbackScsv(client); 766 client.setHostnameVerifier(new RecordingHostnameVerifier()); 767 Internal.instance.setNetwork(client, new SingleInetAddressNetwork()); 768 769 executeSynchronously(new Request.Builder().url(server.getUrl("/")).build()) 770 .assertBody("abc"); 771 } 772 773 @Test public void recoverFromTlsHandshakeFailure_tlsFallbackScsvEnabled() throws Exception { 774 final String tlsFallbackScsv = "TLS_FALLBACK_SCSV"; 775 List<String> supportedCiphers = 776 Arrays.asList(sslContext.getSocketFactory().getSupportedCipherSuites()); 777 if (!supportedCiphers.contains(tlsFallbackScsv)) { 778 // This only works if the client socket supports TLS_FALLBACK_SCSV. 779 return; 780 } 781 782 server.get().useHttps(sslContext.getSocketFactory(), false); 783 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE)); 784 785 RecordingSSLSocketFactory clientSocketFactory = 786 new RecordingSSLSocketFactory(sslContext.getSocketFactory()); 787 client.setSslSocketFactory(clientSocketFactory); 788 client.setHostnameVerifier(new RecordingHostnameVerifier()); 789 Internal.instance.setNetwork(client, new SingleInetAddressNetwork()); 790 791 Request request = new Request.Builder().url(server.getUrl("/")).build(); 792 try { 793 client.newCall(request).execute(); 794 fail(); 795 } catch (SSLHandshakeException expected) { 796 } 797 798 List<SSLSocket> clientSockets = clientSocketFactory.getSocketsCreated(); 799 SSLSocket firstSocket = clientSockets.get(0); 800 assertFalse(Arrays.asList(firstSocket.getEnabledCipherSuites()).contains(tlsFallbackScsv)); 801 SSLSocket secondSocket = clientSockets.get(1); 802 assertTrue(Arrays.asList(secondSocket.getEnabledCipherSuites()).contains(tlsFallbackScsv)); 803 } 804 805 @Test public void recoverFromTlsHandshakeFailure_Async() throws Exception { 806 server.get().useHttps(sslContext.getSocketFactory(), false); 807 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE)); 808 server.enqueue(new MockResponse().setBody("abc")); 809 810 suppressTlsFallbackScsv(client); 811 client.setHostnameVerifier(new RecordingHostnameVerifier()); 812 813 Request request = new Request.Builder() 814 .url(server.getUrl("/")) 815 .build(); 816 client.newCall(request).enqueue(callback); 817 818 callback.await(request.url()).assertBody("abc"); 819 } 820 821 @Test public void noRecoveryFromTlsHandshakeFailureWhenTlsFallbackIsDisabled() throws Exception { 822 client.setConnectionSpecs(Arrays.asList(ConnectionSpec.MODERN_TLS, ConnectionSpec.CLEARTEXT)); 823 824 server.get().useHttps(sslContext.getSocketFactory(), false); 825 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE)); 826 827 suppressTlsFallbackScsv(client); 828 client.setHostnameVerifier(new RecordingHostnameVerifier()); 829 Internal.instance.setNetwork(client, new SingleInetAddressNetwork()); 830 831 Request request = new Request.Builder().url(server.getUrl("/")).build(); 832 try { 833 client.newCall(request).execute(); 834 fail(); 835 } catch (SSLProtocolException expected) { 836 // RI response to the FAIL_HANDSHAKE 837 } catch (SSLHandshakeException expected) { 838 // Android's response to the FAIL_HANDSHAKE 839 } 840 } 841 842 @Test public void cleartextCallsFailWhenCleartextIsDisabled() throws Exception { 843 // Configure the client with only TLS configurations. No cleartext! 844 client.setConnectionSpecs( 845 Arrays.asList(ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS)); 846 847 server.enqueue(new MockResponse()); 848 849 Request request = new Request.Builder().url(server.getUrl("/")).build(); 850 try { 851 client.newCall(request).execute(); 852 fail(); 853 } catch (UnknownServiceException expected) { 854 assertTrue(expected.getMessage().contains("no connection specs")); 855 } 856 } 857 858 @Test public void setFollowSslRedirectsFalse() throws Exception { 859 server.get().useHttps(sslContext.getSocketFactory(), false); 860 server.enqueue(new MockResponse().setResponseCode(301).addHeader("Location: http://square.com")); 861 862 client.setFollowSslRedirects(false); 863 client.setSslSocketFactory(sslContext.getSocketFactory()); 864 client.setHostnameVerifier(new RecordingHostnameVerifier()); 865 866 Request request = new Request.Builder().url(server.getUrl("/")).build(); 867 Response response = client.newCall(request).execute(); 868 assertEquals(301, response.code()); 869 } 870 871 @Test public void matchingPinnedCertificate() throws Exception { 872 server.get().useHttps(sslContext.getSocketFactory(), false); 873 server.enqueue(new MockResponse()); 874 server.enqueue(new MockResponse()); 875 876 client.setSslSocketFactory(sslContext.getSocketFactory()); 877 client.setHostnameVerifier(new RecordingHostnameVerifier()); 878 879 // Make a first request without certificate pinning. Use it to collect certificates to pin. 880 Request request1 = new Request.Builder().url(server.getUrl("/")).build(); 881 Response response1 = client.newCall(request1).execute(); 882 CertificatePinner.Builder certificatePinnerBuilder = new CertificatePinner.Builder(); 883 for (Certificate certificate : response1.handshake().peerCertificates()) { 884 certificatePinnerBuilder.add(server.get().getHostName(), CertificatePinner.pin(certificate)); 885 } 886 887 // Make another request with certificate pinning. It should complete normally. 888 client.setCertificatePinner(certificatePinnerBuilder.build()); 889 Request request2 = new Request.Builder().url(server.getUrl("/")).build(); 890 Response response2 = client.newCall(request2).execute(); 891 assertNotSame(response2.handshake(), response1.handshake()); 892 } 893 894 @Test public void unmatchingPinnedCertificate() throws Exception { 895 server.get().useHttps(sslContext.getSocketFactory(), false); 896 server.enqueue(new MockResponse()); 897 898 client.setSslSocketFactory(sslContext.getSocketFactory()); 899 client.setHostnameVerifier(new RecordingHostnameVerifier()); 900 901 // Pin publicobject.com's cert. 902 client.setCertificatePinner(new CertificatePinner.Builder() 903 .add(server.get().getHostName(), "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=") 904 .build()); 905 906 // When we pin the wrong certificate, connectivity fails. 907 Request request = new Request.Builder().url(server.getUrl("/")).build(); 908 try { 909 client.newCall(request).execute(); 910 fail(); 911 } catch (SSLPeerUnverifiedException expected) { 912 assertTrue(expected.getMessage().startsWith("Certificate pinning failure!")); 913 } 914 } 915 916 @Test public void post_Async() throws Exception { 917 server.enqueue(new MockResponse().setBody("abc")); 918 919 Request request = new Request.Builder() 920 .url(server.getUrl("/")) 921 .post(RequestBody.create(MediaType.parse("text/plain"), "def")) 922 .build(); 923 client.newCall(request).enqueue(callback); 924 925 callback.await(request.url()) 926 .assertCode(200) 927 .assertBody("abc"); 928 929 RecordedRequest recordedRequest = server.takeRequest(); 930 assertEquals("def", recordedRequest.getBody().readUtf8()); 931 assertEquals("3", recordedRequest.getHeader("Content-Length")); 932 assertEquals("text/plain; charset=utf-8", recordedRequest.getHeader("Content-Type")); 933 } 934 935 @Test public void postBodyRetransmittedOnFailureRecovery() throws Exception { 936 server.enqueue(new MockResponse().setBody("abc")); 937 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AFTER_REQUEST)); 938 server.enqueue(new MockResponse().setBody("def")); 939 940 // Seed the connection pool so we have something that can fail. 941 Request request1 = new Request.Builder().url(server.getUrl("/")).build(); 942 Response response1 = client.newCall(request1).execute(); 943 assertEquals("abc", response1.body().string()); 944 945 Request request2 = new Request.Builder() 946 .url(server.getUrl("/")) 947 .post(RequestBody.create(MediaType.parse("text/plain"), "body!")) 948 .build(); 949 Response response2 = client.newCall(request2).execute(); 950 assertEquals("def", response2.body().string()); 951 952 RecordedRequest get = server.takeRequest(); 953 assertEquals(0, get.getSequenceNumber()); 954 955 RecordedRequest post1 = server.takeRequest(); 956 assertEquals("body!", post1.getBody().readUtf8()); 957 assertEquals(1, post1.getSequenceNumber()); 958 959 RecordedRequest post2 = server.takeRequest(); 960 assertEquals("body!", post2.getBody().readUtf8()); 961 assertEquals(0, post2.getSequenceNumber()); 962 } 963 964 @Test public void cacheHit() throws Exception { 965 server.enqueue(new MockResponse() 966 .addHeader("ETag: v1") 967 .addHeader("Cache-Control: max-age=60") 968 .addHeader("Vary: Accept-Charset") 969 .setBody("A")); 970 971 client.setCache(cache); 972 973 // Store a response in the cache. 974 URL url = server.getUrl("/"); 975 Request cacheStoreRequest = new Request.Builder() 976 .url(url) 977 .addHeader("Accept-Language", "fr-CA") 978 .addHeader("Accept-Charset", "UTF-8") 979 .build(); 980 executeSynchronously(cacheStoreRequest) 981 .assertCode(200) 982 .assertBody("A"); 983 assertNull(server.takeRequest().getHeader("If-None-Match")); 984 985 // Hit that stored response. 986 Request cacheHitRequest = new Request.Builder() 987 .url(url) 988 .addHeader("Accept-Language", "en-US") // Different, but Vary says it doesn't matter. 989 .addHeader("Accept-Charset", "UTF-8") 990 .build(); 991 RecordedResponse cacheHit = executeSynchronously(cacheHitRequest); 992 993 // Check the merged response. The request is the application's original request. 994 cacheHit.assertCode(200) 995 .assertBody("A") 996 .assertHeader("ETag", "v1") 997 .assertRequestUrl(cacheStoreRequest.url()) 998 .assertRequestHeader("Accept-Language", "en-US") 999 .assertRequestHeader("Accept-Charset", "UTF-8"); 1000 1001 // Check the cached response. Its request contains only the saved Vary headers. 1002 cacheHit.cacheResponse() 1003 .assertCode(200) 1004 .assertHeader("ETag", "v1") 1005 .assertRequestMethod("GET") 1006 .assertRequestUrl(cacheStoreRequest.url()) 1007 .assertRequestHeader("Accept-Language") 1008 .assertRequestHeader("Accept-Charset", "UTF-8"); 1009 1010 cacheHit.assertNoNetworkResponse(); 1011 } 1012 1013 @Test public void conditionalCacheHit() throws Exception { 1014 server.enqueue(new MockResponse() 1015 .addHeader("ETag: v1") 1016 .addHeader("Vary: Accept-Charset") 1017 .addHeader("Donut: a") 1018 .setBody("A")); 1019 server.enqueue(new MockResponse().clearHeaders() 1020 .addHeader("Donut: b") 1021 .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED)); 1022 1023 client.setCache(cache); 1024 1025 // Store a response in the cache. 1026 URL url = server.getUrl("/"); 1027 Request cacheStoreRequest = new Request.Builder() 1028 .url(url) 1029 .addHeader("Accept-Language", "fr-CA") 1030 .addHeader("Accept-Charset", "UTF-8") 1031 .build(); 1032 executeSynchronously(cacheStoreRequest) 1033 .assertCode(200) 1034 .assertHeader("Donut", "a") 1035 .assertBody("A"); 1036 assertNull(server.takeRequest().getHeader("If-None-Match")); 1037 1038 // Hit that stored response. 1039 Request cacheHitRequest = new Request.Builder() 1040 .url(url) 1041 .addHeader("Accept-Language", "en-US") // Different, but Vary says it doesn't matter. 1042 .addHeader("Accept-Charset", "UTF-8") 1043 .build(); 1044 RecordedResponse cacheHit = executeSynchronously(cacheHitRequest); 1045 assertEquals("v1", server.takeRequest().getHeader("If-None-Match")); 1046 1047 // Check the merged response. The request is the application's original request. 1048 cacheHit.assertCode(200) 1049 .assertBody("A") 1050 .assertHeader("Donut", "b") 1051 .assertRequestUrl(cacheStoreRequest.url()) 1052 .assertRequestHeader("Accept-Language", "en-US") 1053 .assertRequestHeader("Accept-Charset", "UTF-8") 1054 .assertRequestHeader("If-None-Match"); // No If-None-Match on the user's request. 1055 1056 // Check the cached response. Its request contains only the saved Vary headers. 1057 cacheHit.cacheResponse() 1058 .assertCode(200) 1059 .assertHeader("Donut", "a") 1060 .assertHeader("ETag", "v1") 1061 .assertRequestUrl(cacheStoreRequest.url()) 1062 .assertRequestHeader("Accept-Language") // No Vary on Accept-Language. 1063 .assertRequestHeader("Accept-Charset", "UTF-8") // Because of Vary on Accept-Charset. 1064 .assertRequestHeader("If-None-Match"); // This wasn't present in the original request. 1065 1066 // Check the network response. It has the caller's request, plus some caching headers. 1067 cacheHit.networkResponse() 1068 .assertCode(304) 1069 .assertHeader("Donut", "b") 1070 .assertRequestHeader("Accept-Language", "en-US") 1071 .assertRequestHeader("Accept-Charset", "UTF-8") 1072 .assertRequestHeader("If-None-Match", "v1"); // If-None-Match in the validation request. 1073 } 1074 1075 @Test public void conditionalCacheHit_Async() throws Exception { 1076 server.enqueue(new MockResponse().setBody("A").addHeader("ETag: v1")); 1077 server.enqueue(new MockResponse() 1078 .clearHeaders() 1079 .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED)); 1080 1081 client.setCache(cache); 1082 1083 Request request1 = new Request.Builder() 1084 .url(server.getUrl("/")) 1085 .build(); 1086 client.newCall(request1).enqueue(callback); 1087 callback.await(request1.url()).assertCode(200).assertBody("A"); 1088 assertNull(server.takeRequest().getHeader("If-None-Match")); 1089 1090 Request request2 = new Request.Builder() 1091 .url(server.getUrl("/")) 1092 .build(); 1093 client.newCall(request2).enqueue(callback); 1094 callback.await(request2.url()).assertCode(200).assertBody("A"); 1095 assertEquals("v1", server.takeRequest().getHeader("If-None-Match")); 1096 } 1097 1098 @Test public void conditionalCacheMiss() throws Exception { 1099 server.enqueue(new MockResponse() 1100 .addHeader("ETag: v1") 1101 .addHeader("Vary: Accept-Charset") 1102 .addHeader("Donut: a") 1103 .setBody("A")); 1104 server.enqueue(new MockResponse() 1105 .addHeader("Donut: b") 1106 .setBody("B")); 1107 1108 client.setCache(cache); 1109 1110 Request cacheStoreRequest = new Request.Builder() 1111 .url(server.getUrl("/")) 1112 .addHeader("Accept-Language", "fr-CA") 1113 .addHeader("Accept-Charset", "UTF-8") 1114 .build(); 1115 executeSynchronously(cacheStoreRequest) 1116 .assertCode(200) 1117 .assertBody("A"); 1118 assertNull(server.takeRequest().getHeader("If-None-Match")); 1119 1120 Request cacheMissRequest = new Request.Builder() 1121 .url(server.getUrl("/")) 1122 .addHeader("Accept-Language", "en-US") // Different, but Vary says it doesn't matter. 1123 .addHeader("Accept-Charset", "UTF-8") 1124 .build(); 1125 RecordedResponse cacheHit = executeSynchronously(cacheMissRequest); 1126 assertEquals("v1", server.takeRequest().getHeader("If-None-Match")); 1127 1128 // Check the user response. It has the application's original request. 1129 cacheHit.assertCode(200) 1130 .assertBody("B") 1131 .assertHeader("Donut", "b") 1132 .assertRequestUrl(cacheStoreRequest.url()); 1133 1134 // Check the cache response. Even though it's a miss, we used the cache. 1135 cacheHit.cacheResponse() 1136 .assertCode(200) 1137 .assertHeader("Donut", "a") 1138 .assertHeader("ETag", "v1") 1139 .assertRequestUrl(cacheStoreRequest.url()); 1140 1141 // Check the network response. It has the network request, plus caching headers. 1142 cacheHit.networkResponse() 1143 .assertCode(200) 1144 .assertHeader("Donut", "b") 1145 .assertRequestHeader("If-None-Match", "v1") // If-None-Match in the validation request. 1146 .assertRequestUrl(cacheStoreRequest.url()); 1147 } 1148 1149 @Test public void conditionalCacheMiss_Async() throws Exception { 1150 server.enqueue(new MockResponse().setBody("A").addHeader("ETag: v1")); 1151 server.enqueue(new MockResponse().setBody("B")); 1152 1153 client.setCache(cache); 1154 1155 Request request1 = new Request.Builder() 1156 .url(server.getUrl("/")) 1157 .build(); 1158 client.newCall(request1).enqueue(callback); 1159 callback.await(request1.url()).assertCode(200).assertBody("A"); 1160 assertNull(server.takeRequest().getHeader("If-None-Match")); 1161 1162 Request request2 = new Request.Builder() 1163 .url(server.getUrl("/")) 1164 .build(); 1165 client.newCall(request2).enqueue(callback); 1166 callback.await(request2.url()).assertCode(200).assertBody("B"); 1167 assertEquals("v1", server.takeRequest().getHeader("If-None-Match")); 1168 } 1169 1170 @Test public void onlyIfCachedReturns504WhenNotCached() throws Exception { 1171 Request request = new Request.Builder() 1172 .url(server.getUrl("/")) 1173 .header("Cache-Control", "only-if-cached") 1174 .build(); 1175 1176 executeSynchronously(request) 1177 .assertCode(504) 1178 .assertBody("") 1179 .assertNoNetworkResponse() 1180 .assertNoCacheResponse(); 1181 } 1182 1183 @Test public void redirect() throws Exception { 1184 server.enqueue(new MockResponse() 1185 .setResponseCode(301) 1186 .addHeader("Location: /b") 1187 .addHeader("Test", "Redirect from /a to /b") 1188 .setBody("/a has moved!")); 1189 server.enqueue(new MockResponse() 1190 .setResponseCode(302) 1191 .addHeader("Location: /c") 1192 .addHeader("Test", "Redirect from /b to /c") 1193 .setBody("/b has moved!")); 1194 server.enqueue(new MockResponse().setBody("C")); 1195 1196 executeSynchronously(new Request.Builder().url(server.getUrl("/a")).build()) 1197 .assertCode(200) 1198 .assertBody("C") 1199 .priorResponse() 1200 .assertCode(302) 1201 .assertHeader("Test", "Redirect from /b to /c") 1202 .priorResponse() 1203 .assertCode(301) 1204 .assertHeader("Test", "Redirect from /a to /b"); 1205 1206 assertEquals(0, server.takeRequest().getSequenceNumber()); // New connection. 1207 assertEquals(1, server.takeRequest().getSequenceNumber()); // Connection reused. 1208 assertEquals(2, server.takeRequest().getSequenceNumber()); // Connection reused again! 1209 } 1210 1211 @Test public void postRedirectsToGet() throws Exception { 1212 server.enqueue(new MockResponse() 1213 .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1214 .addHeader("Location: /page2") 1215 .setBody("This page has moved!")); 1216 server.enqueue(new MockResponse().setBody("Page 2")); 1217 1218 Response response = client.newCall(new Request.Builder() 1219 .url(server.getUrl("/page1")) 1220 .post(RequestBody.create(MediaType.parse("text/plain"), "Request Body")) 1221 .build()).execute(); 1222 assertEquals("Page 2", response.body().string()); 1223 1224 RecordedRequest page1 = server.takeRequest(); 1225 assertEquals("POST /page1 HTTP/1.1", page1.getRequestLine()); 1226 assertEquals("Request Body", page1.getBody().readUtf8()); 1227 1228 RecordedRequest page2 = server.takeRequest(); 1229 assertEquals("GET /page2 HTTP/1.1", page2.getRequestLine()); 1230 } 1231 1232 @Test public void redirectsDoNotIncludeTooManyCookies() throws Exception { 1233 server2.enqueue(new MockResponse().setBody("Page 2")); 1234 server.enqueue(new MockResponse() 1235 .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1236 .addHeader("Location: " + server2.getUrl("/"))); 1237 1238 CookieManager cookieManager = new CookieManager(null, ACCEPT_ORIGINAL_SERVER); 1239 HttpCookie cookie = new HttpCookie("c", "cookie"); 1240 cookie.setDomain(server.get().getCookieDomain()); 1241 cookie.setPath("/"); 1242 String portList = Integer.toString(server.getPort()); 1243 cookie.setPortlist(portList); 1244 cookieManager.getCookieStore().add(server.getUrl("/").toURI(), cookie); 1245 client.setCookieHandler(cookieManager); 1246 1247 Response response = client.newCall(new Request.Builder() 1248 .url(server.getUrl("/page1")) 1249 .build()).execute(); 1250 assertEquals("Page 2", response.body().string()); 1251 1252 RecordedRequest request1 = server.takeRequest(); 1253 assertEquals("$Version=\"1\"; c=\"cookie\";$Path=\"/\";$Domain=\"" 1254 + server.get().getCookieDomain() 1255 + "\";$Port=\"" 1256 + portList 1257 + "\"", request1.getHeader("Cookie")); 1258 1259 RecordedRequest request2 = server2.takeRequest(); 1260 assertNull(request2.getHeader("Cookie")); 1261 } 1262 1263 @Test public void redirectsDoNotIncludeTooManyAuthHeaders() throws Exception { 1264 server2.enqueue(new MockResponse().setBody("Page 2")); 1265 server.enqueue(new MockResponse() 1266 .setResponseCode(401)); 1267 server.enqueue(new MockResponse() 1268 .setResponseCode(302) 1269 .addHeader("Location: " + server2.getUrl("/b"))); 1270 1271 client.setAuthenticator(new RecordingOkAuthenticator(Credentials.basic("jesse", "secret"))); 1272 1273 Request request = new Request.Builder().url(server.getUrl("/a")).build(); 1274 Response response = client.newCall(request).execute(); 1275 assertEquals("Page 2", response.body().string()); 1276 1277 RecordedRequest redirectRequest = server2.takeRequest(); 1278 assertNull(redirectRequest.getHeader("Authorization")); 1279 assertEquals("/b", redirectRequest.getPath()); 1280 } 1281 1282 @Test public void redirect_Async() throws Exception { 1283 server.enqueue(new MockResponse() 1284 .setResponseCode(301) 1285 .addHeader("Location: /b") 1286 .addHeader("Test", "Redirect from /a to /b") 1287 .setBody("/a has moved!")); 1288 server.enqueue(new MockResponse() 1289 .setResponseCode(302) 1290 .addHeader("Location: /c") 1291 .addHeader("Test", "Redirect from /b to /c") 1292 .setBody("/b has moved!")); 1293 server.enqueue(new MockResponse().setBody("C")); 1294 1295 Request request = new Request.Builder().url(server.getUrl("/a")).build(); 1296 client.newCall(request).enqueue(callback); 1297 1298 callback.await(server.getUrl("/c")) 1299 .assertCode(200) 1300 .assertBody("C") 1301 .priorResponse() 1302 .assertCode(302) 1303 .assertHeader("Test", "Redirect from /b to /c") 1304 .priorResponse() 1305 .assertCode(301) 1306 .assertHeader("Test", "Redirect from /a to /b"); 1307 1308 assertEquals(0, server.takeRequest().getSequenceNumber()); // New connection. 1309 assertEquals(1, server.takeRequest().getSequenceNumber()); // Connection reused. 1310 assertEquals(2, server.takeRequest().getSequenceNumber()); // Connection reused again! 1311 } 1312 1313 @Test public void follow20Redirects() throws Exception { 1314 for (int i = 0; i < 20; i++) { 1315 server.enqueue(new MockResponse() 1316 .setResponseCode(301) 1317 .addHeader("Location: /" + (i + 1)) 1318 .setBody("Redirecting to /" + (i + 1))); 1319 } 1320 server.enqueue(new MockResponse().setBody("Success!")); 1321 1322 executeSynchronously(new Request.Builder().url(server.getUrl("/0")).build()) 1323 .assertCode(200) 1324 .assertBody("Success!"); 1325 } 1326 1327 @Test public void follow20Redirects_Async() throws Exception { 1328 for (int i = 0; i < 20; i++) { 1329 server.enqueue(new MockResponse() 1330 .setResponseCode(301) 1331 .addHeader("Location: /" + (i + 1)) 1332 .setBody("Redirecting to /" + (i + 1))); 1333 } 1334 server.enqueue(new MockResponse().setBody("Success!")); 1335 1336 Request request = new Request.Builder().url(server.getUrl("/0")).build(); 1337 client.newCall(request).enqueue(callback); 1338 callback.await(server.getUrl("/20")) 1339 .assertCode(200) 1340 .assertBody("Success!"); 1341 } 1342 1343 @Test public void doesNotFollow21Redirects() throws Exception { 1344 for (int i = 0; i < 21; i++) { 1345 server.enqueue(new MockResponse() 1346 .setResponseCode(301) 1347 .addHeader("Location: /" + (i + 1)) 1348 .setBody("Redirecting to /" + (i + 1))); 1349 } 1350 1351 try { 1352 client.newCall(new Request.Builder().url(server.getUrl("/0")).build()).execute(); 1353 fail(); 1354 } catch (IOException expected) { 1355 assertEquals("Too many follow-up requests: 21", expected.getMessage()); 1356 } 1357 } 1358 1359 @Test public void doesNotFollow21Redirects_Async() throws Exception { 1360 for (int i = 0; i < 21; i++) { 1361 server.enqueue(new MockResponse() 1362 .setResponseCode(301) 1363 .addHeader("Location: /" + (i + 1)) 1364 .setBody("Redirecting to /" + (i + 1))); 1365 } 1366 1367 Request request = new Request.Builder().url(server.getUrl("/0")).build(); 1368 client.newCall(request).enqueue(callback); 1369 callback.await(server.getUrl("/20")).assertFailure("Too many follow-up requests: 21"); 1370 } 1371 1372 @Test public void canceledBeforeExecute() throws Exception { 1373 Call call = client.newCall(new Request.Builder().url(server.getUrl("/a")).build()); 1374 call.cancel(); 1375 1376 try { 1377 call.execute(); 1378 fail(); 1379 } catch (IOException expected) { 1380 } 1381 assertEquals(0, server.getRequestCount()); 1382 } 1383 1384 @Test public void cancelTagImmediatelyAfterEnqueue() throws Exception { 1385 Call call = client.newCall(new Request.Builder() 1386 .url(server.getUrl("/a")) 1387 .tag("request") 1388 .build()); 1389 call.enqueue(callback); 1390 client.cancel("request"); 1391 assertEquals(0, server.getRequestCount()); 1392 callback.await(server.getUrl("/a")).assertFailure("Canceled"); 1393 } 1394 1395 @Test public void cancelBeforeBodyIsRead() throws Exception { 1396 server.enqueue(new MockResponse().setBody("def").throttleBody(1, 750, TimeUnit.MILLISECONDS)); 1397 1398 final Call call = client.newCall(new Request.Builder().url(server.getUrl("/a")).build()); 1399 ExecutorService executor = Executors.newSingleThreadExecutor(); 1400 Future<Response> result = executor.submit(new Callable<Response>() { 1401 @Override public Response call() throws Exception { 1402 return call.execute(); 1403 } 1404 }); 1405 1406 Thread.sleep(100); // wait for it to go in flight. 1407 1408 call.cancel(); 1409 try { 1410 result.get().body().bytes(); 1411 fail(); 1412 } catch (IOException expected) { 1413 } 1414 assertEquals(1, server.getRequestCount()); 1415 } 1416 1417 @Test public void cancelInFlightBeforeResponseReadThrowsIOE() throws Exception { 1418 server.get().setDispatcher(new Dispatcher() { 1419 @Override public MockResponse dispatch(RecordedRequest request) { 1420 client.cancel("request"); 1421 return new MockResponse().setBody("A"); 1422 } 1423 }); 1424 1425 Request request = new Request.Builder().url(server.getUrl("/a")).tag("request").build(); 1426 try { 1427 client.newCall(request).execute(); 1428 fail(); 1429 } catch (IOException expected) { 1430 } 1431 } 1432 1433 @Test public void cancelInFlightBeforeResponseReadThrowsIOE_HTTP_2() throws Exception { 1434 enableProtocol(Protocol.HTTP_2); 1435 cancelInFlightBeforeResponseReadThrowsIOE(); 1436 } 1437 1438 @Test public void cancelInFlightBeforeResponseReadThrowsIOE_SPDY_3() throws Exception { 1439 enableProtocol(Protocol.SPDY_3); 1440 cancelInFlightBeforeResponseReadThrowsIOE(); 1441 } 1442 1443 /** 1444 * This test puts a request in front of one that is to be canceled, so that it is canceled before 1445 * I/O takes place. 1446 */ 1447 @Test public void canceledBeforeIOSignalsOnFailure() throws Exception { 1448 client.getDispatcher().setMaxRequests(1); // Force requests to be executed serially. 1449 server.get().setDispatcher(new Dispatcher() { 1450 char nextResponse = 'A'; 1451 1452 @Override public MockResponse dispatch(RecordedRequest request) { 1453 client.cancel("request B"); 1454 return new MockResponse().setBody(Character.toString(nextResponse++)); 1455 } 1456 }); 1457 1458 Request requestA = new Request.Builder().url(server.getUrl("/a")).tag("request A").build(); 1459 client.newCall(requestA).enqueue(callback); 1460 assertEquals("/a", server.takeRequest().getPath()); 1461 1462 Request requestB = new Request.Builder().url(server.getUrl("/b")).tag("request B").build(); 1463 client.newCall(requestB).enqueue(callback); 1464 1465 callback.await(requestA.url()).assertBody("A"); 1466 // At this point we know the callback is ready, and that it will receive a cancel failure. 1467 callback.await(requestB.url()).assertFailure("Canceled"); 1468 } 1469 1470 @Test public void canceledBeforeIOSignalsOnFailure_HTTP_2() throws Exception { 1471 enableProtocol(Protocol.HTTP_2); 1472 canceledBeforeIOSignalsOnFailure(); 1473 } 1474 1475 @Test public void canceledBeforeIOSignalsOnFailure_SPDY_3() throws Exception { 1476 enableProtocol(Protocol.SPDY_3); 1477 canceledBeforeIOSignalsOnFailure(); 1478 } 1479 1480 @Test public void canceledBeforeResponseReadSignalsOnFailure() throws Exception { 1481 Request requestA = new Request.Builder().url(server.getUrl("/a")).tag("request A").build(); 1482 final Call call = client.newCall(requestA); 1483 server.get().setDispatcher(new Dispatcher() { 1484 @Override public MockResponse dispatch(RecordedRequest request) { 1485 call.cancel(); 1486 return new MockResponse().setBody("A"); 1487 } 1488 }); 1489 1490 call.enqueue(callback); 1491 assertEquals("/a", server.takeRequest().getPath()); 1492 1493 callback.await(requestA.url()).assertFailure("Canceled", "stream was reset: CANCEL", 1494 "Socket closed"); 1495 } 1496 1497 @Test public void canceledBeforeResponseReadSignalsOnFailure_HTTP_2() throws Exception { 1498 enableProtocol(Protocol.HTTP_2); 1499 canceledBeforeResponseReadSignalsOnFailure(); 1500 } 1501 1502 @Test public void canceledBeforeResponseReadSignalsOnFailure_SPDY_3() throws Exception { 1503 enableProtocol(Protocol.SPDY_3); 1504 canceledBeforeResponseReadSignalsOnFailure(); 1505 } 1506 1507 /** 1508 * There's a race condition where the cancel may apply after the stream has already been 1509 * processed. 1510 */ 1511 @Test public void canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce() throws Exception { 1512 server.enqueue(new MockResponse().setBody("A")); 1513 1514 final CountDownLatch latch = new CountDownLatch(1); 1515 final AtomicReference<String> bodyRef = new AtomicReference<>(); 1516 final AtomicBoolean failureRef = new AtomicBoolean(); 1517 1518 Request request = new Request.Builder().url(server.getUrl("/a")).tag("request A").build(); 1519 final Call call = client.newCall(request); 1520 call.enqueue(new Callback() { 1521 @Override public void onFailure(Request request, IOException e) { 1522 failureRef.set(true); 1523 latch.countDown(); 1524 } 1525 1526 @Override public void onResponse(Response response) throws IOException { 1527 call.cancel(); 1528 try { 1529 bodyRef.set(response.body().string()); 1530 } catch (IOException e) { // It is ok if this broke the stream. 1531 bodyRef.set("A"); 1532 throw e; // We expect to not loop into onFailure in this case. 1533 } finally { 1534 latch.countDown(); 1535 } 1536 } 1537 }); 1538 1539 latch.await(); 1540 assertEquals("A", bodyRef.get()); 1541 assertFalse(failureRef.get()); 1542 } 1543 1544 @Test public void canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce_HTTP_2() 1545 throws Exception { 1546 enableProtocol(Protocol.HTTP_2); 1547 canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce(); 1548 } 1549 1550 @Test public void canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce_SPDY_3() 1551 throws Exception { 1552 enableProtocol(Protocol.SPDY_3); 1553 canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce(); 1554 } 1555 1556 @Test public void gzip() throws Exception { 1557 Buffer gzippedBody = gzip("abcabcabc"); 1558 String bodySize = Long.toString(gzippedBody.size()); 1559 1560 server.enqueue(new MockResponse() 1561 .setBody(gzippedBody) 1562 .addHeader("Content-Encoding: gzip")); 1563 1564 Request request = new Request.Builder() 1565 .url(server.getUrl("/")) 1566 .build(); 1567 1568 // Confirm that the user request doesn't have Accept-Encoding, and the user 1569 // response doesn't have a Content-Encoding or Content-Length. 1570 RecordedResponse userResponse = executeSynchronously(request); 1571 userResponse.assertCode(200) 1572 .assertRequestHeader("Accept-Encoding") 1573 .assertHeader("Content-Encoding") 1574 .assertHeader("Content-Length") 1575 .assertBody("abcabcabc"); 1576 1577 // But the network request doesn't lie. OkHttp used gzip for this call. 1578 userResponse.networkResponse() 1579 .assertHeader("Content-Encoding", "gzip") 1580 .assertHeader("Content-Length", bodySize) 1581 .assertRequestHeader("Accept-Encoding", "gzip"); 1582 } 1583 1584 @Test public void asyncResponseCanBeConsumedLater() throws Exception { 1585 server.enqueue(new MockResponse().setBody("abc")); 1586 server.enqueue(new MockResponse().setBody("def")); 1587 1588 Request request = new Request.Builder() 1589 .url(server.getUrl("/")) 1590 .header("User-Agent", "SyncApiTest") 1591 .build(); 1592 1593 final BlockingQueue<Response> responseRef = new SynchronousQueue<>(); 1594 client.newCall(request).enqueue(new Callback() { 1595 @Override public void onFailure(Request request, IOException e) { 1596 throw new AssertionError(); 1597 } 1598 1599 @Override public void onResponse(Response response) throws IOException { 1600 try { 1601 responseRef.put(response); 1602 } catch (InterruptedException e) { 1603 throw new AssertionError(); 1604 } 1605 } 1606 }); 1607 1608 Response response = responseRef.take(); 1609 assertEquals(200, response.code()); 1610 assertEquals("abc", response.body().string()); 1611 1612 // Make another request just to confirm that that connection can be reused... 1613 executeSynchronously(new Request.Builder().url(server.getUrl("/")).build()).assertBody("def"); 1614 assertEquals(0, server.takeRequest().getSequenceNumber()); // New connection. 1615 assertEquals(1, server.takeRequest().getSequenceNumber()); // Connection reused. 1616 1617 // ... even before we close the response body! 1618 response.body().close(); 1619 } 1620 1621 @Test public void userAgentIsIncludedByDefault() throws Exception { 1622 server.enqueue(new MockResponse()); 1623 1624 executeSynchronously(new Request.Builder().url(server.getUrl("/")).build()); 1625 1626 RecordedRequest recordedRequest = server.takeRequest(); 1627 assertTrue(recordedRequest.getHeader("User-Agent") 1628 .matches(Version.userAgent())); 1629 } 1630 1631 @Test public void setFollowRedirectsFalse() throws Exception { 1632 server.enqueue(new MockResponse() 1633 .setResponseCode(302) 1634 .addHeader("Location: /b") 1635 .setBody("A")); 1636 server.enqueue(new MockResponse().setBody("B")); 1637 1638 client.setFollowRedirects(false); 1639 RecordedResponse recordedResponse = executeSynchronously( 1640 new Request.Builder().url(server.getUrl("/a")).build()); 1641 1642 recordedResponse 1643 .assertBody("A") 1644 .assertCode(302); 1645 } 1646 1647 @Test public void expect100ContinueNonEmptyRequestBody() throws Exception { 1648 server.enqueue(new MockResponse()); 1649 1650 Request request = new Request.Builder() 1651 .url(server.getUrl("/")) 1652 .header("Expect", "100-continue") 1653 .post(RequestBody.create(MediaType.parse("text/plain"), "abc")) 1654 .build(); 1655 1656 executeSynchronously(request) 1657 .assertCode(200) 1658 .assertSuccessful(); 1659 1660 assertEquals("abc", server.takeRequest().getUtf8Body()); 1661 } 1662 1663 @Test public void expect100ContinueEmptyRequestBody() throws Exception { 1664 server.enqueue(new MockResponse()); 1665 1666 Request request = new Request.Builder() 1667 .url(server.getUrl("/")) 1668 .header("Expect", "100-continue") 1669 .post(RequestBody.create(MediaType.parse("text/plain"), "")) 1670 .build(); 1671 1672 executeSynchronously(request) 1673 .assertCode(200) 1674 .assertSuccessful(); 1675 } 1676 1677 private RecordedResponse executeSynchronously(Request request) throws IOException { 1678 Response response = client.newCall(request).execute(); 1679 return new RecordedResponse(request, response, null, response.body().string(), null); 1680 } 1681 1682 /** 1683 * Tests that use this will fail unless boot classpath is set. Ex. {@code 1684 * -Xbootclasspath/p:/tmp/alpn-boot-8.0.0.v20140317} 1685 */ 1686 private void enableProtocol(Protocol protocol) { 1687 client.setSslSocketFactory(sslContext.getSocketFactory()); 1688 client.setHostnameVerifier(new RecordingHostnameVerifier()); 1689 client.setProtocols(Arrays.asList(protocol, Protocol.HTTP_1_1)); 1690 server.get().useHttps(sslContext.getSocketFactory(), false); 1691 server.get().setProtocols(client.getProtocols()); 1692 } 1693 1694 private Buffer gzip(String data) throws IOException { 1695 Buffer result = new Buffer(); 1696 BufferedSink sink = Okio.buffer(new GzipSink(result)); 1697 sink.writeUtf8(data); 1698 sink.close(); 1699 return result; 1700 } 1701 1702 private void assertContains(Collection<String> collection, String element) { 1703 for (String c : collection) { 1704 if (c != null && c.equalsIgnoreCase(element)) return; 1705 } 1706 fail("No " + element + " in " + collection); 1707 } 1708 1709 private void assertContainsNoneMatching(List<String> headers, String pattern) { 1710 for (String header : headers) { 1711 if (header.matches(pattern)) { 1712 fail("Header " + header + " matches " + pattern); 1713 } 1714 } 1715 } 1716 1717 private static class RecordingSSLSocketFactory extends DelegatingSSLSocketFactory { 1718 1719 private List<SSLSocket> socketsCreated = new ArrayList<SSLSocket>(); 1720 1721 public RecordingSSLSocketFactory(SSLSocketFactory delegate) { 1722 super(delegate); 1723 } 1724 1725 @Override 1726 protected void configureSocket(SSLSocket sslSocket) throws IOException { 1727 socketsCreated.add(sslSocket); 1728 } 1729 1730 public List<SSLSocket> getSocketsCreated() { 1731 return socketsCreated; 1732 } 1733 } 1734 1735 /** 1736 * Used during tests that involve TLS connection fallback attempts. OkHttp includes the 1737 * TLS_FALLBACK_SCSV cipher on fallback connections. See 1738 * {@link com.squareup.okhttp.FallbackTestClientSocketFactory} for details. 1739 */ 1740 private static void suppressTlsFallbackScsv(OkHttpClient client) { 1741 FallbackTestClientSocketFactory clientSocketFactory = 1742 new FallbackTestClientSocketFactory(sslContext.getSocketFactory()); 1743 client.setSslSocketFactory(clientSocketFactory); 1744 } 1745} 1746