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.List; 42import java.util.concurrent.BlockingQueue; 43import java.util.concurrent.Callable; 44import java.util.concurrent.CountDownLatch; 45import java.util.concurrent.ExecutorService; 46import java.util.concurrent.Executors; 47import java.util.concurrent.Future; 48import java.util.concurrent.SynchronousQueue; 49import java.util.concurrent.TimeUnit; 50import java.util.concurrent.atomic.AtomicBoolean; 51import java.util.concurrent.atomic.AtomicReference; 52import javax.net.ssl.SSLContext; 53import javax.net.ssl.SSLHandshakeException; 54import javax.net.ssl.SSLPeerUnverifiedException; 55import javax.net.ssl.SSLProtocolException; 56import javax.net.ssl.SSLSocket; 57import javax.net.ssl.SSLSocketFactory; 58import okio.Buffer; 59import okio.BufferedSink; 60import okio.BufferedSource; 61import okio.GzipSink; 62import okio.Okio; 63import org.junit.After; 64import org.junit.Before; 65import org.junit.Ignore; 66import org.junit.Rule; 67import org.junit.Test; 68import org.junit.rules.TemporaryFolder; 69import org.junit.rules.TestRule; 70import org.junit.rules.Timeout; 71 72import static com.squareup.okhttp.internal.Internal.logger; 73import static java.net.CookiePolicy.ACCEPT_ORIGINAL_SERVER; 74import static org.junit.Assert.assertEquals; 75import static org.junit.Assert.assertFalse; 76import static org.junit.Assert.assertNotNull; 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", RequestBody.create(null, new byte[0])) 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 // https://github.com/square/okhttp/issues/442 685 @Test public void timeoutsNotRetried() throws Exception { 686 server.enqueue(new MockResponse() 687 .setSocketPolicy(SocketPolicy.NO_RESPONSE)); 688 server.enqueue(new MockResponse() 689 .setBody("unreachable!")); 690 691 Internal.instance.setNetwork(client, new DoubleInetAddressNetwork()); 692 client.setReadTimeout(100, TimeUnit.MILLISECONDS); 693 694 Request request = new Request.Builder().url(server.getUrl("/")).build(); 695 try { 696 // If this succeeds, too many requests were made. 697 client.newCall(request).execute(); 698 fail(); 699 } catch (InterruptedIOException expected) { 700 } 701 } 702 703 @Test public void reusedSinksGetIndependentTimeoutInstances() throws Exception { 704 server.enqueue(new MockResponse()); 705 server.enqueue(new MockResponse()); 706 707 // Call 1: set a deadline on the request body. 708 RequestBody requestBody1 = new RequestBody() { 709 @Override public MediaType contentType() { 710 return MediaType.parse("text/plain"); 711 } 712 @Override public void writeTo(BufferedSink sink) throws IOException { 713 sink.writeUtf8("abc"); 714 sink.timeout().deadline(5, TimeUnit.SECONDS); 715 } 716 }; 717 Request request1 = new Request.Builder() 718 .url(server.getUrl("/")) 719 .method("POST", requestBody1) 720 .build(); 721 Response response1 = client.newCall(request1).execute(); 722 assertEquals(200, response1.code()); 723 724 // Call 2: check for the absence of a deadline on the request body. 725 RequestBody requestBody2 = new RequestBody() { 726 @Override public MediaType contentType() { 727 return MediaType.parse("text/plain"); 728 } 729 @Override public void writeTo(BufferedSink sink) throws IOException { 730 assertFalse(sink.timeout().hasDeadline()); 731 sink.writeUtf8("def"); 732 } 733 }; 734 Request request2 = new Request.Builder() 735 .url(server.getUrl("/")) 736 .method("POST", requestBody2) 737 .build(); 738 Response response2 = client.newCall(request2).execute(); 739 assertEquals(200, response2.code()); 740 741 // Use sequence numbers to confirm the connection was pooled. 742 assertEquals(0, server.takeRequest().getSequenceNumber()); 743 assertEquals(1, server.takeRequest().getSequenceNumber()); 744 } 745 746 @Test public void reusedSourcesGetIndependentTimeoutInstances() throws Exception { 747 server.enqueue(new MockResponse().setBody("abc")); 748 server.enqueue(new MockResponse().setBody("def")); 749 750 // Call 1: set a deadline on the response body. 751 Request request1 = new Request.Builder().url(server.getUrl("/")).build(); 752 Response response1 = client.newCall(request1).execute(); 753 BufferedSource body1 = response1.body().source(); 754 assertEquals("abc", body1.readUtf8()); 755 body1.timeout().deadline(5, TimeUnit.SECONDS); 756 757 // Call 2: check for the absence of a deadline on the request body. 758 Request request2 = new Request.Builder().url(server.getUrl("/")).build(); 759 Response response2 = client.newCall(request2).execute(); 760 BufferedSource body2 = response2.body().source(); 761 assertEquals("def", body2.readUtf8()); 762 assertFalse(body2.timeout().hasDeadline()); 763 764 // Use sequence numbers to confirm the connection was pooled. 765 assertEquals(0, server.takeRequest().getSequenceNumber()); 766 assertEquals(1, server.takeRequest().getSequenceNumber()); 767 } 768 769 @Test public void tls() throws Exception { 770 server.get().useHttps(sslContext.getSocketFactory(), false); 771 server.enqueue(new MockResponse() 772 .setBody("abc") 773 .addHeader("Content-Type: text/plain")); 774 775 client.setSslSocketFactory(sslContext.getSocketFactory()); 776 client.setHostnameVerifier(new RecordingHostnameVerifier()); 777 778 executeSynchronously(new Request.Builder().url(server.getUrl("/")).build()) 779 .assertHandshake(); 780 } 781 782 @Test public void tls_Async() throws Exception { 783 server.get().useHttps(sslContext.getSocketFactory(), false); 784 server.enqueue(new MockResponse() 785 .setBody("abc") 786 .addHeader("Content-Type: text/plain")); 787 788 client.setSslSocketFactory(sslContext.getSocketFactory()); 789 client.setHostnameVerifier(new RecordingHostnameVerifier()); 790 791 Request request = new Request.Builder() 792 .url(server.getUrl("/")) 793 .build(); 794 client.newCall(request).enqueue(callback); 795 796 callback.await(request.url()).assertHandshake(); 797 } 798 799 @Test public void recoverWhenRetryOnConnectionFailureIsTrue() throws Exception { 800 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AT_START)); 801 server.enqueue(new MockResponse().setBody("retry success")); 802 803 Internal.instance.setNetwork(client, new DoubleInetAddressNetwork()); 804 assertTrue(client.getRetryOnConnectionFailure()); 805 806 Request request = new Request.Builder().url(server.getUrl("/")).build(); 807 Response response = client.newCall(request).execute(); 808 assertEquals("retry success", response.body().string()); 809 } 810 811 @Test public void noRecoverWhenRetryOnConnectionFailureIsFalse() throws Exception { 812 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AT_START)); 813 server.enqueue(new MockResponse().setBody("unreachable!")); 814 815 Internal.instance.setNetwork(client, new DoubleInetAddressNetwork()); 816 client.setRetryOnConnectionFailure(false); 817 818 Request request = new Request.Builder().url(server.getUrl("/")).build(); 819 try { 820 // If this succeeds, too many requests were made. 821 client.newCall(request).execute(); 822 fail(); 823 } catch (IOException expected) { 824 } 825 } 826 827 @Test public void recoverFromTlsHandshakeFailure() throws Exception { 828 server.get().useHttps(sslContext.getSocketFactory(), false); 829 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE)); 830 server.enqueue(new MockResponse().setBody("abc")); 831 832 suppressTlsFallbackScsv(client); 833 client.setHostnameVerifier(new RecordingHostnameVerifier()); 834 Internal.instance.setNetwork(client, new SingleInetAddressNetwork()); 835 836 executeSynchronously(new Request.Builder().url(server.getUrl("/")).build()) 837 .assertBody("abc"); 838 } 839 840 @Test public void recoverFromTlsHandshakeFailure_tlsFallbackScsvEnabled() throws Exception { 841 final String tlsFallbackScsv = "TLS_FALLBACK_SCSV"; 842 List<String> supportedCiphers = 843 Arrays.asList(sslContext.getSocketFactory().getSupportedCipherSuites()); 844 if (!supportedCiphers.contains(tlsFallbackScsv)) { 845 // This only works if the client socket supports TLS_FALLBACK_SCSV. 846 return; 847 } 848 849 server.get().useHttps(sslContext.getSocketFactory(), false); 850 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE)); 851 852 RecordingSSLSocketFactory clientSocketFactory = 853 new RecordingSSLSocketFactory(sslContext.getSocketFactory()); 854 client.setSslSocketFactory(clientSocketFactory); 855 client.setHostnameVerifier(new RecordingHostnameVerifier()); 856 Internal.instance.setNetwork(client, new SingleInetAddressNetwork()); 857 858 Request request = new Request.Builder().url(server.getUrl("/")).build(); 859 try { 860 client.newCall(request).execute(); 861 fail(); 862 } catch (SSLHandshakeException expected) { 863 } 864 865 List<SSLSocket> clientSockets = clientSocketFactory.getSocketsCreated(); 866 SSLSocket firstSocket = clientSockets.get(0); 867 assertFalse(Arrays.asList(firstSocket.getEnabledCipherSuites()).contains(tlsFallbackScsv)); 868 SSLSocket secondSocket = clientSockets.get(1); 869 assertTrue(Arrays.asList(secondSocket.getEnabledCipherSuites()).contains(tlsFallbackScsv)); 870 } 871 872 @Test public void recoverFromTlsHandshakeFailure_Async() throws Exception { 873 server.get().useHttps(sslContext.getSocketFactory(), false); 874 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE)); 875 server.enqueue(new MockResponse().setBody("abc")); 876 877 suppressTlsFallbackScsv(client); 878 client.setHostnameVerifier(new RecordingHostnameVerifier()); 879 880 Request request = new Request.Builder() 881 .url(server.getUrl("/")) 882 .build(); 883 client.newCall(request).enqueue(callback); 884 885 callback.await(request.url()).assertBody("abc"); 886 } 887 888 @Test public void noRecoveryFromTlsHandshakeFailureWhenTlsFallbackIsDisabled() throws Exception { 889 client.setConnectionSpecs(Arrays.asList(ConnectionSpec.MODERN_TLS, ConnectionSpec.CLEARTEXT)); 890 891 server.get().useHttps(sslContext.getSocketFactory(), false); 892 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE)); 893 894 suppressTlsFallbackScsv(client); 895 client.setHostnameVerifier(new RecordingHostnameVerifier()); 896 Internal.instance.setNetwork(client, new SingleInetAddressNetwork()); 897 898 Request request = new Request.Builder().url(server.getUrl("/")).build(); 899 try { 900 client.newCall(request).execute(); 901 fail(); 902 } catch (SSLProtocolException expected) { 903 // RI response to the FAIL_HANDSHAKE 904 } catch (SSLHandshakeException expected) { 905 // Android's response to the FAIL_HANDSHAKE 906 } 907 } 908 909 @Test public void cleartextCallsFailWhenCleartextIsDisabled() throws Exception { 910 // Configure the client with only TLS configurations. No cleartext! 911 client.setConnectionSpecs( 912 Arrays.asList(ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS)); 913 914 server.enqueue(new MockResponse()); 915 916 Request request = new Request.Builder().url(server.getUrl("/")).build(); 917 try { 918 client.newCall(request).execute(); 919 fail(); 920 } catch (UnknownServiceException expected) { 921 assertTrue(expected.getMessage().contains("CLEARTEXT communication not supported")); 922 } 923 } 924 925 @Test public void setFollowSslRedirectsFalse() throws Exception { 926 server.get().useHttps(sslContext.getSocketFactory(), false); 927 server.enqueue(new MockResponse().setResponseCode(301).addHeader("Location: http://square.com")); 928 929 client.setFollowSslRedirects(false); 930 client.setSslSocketFactory(sslContext.getSocketFactory()); 931 client.setHostnameVerifier(new RecordingHostnameVerifier()); 932 933 Request request = new Request.Builder().url(server.getUrl("/")).build(); 934 Response response = client.newCall(request).execute(); 935 assertEquals(301, response.code()); 936 } 937 938 @Test public void matchingPinnedCertificate() throws Exception { 939 server.get().useHttps(sslContext.getSocketFactory(), false); 940 server.enqueue(new MockResponse()); 941 server.enqueue(new MockResponse()); 942 943 client.setSslSocketFactory(sslContext.getSocketFactory()); 944 client.setHostnameVerifier(new RecordingHostnameVerifier()); 945 946 // Make a first request without certificate pinning. Use it to collect certificates to pin. 947 Request request1 = new Request.Builder().url(server.getUrl("/")).build(); 948 Response response1 = client.newCall(request1).execute(); 949 CertificatePinner.Builder certificatePinnerBuilder = new CertificatePinner.Builder(); 950 for (Certificate certificate : response1.handshake().peerCertificates()) { 951 certificatePinnerBuilder.add(server.get().getHostName(), CertificatePinner.pin(certificate)); 952 } 953 954 // Make another request with certificate pinning. It should complete normally. 955 client.setCertificatePinner(certificatePinnerBuilder.build()); 956 Request request2 = new Request.Builder().url(server.getUrl("/")).build(); 957 Response response2 = client.newCall(request2).execute(); 958 assertNotSame(response2.handshake(), response1.handshake()); 959 } 960 961 @Test public void unmatchingPinnedCertificate() throws Exception { 962 server.get().useHttps(sslContext.getSocketFactory(), false); 963 server.enqueue(new MockResponse()); 964 965 client.setSslSocketFactory(sslContext.getSocketFactory()); 966 client.setHostnameVerifier(new RecordingHostnameVerifier()); 967 968 // Pin publicobject.com's cert. 969 client.setCertificatePinner(new CertificatePinner.Builder() 970 .add(server.get().getHostName(), "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=") 971 .build()); 972 973 // When we pin the wrong certificate, connectivity fails. 974 Request request = new Request.Builder().url(server.getUrl("/")).build(); 975 try { 976 client.newCall(request).execute(); 977 fail(); 978 } catch (SSLPeerUnverifiedException expected) { 979 assertTrue(expected.getMessage().startsWith("Certificate pinning failure!")); 980 } 981 } 982 983 @Test public void post_Async() throws Exception { 984 server.enqueue(new MockResponse().setBody("abc")); 985 986 Request request = new Request.Builder() 987 .url(server.getUrl("/")) 988 .post(RequestBody.create(MediaType.parse("text/plain"), "def")) 989 .build(); 990 client.newCall(request).enqueue(callback); 991 992 callback.await(request.url()) 993 .assertCode(200) 994 .assertBody("abc"); 995 996 RecordedRequest recordedRequest = server.takeRequest(); 997 assertEquals("def", recordedRequest.getBody().readUtf8()); 998 assertEquals("3", recordedRequest.getHeader("Content-Length")); 999 assertEquals("text/plain; charset=utf-8", recordedRequest.getHeader("Content-Type")); 1000 } 1001 1002 @Test public void postBodyRetransmittedOnFailureRecovery() throws Exception { 1003 server.enqueue(new MockResponse().setBody("abc")); 1004 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AFTER_REQUEST)); 1005 server.enqueue(new MockResponse().setBody("def")); 1006 1007 // Seed the connection pool so we have something that can fail. 1008 Request request1 = new Request.Builder().url(server.getUrl("/")).build(); 1009 Response response1 = client.newCall(request1).execute(); 1010 assertEquals("abc", response1.body().string()); 1011 1012 Request request2 = new Request.Builder() 1013 .url(server.getUrl("/")) 1014 .post(RequestBody.create(MediaType.parse("text/plain"), "body!")) 1015 .build(); 1016 Response response2 = client.newCall(request2).execute(); 1017 assertEquals("def", response2.body().string()); 1018 1019 RecordedRequest get = server.takeRequest(); 1020 assertEquals(0, get.getSequenceNumber()); 1021 1022 RecordedRequest post1 = server.takeRequest(); 1023 assertEquals("body!", post1.getBody().readUtf8()); 1024 assertEquals(1, post1.getSequenceNumber()); 1025 1026 RecordedRequest post2 = server.takeRequest(); 1027 assertEquals("body!", post2.getBody().readUtf8()); 1028 assertEquals(0, post2.getSequenceNumber()); 1029 } 1030 1031 @Test public void cacheHit() throws Exception { 1032 server.enqueue(new MockResponse() 1033 .addHeader("ETag: v1") 1034 .addHeader("Cache-Control: max-age=60") 1035 .addHeader("Vary: Accept-Charset") 1036 .setBody("A")); 1037 1038 client.setCache(cache); 1039 1040 // Store a response in the cache. 1041 URL url = server.getUrl("/"); 1042 Request cacheStoreRequest = new Request.Builder() 1043 .url(url) 1044 .addHeader("Accept-Language", "fr-CA") 1045 .addHeader("Accept-Charset", "UTF-8") 1046 .build(); 1047 executeSynchronously(cacheStoreRequest) 1048 .assertCode(200) 1049 .assertBody("A"); 1050 assertNull(server.takeRequest().getHeader("If-None-Match")); 1051 1052 // Hit that stored response. 1053 Request cacheHitRequest = new Request.Builder() 1054 .url(url) 1055 .addHeader("Accept-Language", "en-US") // Different, but Vary says it doesn't matter. 1056 .addHeader("Accept-Charset", "UTF-8") 1057 .build(); 1058 RecordedResponse cacheHit = executeSynchronously(cacheHitRequest); 1059 1060 // Check the merged response. The request is the application's original request. 1061 cacheHit.assertCode(200) 1062 .assertBody("A") 1063 .assertHeader("ETag", "v1") 1064 .assertRequestUrl(cacheStoreRequest.url()) 1065 .assertRequestHeader("Accept-Language", "en-US") 1066 .assertRequestHeader("Accept-Charset", "UTF-8"); 1067 1068 // Check the cached response. Its request contains only the saved Vary headers. 1069 cacheHit.cacheResponse() 1070 .assertCode(200) 1071 .assertHeader("ETag", "v1") 1072 .assertRequestMethod("GET") 1073 .assertRequestUrl(cacheStoreRequest.url()) 1074 .assertRequestHeader("Accept-Language") 1075 .assertRequestHeader("Accept-Charset", "UTF-8"); 1076 1077 cacheHit.assertNoNetworkResponse(); 1078 } 1079 1080 @Test public void conditionalCacheHit() throws Exception { 1081 server.enqueue(new MockResponse() 1082 .addHeader("ETag: v1") 1083 .addHeader("Vary: Accept-Charset") 1084 .addHeader("Donut: a") 1085 .setBody("A")); 1086 server.enqueue(new MockResponse().clearHeaders() 1087 .addHeader("Donut: b") 1088 .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED)); 1089 1090 client.setCache(cache); 1091 1092 // Store a response in the cache. 1093 URL url = server.getUrl("/"); 1094 Request cacheStoreRequest = new Request.Builder() 1095 .url(url) 1096 .addHeader("Accept-Language", "fr-CA") 1097 .addHeader("Accept-Charset", "UTF-8") 1098 .build(); 1099 executeSynchronously(cacheStoreRequest) 1100 .assertCode(200) 1101 .assertHeader("Donut", "a") 1102 .assertBody("A"); 1103 assertNull(server.takeRequest().getHeader("If-None-Match")); 1104 1105 // Hit that stored response. 1106 Request cacheHitRequest = new Request.Builder() 1107 .url(url) 1108 .addHeader("Accept-Language", "en-US") // Different, but Vary says it doesn't matter. 1109 .addHeader("Accept-Charset", "UTF-8") 1110 .build(); 1111 RecordedResponse cacheHit = executeSynchronously(cacheHitRequest); 1112 assertEquals("v1", server.takeRequest().getHeader("If-None-Match")); 1113 1114 // Check the merged response. The request is the application's original request. 1115 cacheHit.assertCode(200) 1116 .assertBody("A") 1117 .assertHeader("Donut", "b") 1118 .assertRequestUrl(cacheStoreRequest.url()) 1119 .assertRequestHeader("Accept-Language", "en-US") 1120 .assertRequestHeader("Accept-Charset", "UTF-8") 1121 .assertRequestHeader("If-None-Match"); // No If-None-Match on the user's request. 1122 1123 // Check the cached response. Its request contains only the saved Vary headers. 1124 cacheHit.cacheResponse() 1125 .assertCode(200) 1126 .assertHeader("Donut", "a") 1127 .assertHeader("ETag", "v1") 1128 .assertRequestUrl(cacheStoreRequest.url()) 1129 .assertRequestHeader("Accept-Language") // No Vary on Accept-Language. 1130 .assertRequestHeader("Accept-Charset", "UTF-8") // Because of Vary on Accept-Charset. 1131 .assertRequestHeader("If-None-Match"); // This wasn't present in the original request. 1132 1133 // Check the network response. It has the caller's request, plus some caching headers. 1134 cacheHit.networkResponse() 1135 .assertCode(304) 1136 .assertHeader("Donut", "b") 1137 .assertRequestHeader("Accept-Language", "en-US") 1138 .assertRequestHeader("Accept-Charset", "UTF-8") 1139 .assertRequestHeader("If-None-Match", "v1"); // If-None-Match in the validation request. 1140 } 1141 1142 @Test public void conditionalCacheHit_Async() throws Exception { 1143 server.enqueue(new MockResponse().setBody("A").addHeader("ETag: v1")); 1144 server.enqueue(new MockResponse() 1145 .clearHeaders() 1146 .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED)); 1147 1148 client.setCache(cache); 1149 1150 Request request1 = new Request.Builder() 1151 .url(server.getUrl("/")) 1152 .build(); 1153 client.newCall(request1).enqueue(callback); 1154 callback.await(request1.url()).assertCode(200).assertBody("A"); 1155 assertNull(server.takeRequest().getHeader("If-None-Match")); 1156 1157 Request request2 = new Request.Builder() 1158 .url(server.getUrl("/")) 1159 .build(); 1160 client.newCall(request2).enqueue(callback); 1161 callback.await(request2.url()).assertCode(200).assertBody("A"); 1162 assertEquals("v1", server.takeRequest().getHeader("If-None-Match")); 1163 } 1164 1165 @Test public void conditionalCacheMiss() throws Exception { 1166 server.enqueue(new MockResponse() 1167 .addHeader("ETag: v1") 1168 .addHeader("Vary: Accept-Charset") 1169 .addHeader("Donut: a") 1170 .setBody("A")); 1171 server.enqueue(new MockResponse() 1172 .addHeader("Donut: b") 1173 .setBody("B")); 1174 1175 client.setCache(cache); 1176 1177 Request cacheStoreRequest = new Request.Builder() 1178 .url(server.getUrl("/")) 1179 .addHeader("Accept-Language", "fr-CA") 1180 .addHeader("Accept-Charset", "UTF-8") 1181 .build(); 1182 executeSynchronously(cacheStoreRequest) 1183 .assertCode(200) 1184 .assertBody("A"); 1185 assertNull(server.takeRequest().getHeader("If-None-Match")); 1186 1187 Request cacheMissRequest = new Request.Builder() 1188 .url(server.getUrl("/")) 1189 .addHeader("Accept-Language", "en-US") // Different, but Vary says it doesn't matter. 1190 .addHeader("Accept-Charset", "UTF-8") 1191 .build(); 1192 RecordedResponse cacheHit = executeSynchronously(cacheMissRequest); 1193 assertEquals("v1", server.takeRequest().getHeader("If-None-Match")); 1194 1195 // Check the user response. It has the application's original request. 1196 cacheHit.assertCode(200) 1197 .assertBody("B") 1198 .assertHeader("Donut", "b") 1199 .assertRequestUrl(cacheStoreRequest.url()); 1200 1201 // Check the cache response. Even though it's a miss, we used the cache. 1202 cacheHit.cacheResponse() 1203 .assertCode(200) 1204 .assertHeader("Donut", "a") 1205 .assertHeader("ETag", "v1") 1206 .assertRequestUrl(cacheStoreRequest.url()); 1207 1208 // Check the network response. It has the network request, plus caching headers. 1209 cacheHit.networkResponse() 1210 .assertCode(200) 1211 .assertHeader("Donut", "b") 1212 .assertRequestHeader("If-None-Match", "v1") // If-None-Match in the validation request. 1213 .assertRequestUrl(cacheStoreRequest.url()); 1214 } 1215 1216 @Test public void conditionalCacheMiss_Async() throws Exception { 1217 server.enqueue(new MockResponse().setBody("A").addHeader("ETag: v1")); 1218 server.enqueue(new MockResponse().setBody("B")); 1219 1220 client.setCache(cache); 1221 1222 Request request1 = new Request.Builder() 1223 .url(server.getUrl("/")) 1224 .build(); 1225 client.newCall(request1).enqueue(callback); 1226 callback.await(request1.url()).assertCode(200).assertBody("A"); 1227 assertNull(server.takeRequest().getHeader("If-None-Match")); 1228 1229 Request request2 = new Request.Builder() 1230 .url(server.getUrl("/")) 1231 .build(); 1232 client.newCall(request2).enqueue(callback); 1233 callback.await(request2.url()).assertCode(200).assertBody("B"); 1234 assertEquals("v1", server.takeRequest().getHeader("If-None-Match")); 1235 } 1236 1237 @Test public void onlyIfCachedReturns504WhenNotCached() throws Exception { 1238 Request request = new Request.Builder() 1239 .url(server.getUrl("/")) 1240 .header("Cache-Control", "only-if-cached") 1241 .build(); 1242 1243 executeSynchronously(request) 1244 .assertCode(504) 1245 .assertBody("") 1246 .assertNoNetworkResponse() 1247 .assertNoCacheResponse(); 1248 } 1249 1250 @Test public void redirect() throws Exception { 1251 server.enqueue(new MockResponse() 1252 .setResponseCode(301) 1253 .addHeader("Location: /b") 1254 .addHeader("Test", "Redirect from /a to /b") 1255 .setBody("/a has moved!")); 1256 server.enqueue(new MockResponse() 1257 .setResponseCode(302) 1258 .addHeader("Location: /c") 1259 .addHeader("Test", "Redirect from /b to /c") 1260 .setBody("/b has moved!")); 1261 server.enqueue(new MockResponse().setBody("C")); 1262 1263 executeSynchronously(new Request.Builder().url(server.getUrl("/a")).build()) 1264 .assertCode(200) 1265 .assertBody("C") 1266 .priorResponse() 1267 .assertCode(302) 1268 .assertHeader("Test", "Redirect from /b to /c") 1269 .priorResponse() 1270 .assertCode(301) 1271 .assertHeader("Test", "Redirect from /a to /b"); 1272 1273 assertEquals(0, server.takeRequest().getSequenceNumber()); // New connection. 1274 assertEquals(1, server.takeRequest().getSequenceNumber()); // Connection reused. 1275 assertEquals(2, server.takeRequest().getSequenceNumber()); // Connection reused again! 1276 } 1277 1278 @Test public void postRedirectsToGet() throws Exception { 1279 server.enqueue(new MockResponse() 1280 .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1281 .addHeader("Location: /page2") 1282 .setBody("This page has moved!")); 1283 server.enqueue(new MockResponse().setBody("Page 2")); 1284 1285 Response response = client.newCall(new Request.Builder() 1286 .url(server.getUrl("/page1")) 1287 .post(RequestBody.create(MediaType.parse("text/plain"), "Request Body")) 1288 .build()).execute(); 1289 assertEquals("Page 2", response.body().string()); 1290 1291 RecordedRequest page1 = server.takeRequest(); 1292 assertEquals("POST /page1 HTTP/1.1", page1.getRequestLine()); 1293 assertEquals("Request Body", page1.getBody().readUtf8()); 1294 1295 RecordedRequest page2 = server.takeRequest(); 1296 assertEquals("GET /page2 HTTP/1.1", page2.getRequestLine()); 1297 } 1298 1299 @Test public void redirectsDoNotIncludeTooManyCookies() throws Exception { 1300 server2.enqueue(new MockResponse().setBody("Page 2")); 1301 server.enqueue(new MockResponse() 1302 .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1303 .addHeader("Location: " + server2.getUrl("/"))); 1304 1305 CookieManager cookieManager = new CookieManager(null, ACCEPT_ORIGINAL_SERVER); 1306 HttpCookie cookie = new HttpCookie("c", "cookie"); 1307 cookie.setDomain(server.get().getCookieDomain()); 1308 cookie.setPath("/"); 1309 String portList = Integer.toString(server.getPort()); 1310 cookie.setPortlist(portList); 1311 cookieManager.getCookieStore().add(server.getUrl("/").toURI(), cookie); 1312 client.setCookieHandler(cookieManager); 1313 1314 Response response = client.newCall(new Request.Builder() 1315 .url(server.getUrl("/page1")) 1316 .build()).execute(); 1317 assertEquals("Page 2", response.body().string()); 1318 1319 RecordedRequest request1 = server.takeRequest(); 1320 assertEquals("$Version=\"1\"; c=\"cookie\";$Path=\"/\";$Domain=\"" 1321 + server.get().getCookieDomain() 1322 + "\";$Port=\"" 1323 + portList 1324 + "\"", request1.getHeader("Cookie")); 1325 1326 RecordedRequest request2 = server2.takeRequest(); 1327 assertNull(request2.getHeader("Cookie")); 1328 } 1329 1330 @Test public void redirectsDoNotIncludeTooManyAuthHeaders() throws Exception { 1331 server2.enqueue(new MockResponse().setBody("Page 2")); 1332 server.enqueue(new MockResponse() 1333 .setResponseCode(401)); 1334 server.enqueue(new MockResponse() 1335 .setResponseCode(302) 1336 .addHeader("Location: " + server2.getUrl("/b"))); 1337 1338 client.setAuthenticator(new RecordingOkAuthenticator(Credentials.basic("jesse", "secret"))); 1339 1340 Request request = new Request.Builder().url(server.getUrl("/a")).build(); 1341 Response response = client.newCall(request).execute(); 1342 assertEquals("Page 2", response.body().string()); 1343 1344 RecordedRequest redirectRequest = server2.takeRequest(); 1345 assertNull(redirectRequest.getHeader("Authorization")); 1346 assertEquals("/b", redirectRequest.getPath()); 1347 } 1348 1349 @Test public void redirect_Async() throws Exception { 1350 server.enqueue(new MockResponse() 1351 .setResponseCode(301) 1352 .addHeader("Location: /b") 1353 .addHeader("Test", "Redirect from /a to /b") 1354 .setBody("/a has moved!")); 1355 server.enqueue(new MockResponse() 1356 .setResponseCode(302) 1357 .addHeader("Location: /c") 1358 .addHeader("Test", "Redirect from /b to /c") 1359 .setBody("/b has moved!")); 1360 server.enqueue(new MockResponse().setBody("C")); 1361 1362 Request request = new Request.Builder().url(server.getUrl("/a")).build(); 1363 client.newCall(request).enqueue(callback); 1364 1365 callback.await(server.getUrl("/c")) 1366 .assertCode(200) 1367 .assertBody("C") 1368 .priorResponse() 1369 .assertCode(302) 1370 .assertHeader("Test", "Redirect from /b to /c") 1371 .priorResponse() 1372 .assertCode(301) 1373 .assertHeader("Test", "Redirect from /a to /b"); 1374 1375 assertEquals(0, server.takeRequest().getSequenceNumber()); // New connection. 1376 assertEquals(1, server.takeRequest().getSequenceNumber()); // Connection reused. 1377 assertEquals(2, server.takeRequest().getSequenceNumber()); // Connection reused again! 1378 } 1379 1380 @Test public void follow20Redirects() throws Exception { 1381 for (int i = 0; i < 20; i++) { 1382 server.enqueue(new MockResponse() 1383 .setResponseCode(301) 1384 .addHeader("Location: /" + (i + 1)) 1385 .setBody("Redirecting to /" + (i + 1))); 1386 } 1387 server.enqueue(new MockResponse().setBody("Success!")); 1388 1389 executeSynchronously(new Request.Builder().url(server.getUrl("/0")).build()) 1390 .assertCode(200) 1391 .assertBody("Success!"); 1392 } 1393 1394 @Test public void follow20Redirects_Async() throws Exception { 1395 for (int i = 0; i < 20; i++) { 1396 server.enqueue(new MockResponse() 1397 .setResponseCode(301) 1398 .addHeader("Location: /" + (i + 1)) 1399 .setBody("Redirecting to /" + (i + 1))); 1400 } 1401 server.enqueue(new MockResponse().setBody("Success!")); 1402 1403 Request request = new Request.Builder().url(server.getUrl("/0")).build(); 1404 client.newCall(request).enqueue(callback); 1405 callback.await(server.getUrl("/20")) 1406 .assertCode(200) 1407 .assertBody("Success!"); 1408 } 1409 1410 @Test public void doesNotFollow21Redirects() throws Exception { 1411 for (int i = 0; i < 21; i++) { 1412 server.enqueue(new MockResponse() 1413 .setResponseCode(301) 1414 .addHeader("Location: /" + (i + 1)) 1415 .setBody("Redirecting to /" + (i + 1))); 1416 } 1417 1418 try { 1419 client.newCall(new Request.Builder().url(server.getUrl("/0")).build()).execute(); 1420 fail(); 1421 } catch (IOException expected) { 1422 assertEquals("Too many follow-up requests: 21", expected.getMessage()); 1423 } 1424 } 1425 1426 @Test public void doesNotFollow21Redirects_Async() throws Exception { 1427 for (int i = 0; i < 21; i++) { 1428 server.enqueue(new MockResponse() 1429 .setResponseCode(301) 1430 .addHeader("Location: /" + (i + 1)) 1431 .setBody("Redirecting to /" + (i + 1))); 1432 } 1433 1434 Request request = new Request.Builder().url(server.getUrl("/0")).build(); 1435 client.newCall(request).enqueue(callback); 1436 callback.await(server.getUrl("/20")).assertFailure("Too many follow-up requests: 21"); 1437 } 1438 1439 @Test public void canceledBeforeExecute() throws Exception { 1440 Call call = client.newCall(new Request.Builder().url(server.getUrl("/a")).build()); 1441 call.cancel(); 1442 1443 try { 1444 call.execute(); 1445 fail(); 1446 } catch (IOException expected) { 1447 } 1448 assertEquals(0, server.getRequestCount()); 1449 } 1450 1451 @Test public void cancelTagImmediatelyAfterEnqueue() throws Exception { 1452 Call call = client.newCall(new Request.Builder() 1453 .url(server.getUrl("/a")) 1454 .tag("request") 1455 .build()); 1456 call.enqueue(callback); 1457 client.cancel("request"); 1458 assertEquals(0, server.getRequestCount()); 1459 callback.await(server.getUrl("/a")).assertFailure("Canceled"); 1460 } 1461 1462 @Test public void cancelBeforeBodyIsRead() throws Exception { 1463 server.enqueue(new MockResponse().setBody("def").throttleBody(1, 750, TimeUnit.MILLISECONDS)); 1464 1465 final Call call = client.newCall(new Request.Builder().url(server.getUrl("/a")).build()); 1466 ExecutorService executor = Executors.newSingleThreadExecutor(); 1467 Future<Response> result = executor.submit(new Callable<Response>() { 1468 @Override public Response call() throws Exception { 1469 return call.execute(); 1470 } 1471 }); 1472 1473 Thread.sleep(100); // wait for it to go in flight. 1474 1475 call.cancel(); 1476 try { 1477 result.get().body().bytes(); 1478 fail(); 1479 } catch (IOException expected) { 1480 } 1481 assertEquals(1, server.getRequestCount()); 1482 } 1483 1484 @Test public void cancelInFlightBeforeResponseReadThrowsIOE() throws Exception { 1485 server.get().setDispatcher(new Dispatcher() { 1486 @Override public MockResponse dispatch(RecordedRequest request) { 1487 client.cancel("request"); 1488 return new MockResponse().setBody("A"); 1489 } 1490 }); 1491 1492 Request request = new Request.Builder().url(server.getUrl("/a")).tag("request").build(); 1493 try { 1494 client.newCall(request).execute(); 1495 fail(); 1496 } catch (IOException expected) { 1497 } 1498 } 1499 1500 @Test public void cancelInFlightBeforeResponseReadThrowsIOE_HTTP_2() throws Exception { 1501 enableProtocol(Protocol.HTTP_2); 1502 cancelInFlightBeforeResponseReadThrowsIOE(); 1503 } 1504 1505 @Test public void cancelInFlightBeforeResponseReadThrowsIOE_SPDY_3() throws Exception { 1506 enableProtocol(Protocol.SPDY_3); 1507 cancelInFlightBeforeResponseReadThrowsIOE(); 1508 } 1509 1510 /** 1511 * This test puts a request in front of one that is to be canceled, so that it is canceled before 1512 * I/O takes place. 1513 */ 1514 @Test public void canceledBeforeIOSignalsOnFailure() throws Exception { 1515 client.getDispatcher().setMaxRequests(1); // Force requests to be executed serially. 1516 server.get().setDispatcher(new Dispatcher() { 1517 char nextResponse = 'A'; 1518 1519 @Override public MockResponse dispatch(RecordedRequest request) { 1520 client.cancel("request B"); 1521 return new MockResponse().setBody(Character.toString(nextResponse++)); 1522 } 1523 }); 1524 1525 Request requestA = new Request.Builder().url(server.getUrl("/a")).tag("request A").build(); 1526 client.newCall(requestA).enqueue(callback); 1527 assertEquals("/a", server.takeRequest().getPath()); 1528 1529 Request requestB = new Request.Builder().url(server.getUrl("/b")).tag("request B").build(); 1530 client.newCall(requestB).enqueue(callback); 1531 1532 callback.await(requestA.url()).assertBody("A"); 1533 // At this point we know the callback is ready, and that it will receive a cancel failure. 1534 callback.await(requestB.url()).assertFailure("Canceled"); 1535 } 1536 1537 @Test public void canceledBeforeIOSignalsOnFailure_HTTP_2() throws Exception { 1538 enableProtocol(Protocol.HTTP_2); 1539 canceledBeforeIOSignalsOnFailure(); 1540 } 1541 1542 @Test public void canceledBeforeIOSignalsOnFailure_SPDY_3() throws Exception { 1543 enableProtocol(Protocol.SPDY_3); 1544 canceledBeforeIOSignalsOnFailure(); 1545 } 1546 1547 @Test public void canceledBeforeResponseReadSignalsOnFailure() throws Exception { 1548 Request requestA = new Request.Builder().url(server.getUrl("/a")).tag("request A").build(); 1549 final Call call = client.newCall(requestA); 1550 server.get().setDispatcher(new Dispatcher() { 1551 @Override public MockResponse dispatch(RecordedRequest request) { 1552 call.cancel(); 1553 return new MockResponse().setBody("A"); 1554 } 1555 }); 1556 1557 call.enqueue(callback); 1558 assertEquals("/a", server.takeRequest().getPath()); 1559 1560 callback.await(requestA.url()).assertFailure("Canceled", "stream was reset: CANCEL", 1561 "Socket closed"); 1562 } 1563 1564 @Test public void canceledBeforeResponseReadSignalsOnFailure_HTTP_2() throws Exception { 1565 enableProtocol(Protocol.HTTP_2); 1566 canceledBeforeResponseReadSignalsOnFailure(); 1567 } 1568 1569 @Test public void canceledBeforeResponseReadSignalsOnFailure_SPDY_3() throws Exception { 1570 enableProtocol(Protocol.SPDY_3); 1571 canceledBeforeResponseReadSignalsOnFailure(); 1572 } 1573 1574 /** 1575 * There's a race condition where the cancel may apply after the stream has already been 1576 * processed. 1577 */ 1578 @Test public void canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce() throws Exception { 1579 server.enqueue(new MockResponse().setBody("A")); 1580 1581 final CountDownLatch latch = new CountDownLatch(1); 1582 final AtomicReference<String> bodyRef = new AtomicReference<>(); 1583 final AtomicBoolean failureRef = new AtomicBoolean(); 1584 1585 Request request = new Request.Builder().url(server.getUrl("/a")).tag("request A").build(); 1586 final Call call = client.newCall(request); 1587 call.enqueue(new Callback() { 1588 @Override public void onFailure(Request request, IOException e) { 1589 failureRef.set(true); 1590 latch.countDown(); 1591 } 1592 1593 @Override public void onResponse(Response response) throws IOException { 1594 call.cancel(); 1595 try { 1596 bodyRef.set(response.body().string()); 1597 } catch (IOException e) { // It is ok if this broke the stream. 1598 bodyRef.set("A"); 1599 throw e; // We expect to not loop into onFailure in this case. 1600 } finally { 1601 latch.countDown(); 1602 } 1603 } 1604 }); 1605 1606 latch.await(); 1607 assertEquals("A", bodyRef.get()); 1608 assertFalse(failureRef.get()); 1609 } 1610 1611 @Test public void canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce_HTTP_2() 1612 throws Exception { 1613 enableProtocol(Protocol.HTTP_2); 1614 canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce(); 1615 } 1616 1617 @Test public void canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce_SPDY_3() 1618 throws Exception { 1619 enableProtocol(Protocol.SPDY_3); 1620 canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce(); 1621 } 1622 1623 @Test public void cancelWithInterceptor() throws Exception { 1624 client.interceptors().add(new Interceptor() { 1625 @Override public Response intercept(Chain chain) throws IOException { 1626 chain.proceed(chain.request()); 1627 throw new AssertionError(); // We expect an exception. 1628 } 1629 }); 1630 1631 Call call = client.newCall(new Request.Builder().url(server.getUrl("/a")).build()); 1632 call.cancel(); 1633 1634 try { 1635 call.execute(); 1636 fail(); 1637 } catch (IOException expected) { 1638 } 1639 assertEquals(0, server.getRequestCount()); 1640 } 1641 1642 @Test public void gzip() throws Exception { 1643 Buffer gzippedBody = gzip("abcabcabc"); 1644 String bodySize = Long.toString(gzippedBody.size()); 1645 1646 server.enqueue(new MockResponse() 1647 .setBody(gzippedBody) 1648 .addHeader("Content-Encoding: gzip")); 1649 1650 Request request = new Request.Builder() 1651 .url(server.getUrl("/")) 1652 .build(); 1653 1654 // Confirm that the user request doesn't have Accept-Encoding, and the user 1655 // response doesn't have a Content-Encoding or Content-Length. 1656 RecordedResponse userResponse = executeSynchronously(request); 1657 userResponse.assertCode(200) 1658 .assertRequestHeader("Accept-Encoding") 1659 .assertHeader("Content-Encoding") 1660 .assertHeader("Content-Length") 1661 .assertBody("abcabcabc"); 1662 1663 // But the network request doesn't lie. OkHttp used gzip for this call. 1664 userResponse.networkResponse() 1665 .assertHeader("Content-Encoding", "gzip") 1666 .assertHeader("Content-Length", bodySize) 1667 .assertRequestHeader("Accept-Encoding", "gzip"); 1668 } 1669 1670 @Test public void asyncResponseCanBeConsumedLater() throws Exception { 1671 server.enqueue(new MockResponse().setBody("abc")); 1672 server.enqueue(new MockResponse().setBody("def")); 1673 1674 Request request = new Request.Builder() 1675 .url(server.getUrl("/")) 1676 .header("User-Agent", "SyncApiTest") 1677 .build(); 1678 1679 final BlockingQueue<Response> responseRef = new SynchronousQueue<>(); 1680 client.newCall(request).enqueue(new Callback() { 1681 @Override public void onFailure(Request request, IOException e) { 1682 throw new AssertionError(); 1683 } 1684 1685 @Override public void onResponse(Response response) throws IOException { 1686 try { 1687 responseRef.put(response); 1688 } catch (InterruptedException e) { 1689 throw new AssertionError(); 1690 } 1691 } 1692 }); 1693 1694 Response response = responseRef.take(); 1695 assertEquals(200, response.code()); 1696 assertEquals("abc", response.body().string()); 1697 1698 // Make another request just to confirm that that connection can be reused... 1699 executeSynchronously(new Request.Builder().url(server.getUrl("/")).build()).assertBody("def"); 1700 assertEquals(0, server.takeRequest().getSequenceNumber()); // New connection. 1701 assertEquals(1, server.takeRequest().getSequenceNumber()); // Connection reused. 1702 1703 // ... even before we close the response body! 1704 response.body().close(); 1705 } 1706 1707 @Test public void userAgentIsIncludedByDefault() throws Exception { 1708 server.enqueue(new MockResponse()); 1709 1710 executeSynchronously(new Request.Builder().url(server.getUrl("/")).build()); 1711 1712 RecordedRequest recordedRequest = server.takeRequest(); 1713 assertTrue(recordedRequest.getHeader("User-Agent") 1714 .matches(Version.userAgent())); 1715 } 1716 1717 @Test public void setFollowRedirectsFalse() throws Exception { 1718 server.enqueue(new MockResponse() 1719 .setResponseCode(302) 1720 .addHeader("Location: /b") 1721 .setBody("A")); 1722 server.enqueue(new MockResponse().setBody("B")); 1723 1724 client.setFollowRedirects(false); 1725 RecordedResponse recordedResponse = executeSynchronously( 1726 new Request.Builder().url(server.getUrl("/a")).build()); 1727 1728 recordedResponse 1729 .assertBody("A") 1730 .assertCode(302); 1731 } 1732 1733 @Test public void expect100ContinueNonEmptyRequestBody() throws Exception { 1734 server.enqueue(new MockResponse()); 1735 1736 Request request = new Request.Builder() 1737 .url(server.getUrl("/")) 1738 .header("Expect", "100-continue") 1739 .post(RequestBody.create(MediaType.parse("text/plain"), "abc")) 1740 .build(); 1741 1742 executeSynchronously(request) 1743 .assertCode(200) 1744 .assertSuccessful(); 1745 1746 assertEquals("abc", server.takeRequest().getUtf8Body()); 1747 } 1748 1749 @Test public void expect100ContinueEmptyRequestBody() throws Exception { 1750 server.enqueue(new MockResponse()); 1751 1752 Request request = new Request.Builder() 1753 .url(server.getUrl("/")) 1754 .header("Expect", "100-continue") 1755 .post(RequestBody.create(MediaType.parse("text/plain"), "")) 1756 .build(); 1757 1758 executeSynchronously(request) 1759 .assertCode(200) 1760 .assertSuccessful(); 1761 } 1762 1763 private RecordedResponse executeSynchronously(Request request) throws IOException { 1764 Response response = client.newCall(request).execute(); 1765 return new RecordedResponse(request, response, null, response.body().string(), null); 1766 } 1767 1768 /** 1769 * Tests that use this will fail unless boot classpath is set. Ex. {@code 1770 * -Xbootclasspath/p:/tmp/alpn-boot-8.0.0.v20140317} 1771 */ 1772 private void enableProtocol(Protocol protocol) { 1773 client.setSslSocketFactory(sslContext.getSocketFactory()); 1774 client.setHostnameVerifier(new RecordingHostnameVerifier()); 1775 client.setProtocols(Arrays.asList(protocol, Protocol.HTTP_1_1)); 1776 server.get().useHttps(sslContext.getSocketFactory(), false); 1777 server.get().setProtocols(client.getProtocols()); 1778 } 1779 1780 private Buffer gzip(String data) throws IOException { 1781 Buffer result = new Buffer(); 1782 BufferedSink sink = Okio.buffer(new GzipSink(result)); 1783 sink.writeUtf8(data); 1784 sink.close(); 1785 return result; 1786 } 1787 1788 private static class RecordingSSLSocketFactory extends DelegatingSSLSocketFactory { 1789 1790 private List<SSLSocket> socketsCreated = new ArrayList<SSLSocket>(); 1791 1792 public RecordingSSLSocketFactory(SSLSocketFactory delegate) { 1793 super(delegate); 1794 } 1795 1796 @Override 1797 protected void configureSocket(SSLSocket sslSocket) throws IOException { 1798 socketsCreated.add(sslSocket); 1799 } 1800 1801 public List<SSLSocket> getSocketsCreated() { 1802 return socketsCreated; 1803 } 1804 } 1805 1806 /** 1807 * Used during tests that involve TLS connection fallback attempts. OkHttp includes the 1808 * TLS_FALLBACK_SCSV cipher on fallback connections. See 1809 * {@link com.squareup.okhttp.FallbackTestClientSocketFactory} for details. 1810 */ 1811 private static void suppressTlsFallbackScsv(OkHttpClient client) { 1812 FallbackTestClientSocketFactory clientSocketFactory = 1813 new FallbackTestClientSocketFactory(sslContext.getSocketFactory()); 1814 client.setSslSocketFactory(clientSocketFactory); 1815 } 1816} 1817