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