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 */ 16 package com.squareup.okhttp; 17 18 import com.squareup.okhttp.internal.DoubleInetAddressDns; 19 import com.squareup.okhttp.internal.RecordingOkAuthenticator; 20 import com.squareup.okhttp.internal.SingleInetAddressDns; 21 import com.squareup.okhttp.internal.SslContextBuilder; 22 import com.squareup.okhttp.internal.Util; 23 import com.squareup.okhttp.internal.Version; 24 import com.squareup.okhttp.internal.http.FakeDns; 25 import com.squareup.okhttp.internal.io.InMemoryFileSystem; 26 import com.squareup.okhttp.mockwebserver.Dispatcher; 27 import com.squareup.okhttp.mockwebserver.MockResponse; 28 import com.squareup.okhttp.mockwebserver.MockWebServer; 29 import com.squareup.okhttp.mockwebserver.RecordedRequest; 30 import com.squareup.okhttp.mockwebserver.SocketPolicy; 31 import com.squareup.okhttp.testing.RecordingHostnameVerifier; 32 import java.io.File; 33 import java.io.IOException; 34 import java.io.InputStream; 35 import java.io.InterruptedIOException; 36 import java.net.CookieManager; 37 import java.net.HttpCookie; 38 import java.net.HttpURLConnection; 39 import java.net.InetAddress; 40 import java.net.InetSocketAddress; 41 import java.net.ProtocolException; 42 import java.net.ServerSocket; 43 import java.net.UnknownServiceException; 44 import java.security.cert.Certificate; 45 import java.util.ArrayList; 46 import java.util.Arrays; 47 import java.util.Collections; 48 import java.util.List; 49 import java.util.concurrent.BlockingQueue; 50 import java.util.concurrent.Callable; 51 import java.util.concurrent.CountDownLatch; 52 import java.util.concurrent.ExecutorService; 53 import java.util.concurrent.Executors; 54 import java.util.concurrent.Future; 55 import java.util.concurrent.SynchronousQueue; 56 import java.util.concurrent.TimeUnit; 57 import java.util.concurrent.atomic.AtomicBoolean; 58 import java.util.concurrent.atomic.AtomicReference; 59 import javax.net.ServerSocketFactory; 60 import javax.net.ssl.SSLContext; 61 import javax.net.ssl.SSLHandshakeException; 62 import javax.net.ssl.SSLPeerUnverifiedException; 63 import javax.net.ssl.SSLProtocolException; 64 import javax.net.ssl.SSLSocket; 65 import javax.net.ssl.SSLSocketFactory; 66 import okio.Buffer; 67 import okio.BufferedSink; 68 import okio.BufferedSource; 69 import okio.GzipSink; 70 import okio.Okio; 71 import org.junit.After; 72 import org.junit.Before; 73 import org.junit.Ignore; 74 import org.junit.Rule; 75 import org.junit.Test; 76 import org.junit.rules.TestRule; 77 import org.junit.rules.Timeout; 78 79 import static com.squareup.okhttp.internal.Internal.logger; 80 import static java.net.CookiePolicy.ACCEPT_ORIGINAL_SERVER; 81 import static org.junit.Assert.assertEquals; 82 import static org.junit.Assert.assertFalse; 83 import static org.junit.Assert.assertNotSame; 84 import static org.junit.Assert.assertNull; 85 import static org.junit.Assert.assertTrue; 86 import static org.junit.Assert.fail; 87 88 public final class CallTest { 89 @Rule public final TestRule timeout = new Timeout(30_000); 90 @Rule public final MockWebServer server = new MockWebServer(); 91 @Rule public final MockWebServer server2 = new MockWebServer(); 92 @Rule public final InMemoryFileSystem fileSystem = new InMemoryFileSystem(); 93 94 // Android-added: Use TLS 1.3 and 1.2 for testing 95 private static final ConnectionSpec TLS_SPEC_1_3 = 96 new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) 97 .tlsVersions(TlsVersion.TLS_1_3) 98 .build(); 99 100 private static final ConnectionSpec TLS_SPEC_1_2 = 101 new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) 102 .tlsVersions(TlsVersion.TLS_1_2) 103 .build(); 104 105 private static final List<ConnectionSpec> TLS_SPEC_NO_V1 106 = Arrays.asList(TLS_SPEC_1_3, TLS_SPEC_1_2); 107 108 private SSLContext sslContext = SslContextBuilder.localhost(); 109 private OkHttpClient client = new OkHttpClient(); 110 private RecordingCallback callback = new RecordingCallback(); 111 private TestLogHandler logHandler = new TestLogHandler(); 112 private Cache cache = new Cache(new File("/cache/"), Integer.MAX_VALUE, fileSystem); 113 private ServerSocket nullServer; 114 setUp()115 @Before public void setUp() throws Exception { 116 logger.addHandler(logHandler); 117 } 118 tearDown()119 @After public void tearDown() throws Exception { 120 cache.delete(); 121 Util.closeQuietly(nullServer); 122 logger.removeHandler(logHandler); 123 } 124 get()125 @Test public void get() throws Exception { 126 server.enqueue(new MockResponse().setBody("abc").addHeader("Content-Type: text/plain")); 127 128 Request request = new Request.Builder() 129 .url(server.url("/")) 130 .header("User-Agent", "SyncApiTest") 131 .build(); 132 133 executeSynchronously(request) 134 .assertCode(200) 135 .assertSuccessful() 136 .assertHeader("Content-Type", "text/plain") 137 .assertBody("abc"); 138 139 RecordedRequest recordedRequest = server.takeRequest(); 140 assertEquals("GET", recordedRequest.getMethod()); 141 assertEquals("SyncApiTest", recordedRequest.getHeader("User-Agent")); 142 assertEquals(0, recordedRequest.getBody().size()); 143 assertNull(recordedRequest.getHeader("Content-Length")); 144 } 145 buildRequestUsingHttpUrl()146 @Test public void buildRequestUsingHttpUrl() throws Exception { 147 server.enqueue(new MockResponse()); 148 149 HttpUrl httpUrl = server.url("/"); 150 Request request = new Request.Builder() 151 .url(httpUrl) 152 .build(); 153 assertEquals(httpUrl, request.httpUrl()); 154 155 executeSynchronously(request).assertSuccessful(); 156 } 157 invalidScheme()158 @Test public void invalidScheme() throws Exception { 159 Request.Builder requestBuilder = new Request.Builder(); 160 try { 161 requestBuilder.url("ftp://hostname/path"); 162 fail(); 163 } catch (IllegalArgumentException expected) { 164 assertEquals(expected.getMessage(), "unexpected url: ftp://hostname/path"); 165 } 166 } 167 invalidPort()168 @Test public void invalidPort() throws Exception { 169 Request.Builder requestBuilder = new Request.Builder(); 170 try { 171 requestBuilder.url("http://localhost:65536/"); 172 fail(); 173 } catch (IllegalArgumentException expected) { 174 assertEquals(expected.getMessage(), "unexpected url: http://localhost:65536/"); 175 } 176 } 177 getReturns500()178 @Test public void getReturns500() throws Exception { 179 server.enqueue(new MockResponse().setResponseCode(500)); 180 181 Request request = new Request.Builder() 182 .url(server.url("/")) 183 .build(); 184 185 executeSynchronously(request) 186 .assertCode(500) 187 .assertNotSuccessful(); 188 } 189 get_HTTP_2()190 @Test public void get_HTTP_2() throws Exception { 191 enableProtocol(Protocol.HTTP_2); 192 get(); 193 } 194 get_HTTPS()195 @Test public void get_HTTPS() throws Exception { 196 enableTls(); 197 get(); 198 } 199 get_SPDY_3()200 @Test public void get_SPDY_3() throws Exception { 201 enableProtocol(Protocol.SPDY_3); 202 get(); 203 } 204 repeatedHeaderNames()205 @Test public void repeatedHeaderNames() throws Exception { 206 server.enqueue(new MockResponse() 207 .addHeader("B", "123") 208 .addHeader("B", "234")); 209 210 Request request = new Request.Builder() 211 .url(server.url("/")) 212 .addHeader("A", "345") 213 .addHeader("A", "456") 214 .build(); 215 216 executeSynchronously(request) 217 .assertCode(200) 218 .assertHeader("B", "123", "234"); 219 220 RecordedRequest recordedRequest = server.takeRequest(); 221 assertEquals(Arrays.asList("345", "456"), recordedRequest.getHeaders().values("A")); 222 } 223 repeatedHeaderNames_SPDY_3()224 @Test public void repeatedHeaderNames_SPDY_3() throws Exception { 225 enableProtocol(Protocol.SPDY_3); 226 repeatedHeaderNames(); 227 } 228 repeatedHeaderNames_HTTP_2()229 @Test public void repeatedHeaderNames_HTTP_2() throws Exception { 230 enableProtocol(Protocol.HTTP_2); 231 repeatedHeaderNames(); 232 } 233 getWithRequestBody()234 @Test public void getWithRequestBody() throws Exception { 235 server.enqueue(new MockResponse()); 236 237 try { 238 new Request.Builder().method("GET", RequestBody.create(MediaType.parse("text/plain"), "abc")); 239 fail(); 240 } catch (IllegalArgumentException expected) { 241 } 242 } 243 head()244 @Test public void head() throws Exception { 245 server.enqueue(new MockResponse().addHeader("Content-Type: text/plain")); 246 247 Request request = new Request.Builder() 248 .url(server.url("/")) 249 .head() 250 .header("User-Agent", "SyncApiTest") 251 .build(); 252 253 executeSynchronously(request) 254 .assertCode(200) 255 .assertHeader("Content-Type", "text/plain"); 256 257 RecordedRequest recordedRequest = server.takeRequest(); 258 assertEquals("HEAD", recordedRequest.getMethod()); 259 assertEquals("SyncApiTest", recordedRequest.getHeader("User-Agent")); 260 assertEquals(0, recordedRequest.getBody().size()); 261 assertNull(recordedRequest.getHeader("Content-Length")); 262 } 263 head_HTTPS()264 @Test public void head_HTTPS() throws Exception { 265 enableTls(); 266 head(); 267 } 268 head_HTTP_2()269 @Test public void head_HTTP_2() throws Exception { 270 enableProtocol(Protocol.HTTP_2); 271 head(); 272 } 273 head_SPDY_3()274 @Test public void head_SPDY_3() throws Exception { 275 enableProtocol(Protocol.SPDY_3); 276 head(); 277 } 278 post()279 @Test public void post() throws Exception { 280 server.enqueue(new MockResponse().setBody("abc")); 281 282 Request request = new Request.Builder() 283 .url(server.url("/")) 284 .post(RequestBody.create(MediaType.parse("text/plain"), "def")) 285 .build(); 286 287 executeSynchronously(request) 288 .assertCode(200) 289 .assertBody("abc"); 290 291 RecordedRequest recordedRequest = server.takeRequest(); 292 assertEquals("POST", recordedRequest.getMethod()); 293 assertEquals("def", recordedRequest.getBody().readUtf8()); 294 assertEquals("3", recordedRequest.getHeader("Content-Length")); 295 assertEquals("text/plain; charset=utf-8", recordedRequest.getHeader("Content-Type")); 296 } 297 post_HTTPS()298 @Test public void post_HTTPS() throws Exception { 299 enableTls(); 300 post(); 301 } 302 post_HTTP_2()303 @Test public void post_HTTP_2() throws Exception { 304 enableProtocol(Protocol.HTTP_2); 305 post(); 306 } 307 post_SPDY_3()308 @Test public void post_SPDY_3() throws Exception { 309 enableProtocol(Protocol.SPDY_3); 310 post(); 311 } 312 postZeroLength()313 @Test public void postZeroLength() throws Exception { 314 server.enqueue(new MockResponse().setBody("abc")); 315 316 Request request = new Request.Builder() 317 .url(server.url("/")) 318 .method("POST", RequestBody.create(null, new byte[0])) 319 .build(); 320 321 executeSynchronously(request) 322 .assertCode(200) 323 .assertBody("abc"); 324 325 RecordedRequest recordedRequest = server.takeRequest(); 326 assertEquals("POST", recordedRequest.getMethod()); 327 assertEquals(0, recordedRequest.getBody().size()); 328 assertEquals("0", recordedRequest.getHeader("Content-Length")); 329 assertEquals(null, recordedRequest.getHeader("Content-Type")); 330 } 331 postZerolength_HTTPS()332 @Test public void postZerolength_HTTPS() throws Exception { 333 enableTls(); 334 postZeroLength(); 335 } 336 postZerolength_HTTP_2()337 @Test public void postZerolength_HTTP_2() throws Exception { 338 enableProtocol(Protocol.HTTP_2); 339 postZeroLength(); 340 } 341 postZeroLength_SPDY_3()342 @Test public void postZeroLength_SPDY_3() throws Exception { 343 enableProtocol(Protocol.SPDY_3); 344 postZeroLength(); 345 } 346 postBodyRetransmittedAfterAuthorizationFail()347 @Test public void postBodyRetransmittedAfterAuthorizationFail() throws Exception { 348 postBodyRetransmittedAfterAuthorizationFail("abc"); 349 } 350 postBodyRetransmittedAfterAuthorizationFail_HTTPS()351 @Test public void postBodyRetransmittedAfterAuthorizationFail_HTTPS() throws Exception { 352 enableTls(); 353 postBodyRetransmittedAfterAuthorizationFail("abc"); 354 } 355 postBodyRetransmittedAfterAuthorizationFail_HTTP_2()356 @Test public void postBodyRetransmittedAfterAuthorizationFail_HTTP_2() throws Exception { 357 enableProtocol(Protocol.HTTP_2); 358 postBodyRetransmittedAfterAuthorizationFail("abc"); 359 } 360 postBodyRetransmittedAfterAuthorizationFail_SPDY_3()361 @Test public void postBodyRetransmittedAfterAuthorizationFail_SPDY_3() throws Exception { 362 enableProtocol(Protocol.SPDY_3); 363 postBodyRetransmittedAfterAuthorizationFail("abc"); 364 } 365 366 /** Don't explode when resending an empty post. https://github.com/square/okhttp/issues/1131 */ postEmptyBodyRetransmittedAfterAuthorizationFail()367 @Test public void postEmptyBodyRetransmittedAfterAuthorizationFail() throws Exception { 368 postBodyRetransmittedAfterAuthorizationFail(""); 369 } 370 postEmptyBodyRetransmittedAfterAuthorizationFail_HTTPS()371 @Test public void postEmptyBodyRetransmittedAfterAuthorizationFail_HTTPS() throws Exception { 372 enableTls(); 373 postBodyRetransmittedAfterAuthorizationFail(""); 374 } 375 postEmptyBodyRetransmittedAfterAuthorizationFail_HTTP_2()376 @Test public void postEmptyBodyRetransmittedAfterAuthorizationFail_HTTP_2() throws Exception { 377 enableProtocol(Protocol.HTTP_2); 378 postBodyRetransmittedAfterAuthorizationFail(""); 379 } 380 postEmptyBodyRetransmittedAfterAuthorizationFail_SPDY_3()381 @Test public void postEmptyBodyRetransmittedAfterAuthorizationFail_SPDY_3() throws Exception { 382 enableProtocol(Protocol.SPDY_3); 383 postBodyRetransmittedAfterAuthorizationFail(""); 384 } 385 postBodyRetransmittedAfterAuthorizationFail(String body)386 private void postBodyRetransmittedAfterAuthorizationFail(String body) throws Exception { 387 server.enqueue(new MockResponse().setResponseCode(401)); 388 server.enqueue(new MockResponse()); 389 390 Request request = new Request.Builder() 391 .url(server.url("/")) 392 .method("POST", RequestBody.create(null, body)) 393 .build(); 394 395 String credential = Credentials.basic("jesse", "secret"); 396 client.setAuthenticator(new RecordingOkAuthenticator(credential)); 397 398 Response response = client.newCall(request).execute(); 399 assertEquals(200, response.code()); 400 401 RecordedRequest recordedRequest1 = server.takeRequest(); 402 assertEquals("POST", recordedRequest1.getMethod()); 403 assertEquals(body, recordedRequest1.getBody().readUtf8()); 404 assertNull(recordedRequest1.getHeader("Authorization")); 405 406 RecordedRequest recordedRequest2 = server.takeRequest(); 407 assertEquals("POST", recordedRequest2.getMethod()); 408 assertEquals(body, recordedRequest2.getBody().readUtf8()); 409 assertEquals(credential, recordedRequest2.getHeader("Authorization")); 410 } 411 attemptAuthorization20Times()412 @Test public void attemptAuthorization20Times() throws Exception { 413 for (int i = 0; i < 20; i++) { 414 server.enqueue(new MockResponse().setResponseCode(401)); 415 } 416 server.enqueue(new MockResponse().setBody("Success!")); 417 418 String credential = Credentials.basic("jesse", "secret"); 419 client.setAuthenticator(new RecordingOkAuthenticator(credential)); 420 421 Request request = new Request.Builder().url(server.url("/")).build(); 422 executeSynchronously(request) 423 .assertCode(200) 424 .assertBody("Success!"); 425 } 426 doesNotAttemptAuthorization21Times()427 @Test public void doesNotAttemptAuthorization21Times() throws Exception { 428 for (int i = 0; i < 21; i++) { 429 server.enqueue(new MockResponse().setResponseCode(401)); 430 } 431 432 String credential = Credentials.basic("jesse", "secret"); 433 client.setAuthenticator(new RecordingOkAuthenticator(credential)); 434 435 try { 436 client.newCall(new Request.Builder().url(server.url("/0")).build()).execute(); 437 fail(); 438 } catch (IOException expected) { 439 assertEquals("Too many follow-up requests: 21", expected.getMessage()); 440 } 441 } 442 delete()443 @Test public void delete() throws Exception { 444 server.enqueue(new MockResponse().setBody("abc")); 445 446 Request request = new Request.Builder() 447 .url(server.url("/")) 448 .delete() 449 .build(); 450 451 executeSynchronously(request) 452 .assertCode(200) 453 .assertBody("abc"); 454 455 RecordedRequest recordedRequest = server.takeRequest(); 456 assertEquals("DELETE", recordedRequest.getMethod()); 457 assertEquals(0, recordedRequest.getBody().size()); 458 assertEquals("0", recordedRequest.getHeader("Content-Length")); 459 assertEquals(null, recordedRequest.getHeader("Content-Type")); 460 } 461 delete_HTTPS()462 @Test public void delete_HTTPS() throws Exception { 463 enableTls(); 464 delete(); 465 } 466 delete_HTTP_2()467 @Test public void delete_HTTP_2() throws Exception { 468 enableProtocol(Protocol.HTTP_2); 469 delete(); 470 } 471 delete_SPDY_3()472 @Test public void delete_SPDY_3() throws Exception { 473 enableProtocol(Protocol.SPDY_3); 474 delete(); 475 } 476 deleteWithRequestBody()477 @Test public void deleteWithRequestBody() throws Exception { 478 server.enqueue(new MockResponse().setBody("abc")); 479 480 Request request = new Request.Builder() 481 .url(server.url("/")) 482 .method("DELETE", RequestBody.create(MediaType.parse("text/plain"), "def")) 483 .build(); 484 485 executeSynchronously(request) 486 .assertCode(200) 487 .assertBody("abc"); 488 489 RecordedRequest recordedRequest = server.takeRequest(); 490 assertEquals("DELETE", recordedRequest.getMethod()); 491 assertEquals("def", recordedRequest.getBody().readUtf8()); 492 } 493 put()494 @Test public void put() throws Exception { 495 server.enqueue(new MockResponse().setBody("abc")); 496 497 Request request = new Request.Builder() 498 .url(server.url("/")) 499 .put(RequestBody.create(MediaType.parse("text/plain"), "def")) 500 .build(); 501 502 executeSynchronously(request) 503 .assertCode(200) 504 .assertBody("abc"); 505 506 RecordedRequest recordedRequest = server.takeRequest(); 507 assertEquals("PUT", recordedRequest.getMethod()); 508 assertEquals("def", recordedRequest.getBody().readUtf8()); 509 assertEquals("3", recordedRequest.getHeader("Content-Length")); 510 assertEquals("text/plain; charset=utf-8", recordedRequest.getHeader("Content-Type")); 511 } 512 put_HTTPS()513 @Test public void put_HTTPS() throws Exception { 514 enableTls(); 515 put(); 516 } 517 put_HTTP_2()518 @Test public void put_HTTP_2() throws Exception { 519 enableProtocol(Protocol.HTTP_2); 520 put(); 521 } 522 put_SPDY_3()523 @Test public void put_SPDY_3() throws Exception { 524 enableProtocol(Protocol.SPDY_3); 525 put(); 526 } 527 patch()528 @Test public void patch() throws Exception { 529 server.enqueue(new MockResponse().setBody("abc")); 530 531 Request request = new Request.Builder() 532 .url(server.url("/")) 533 .patch(RequestBody.create(MediaType.parse("text/plain"), "def")) 534 .build(); 535 536 executeSynchronously(request) 537 .assertCode(200) 538 .assertBody("abc"); 539 540 RecordedRequest recordedRequest = server.takeRequest(); 541 assertEquals("PATCH", recordedRequest.getMethod()); 542 assertEquals("def", recordedRequest.getBody().readUtf8()); 543 assertEquals("3", recordedRequest.getHeader("Content-Length")); 544 assertEquals("text/plain; charset=utf-8", recordedRequest.getHeader("Content-Type")); 545 } 546 patch_HTTP_2()547 @Test public void patch_HTTP_2() throws Exception { 548 enableProtocol(Protocol.HTTP_2); 549 patch(); 550 } 551 patch_HTTPS()552 @Test public void patch_HTTPS() throws Exception { 553 enableTls(); 554 patch(); 555 } 556 patch_SPDY_3()557 @Test public void patch_SPDY_3() throws Exception { 558 enableProtocol(Protocol.SPDY_3); 559 patch(); 560 } 561 unspecifiedRequestBodyContentTypeDoesNotGetDefault()562 @Test public void unspecifiedRequestBodyContentTypeDoesNotGetDefault() throws Exception { 563 server.enqueue(new MockResponse()); 564 565 Request request = new Request.Builder() 566 .url(server.url("/")) 567 .method("POST", RequestBody.create(null, "abc")) 568 .build(); 569 570 executeSynchronously(request).assertCode(200); 571 572 RecordedRequest recordedRequest = server.takeRequest(); 573 assertEquals(null, recordedRequest.getHeader("Content-Type")); 574 assertEquals("3", recordedRequest.getHeader("Content-Length")); 575 assertEquals("abc", recordedRequest.getBody().readUtf8()); 576 } 577 illegalToExecuteTwice()578 @Test public void illegalToExecuteTwice() throws Exception { 579 server.enqueue(new MockResponse() 580 .setBody("abc") 581 .addHeader("Content-Type: text/plain")); 582 583 Request request = new Request.Builder() 584 .url(server.url("/")) 585 .header("User-Agent", "SyncApiTest") 586 .build(); 587 588 Call call = client.newCall(request); 589 Response response = call.execute(); 590 response.body().close(); 591 592 try { 593 call.execute(); 594 fail(); 595 } catch (IllegalStateException e){ 596 assertEquals("Already Executed", e.getMessage()); 597 } 598 599 try { 600 call.enqueue(callback); 601 fail(); 602 } catch (IllegalStateException e){ 603 assertEquals("Already Executed", e.getMessage()); 604 } 605 606 assertEquals("SyncApiTest", server.takeRequest().getHeader("User-Agent")); 607 } 608 illegalToExecuteTwice_Async()609 @Test public void illegalToExecuteTwice_Async() throws Exception { 610 server.enqueue(new MockResponse() 611 .setBody("abc") 612 .addHeader("Content-Type: text/plain")); 613 614 Request request = new Request.Builder() 615 .url(server.url("/")) 616 .header("User-Agent", "SyncApiTest") 617 .build(); 618 619 Call call = client.newCall(request); 620 call.enqueue(callback); 621 622 try { 623 call.execute(); 624 fail(); 625 } catch (IllegalStateException e){ 626 assertEquals("Already Executed", e.getMessage()); 627 } 628 629 try { 630 call.enqueue(callback); 631 fail(); 632 } catch (IllegalStateException e){ 633 assertEquals("Already Executed", e.getMessage()); 634 } 635 636 assertEquals("SyncApiTest", server.takeRequest().getHeader("User-Agent")); 637 } 638 get_Async()639 @Test public void get_Async() throws Exception { 640 server.enqueue(new MockResponse() 641 .setBody("abc") 642 .addHeader("Content-Type: text/plain")); 643 644 Request request = new Request.Builder() 645 .url(server.url("/")) 646 .header("User-Agent", "AsyncApiTest") 647 .build(); 648 client.newCall(request).enqueue(callback); 649 650 callback.await(request.httpUrl()) 651 .assertCode(200) 652 .assertHeader("Content-Type", "text/plain") 653 .assertBody("abc"); 654 655 assertEquals("AsyncApiTest", server.takeRequest().getHeader("User-Agent")); 656 } 657 exceptionThrownByOnResponseIsRedactedAndLogged()658 @Test public void exceptionThrownByOnResponseIsRedactedAndLogged() throws Exception { 659 server.enqueue(new MockResponse()); 660 661 Request request = new Request.Builder() 662 .url(server.url("/secret")) 663 .build(); 664 665 client.newCall(request).enqueue(new Callback() { 666 @Override public void onFailure(Request request, IOException e) { 667 fail(); 668 } 669 670 @Override public void onResponse(Response response) throws IOException { 671 throw new IOException("a"); 672 } 673 }); 674 675 assertEquals("INFO: Callback failure for call to " + server.url("/") + "...", 676 logHandler.take()); 677 } 678 connectionPooling()679 @Test public void connectionPooling() throws Exception { 680 server.enqueue(new MockResponse().setBody("abc")); 681 server.enqueue(new MockResponse().setBody("def")); 682 server.enqueue(new MockResponse().setBody("ghi")); 683 684 executeSynchronously(new Request.Builder().url(server.url("/a")).build()) 685 .assertBody("abc"); 686 687 executeSynchronously(new Request.Builder().url(server.url("/b")).build()) 688 .assertBody("def"); 689 690 executeSynchronously(new Request.Builder().url(server.url("/c")).build()) 691 .assertBody("ghi"); 692 693 assertEquals(0, server.takeRequest().getSequenceNumber()); 694 assertEquals(1, server.takeRequest().getSequenceNumber()); 695 assertEquals(2, server.takeRequest().getSequenceNumber()); 696 } 697 connectionPooling_Async()698 @Test public void connectionPooling_Async() throws Exception { 699 server.enqueue(new MockResponse().setBody("abc")); 700 server.enqueue(new MockResponse().setBody("def")); 701 server.enqueue(new MockResponse().setBody("ghi")); 702 703 client.newCall(new Request.Builder().url(server.url("/a")).build()).enqueue(callback); 704 callback.await(server.url("/a")).assertBody("abc"); 705 706 client.newCall(new Request.Builder().url(server.url("/b")).build()).enqueue(callback); 707 callback.await(server.url("/b")).assertBody("def"); 708 709 client.newCall(new Request.Builder().url(server.url("/c")).build()).enqueue(callback); 710 callback.await(server.url("/c")).assertBody("ghi"); 711 712 assertEquals(0, server.takeRequest().getSequenceNumber()); 713 assertEquals(1, server.takeRequest().getSequenceNumber()); 714 assertEquals(2, server.takeRequest().getSequenceNumber()); 715 } 716 connectionReuseWhenResponseBodyConsumed_Async()717 @Test public void connectionReuseWhenResponseBodyConsumed_Async() throws Exception { 718 server.enqueue(new MockResponse().setBody("abc")); 719 server.enqueue(new MockResponse().setBody("def")); 720 721 Request request = new Request.Builder().url(server.url("/a")).build(); 722 client.newCall(request).enqueue(new Callback() { 723 @Override public void onFailure(Request request, IOException e) { 724 throw new AssertionError(); 725 } 726 727 @Override public void onResponse(Response response) throws IOException { 728 InputStream bytes = response.body().byteStream(); 729 assertEquals('a', bytes.read()); 730 assertEquals('b', bytes.read()); 731 assertEquals('c', bytes.read()); 732 733 // This request will share a connection with 'A' cause it's all done. 734 client.newCall(new Request.Builder().url(server.url("/b")).build()).enqueue(callback); 735 } 736 }); 737 738 callback.await(server.url("/b")).assertCode(200).assertBody("def"); 739 assertEquals(0, server.takeRequest().getSequenceNumber()); // New connection. 740 assertEquals(1, server.takeRequest().getSequenceNumber()); // Connection reuse! 741 } 742 timeoutsUpdatedOnReusedConnections()743 @Test public void timeoutsUpdatedOnReusedConnections() throws Exception { 744 server.enqueue(new MockResponse().setBody("abc")); 745 server.enqueue(new MockResponse().setBody("def").throttleBody(1, 750, TimeUnit.MILLISECONDS)); 746 747 // First request: time out after 1000ms. 748 client.setReadTimeout(1000, TimeUnit.MILLISECONDS); 749 executeSynchronously(new Request.Builder().url(server.url("/a")).build()).assertBody("abc"); 750 751 // Second request: time out after 250ms. 752 client.setReadTimeout(250, TimeUnit.MILLISECONDS); 753 Request request = new Request.Builder().url(server.url("/b")).build(); 754 Response response = client.newCall(request).execute(); 755 BufferedSource bodySource = response.body().source(); 756 assertEquals('d', bodySource.readByte()); 757 758 // The second byte of this request will be delayed by 750ms so we should time out after 250ms. 759 long startNanos = System.nanoTime(); 760 try { 761 bodySource.readByte(); 762 fail(); 763 } catch (IOException expected) { 764 // Timed out as expected. 765 long elapsedNanos = System.nanoTime() - startNanos; 766 long elapsedMillis = TimeUnit.NANOSECONDS.toMillis(elapsedNanos); 767 assertTrue(String.format("Timed out: %sms", elapsedMillis), elapsedMillis < 500); 768 } finally { 769 bodySource.close(); 770 } 771 } 772 773 /** https://github.com/square/okhttp/issues/442 */ timeoutsNotRetried()774 @Test public void timeoutsNotRetried() throws Exception { 775 server.enqueue(new MockResponse() 776 .setSocketPolicy(SocketPolicy.NO_RESPONSE)); 777 server.enqueue(new MockResponse() 778 .setBody("unreachable!")); 779 780 client.setDns(new DoubleInetAddressDns()); 781 client.setReadTimeout(100, TimeUnit.MILLISECONDS); 782 783 Request request = new Request.Builder().url(server.url("/")).build(); 784 try { 785 // If this succeeds, too many requests were made. 786 client.newCall(request).execute(); 787 fail(); 788 } catch (InterruptedIOException expected) { 789 } 790 } 791 792 /** https://github.com/square/okhttp/issues/1801 */ asyncCallEngineInitialized()793 @Test public void asyncCallEngineInitialized() throws Exception { 794 OkHttpClient c = new OkHttpClient(); 795 c.interceptors().add(new Interceptor() { 796 @Override public Response intercept(Chain chain) throws IOException { 797 throw new IOException(); 798 } 799 }); 800 Request request = new Request.Builder().url(server.url("/")).build(); 801 c.newCall(request).enqueue(callback); 802 RecordedResponse response = callback.await(request.httpUrl()); 803 assertEquals(request, response.request); 804 } 805 reusedSinksGetIndependentTimeoutInstances()806 @Test public void reusedSinksGetIndependentTimeoutInstances() throws Exception { 807 server.enqueue(new MockResponse()); 808 server.enqueue(new MockResponse()); 809 810 // Call 1: set a deadline on the request body. 811 RequestBody requestBody1 = new RequestBody() { 812 @Override public MediaType contentType() { 813 return MediaType.parse("text/plain"); 814 } 815 @Override public void writeTo(BufferedSink sink) throws IOException { 816 sink.writeUtf8("abc"); 817 sink.timeout().deadline(5, TimeUnit.SECONDS); 818 } 819 }; 820 Request request1 = new Request.Builder() 821 .url(server.url("/")) 822 .method("POST", requestBody1) 823 .build(); 824 Response response1 = client.newCall(request1).execute(); 825 assertEquals(200, response1.code()); 826 827 // Call 2: check for the absence of a deadline on the request body. 828 RequestBody requestBody2 = new RequestBody() { 829 @Override public MediaType contentType() { 830 return MediaType.parse("text/plain"); 831 } 832 @Override public void writeTo(BufferedSink sink) throws IOException { 833 assertFalse(sink.timeout().hasDeadline()); 834 sink.writeUtf8("def"); 835 } 836 }; 837 Request request2 = new Request.Builder() 838 .url(server.url("/")) 839 .method("POST", requestBody2) 840 .build(); 841 Response response2 = client.newCall(request2).execute(); 842 assertEquals(200, response2.code()); 843 844 // Use sequence numbers to confirm the connection was pooled. 845 assertEquals(0, server.takeRequest().getSequenceNumber()); 846 assertEquals(1, server.takeRequest().getSequenceNumber()); 847 } 848 reusedSourcesGetIndependentTimeoutInstances()849 @Test public void reusedSourcesGetIndependentTimeoutInstances() throws Exception { 850 server.enqueue(new MockResponse().setBody("abc")); 851 server.enqueue(new MockResponse().setBody("def")); 852 853 // Call 1: set a deadline on the response body. 854 Request request1 = new Request.Builder().url(server.url("/")).build(); 855 Response response1 = client.newCall(request1).execute(); 856 BufferedSource body1 = response1.body().source(); 857 assertEquals("abc", body1.readUtf8()); 858 body1.timeout().deadline(5, TimeUnit.SECONDS); 859 860 // Call 2: check for the absence of a deadline on the request body. 861 Request request2 = new Request.Builder().url(server.url("/")).build(); 862 Response response2 = client.newCall(request2).execute(); 863 BufferedSource body2 = response2.body().source(); 864 assertEquals("def", body2.readUtf8()); 865 assertFalse(body2.timeout().hasDeadline()); 866 867 // Use sequence numbers to confirm the connection was pooled. 868 assertEquals(0, server.takeRequest().getSequenceNumber()); 869 assertEquals(1, server.takeRequest().getSequenceNumber()); 870 } 871 tls()872 @Test public void tls() throws Exception { 873 enableTls(); 874 server.enqueue(new MockResponse() 875 .setBody("abc") 876 .addHeader("Content-Type: text/plain")); 877 878 executeSynchronously(new Request.Builder().url(server.url("/")).build()) 879 .assertHandshake(); 880 } 881 tls_Async()882 @Test public void tls_Async() throws Exception { 883 enableTls(); 884 server.enqueue(new MockResponse() 885 .setBody("abc") 886 .addHeader("Content-Type: text/plain")); 887 888 Request request = new Request.Builder() 889 .url(server.url("/")) 890 .build(); 891 client.newCall(request).enqueue(callback); 892 893 callback.await(request.httpUrl()).assertHandshake(); 894 } 895 recoverWhenRetryOnConnectionFailureIsTrue()896 @Test public void recoverWhenRetryOnConnectionFailureIsTrue() throws Exception { 897 server.enqueue(new MockResponse().setBody("seed connection pool")); 898 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AFTER_REQUEST)); 899 server.enqueue(new MockResponse().setBody("retry success")); 900 901 client.setDns(new DoubleInetAddressDns()); 902 assertTrue(client.getRetryOnConnectionFailure()); 903 904 Request request = new Request.Builder().url(server.url("/")).build(); 905 executeSynchronously(request).assertBody("seed connection pool"); 906 executeSynchronously(request).assertBody("retry success"); 907 } 908 noRecoverWhenRetryOnConnectionFailureIsFalse()909 @Test public void noRecoverWhenRetryOnConnectionFailureIsFalse() throws Exception { 910 server.enqueue(new MockResponse().setBody("seed connection pool")); 911 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AFTER_REQUEST)); 912 server.enqueue(new MockResponse().setBody("unreachable!")); 913 914 client.setDns(new DoubleInetAddressDns()); 915 client.setRetryOnConnectionFailure(false); 916 917 Request request = new Request.Builder().url(server.url("/")).build(); 918 executeSynchronously(request).assertBody("seed connection pool"); 919 try { 920 // If this succeeds, too many requests were made. 921 client.newCall(request).execute(); 922 fail(); 923 } catch (IOException expected) { 924 } 925 } 926 recoverFromTlsHandshakeFailure()927 @Test public void recoverFromTlsHandshakeFailure() throws Exception { 928 server.useHttps(sslContext.getSocketFactory(), false); 929 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE)); 930 server.enqueue(new MockResponse().setBody("abc")); 931 932 suppressTlsFallbackScsv(client); 933 // Android-added: Use TLS 1.3 and 1.2 for testing 934 client.setConnectionSpecs(TLS_SPEC_NO_V1); 935 client.setHostnameVerifier(new RecordingHostnameVerifier()); 936 client.setDns(new SingleInetAddressDns()); 937 938 executeSynchronously(new Request.Builder().url(server.url("/")).build()) 939 .assertBody("abc"); 940 } 941 recoverFromTlsHandshakeFailure_tlsFallbackScsvEnabled()942 @Test public void recoverFromTlsHandshakeFailure_tlsFallbackScsvEnabled() throws Exception { 943 final String tlsFallbackScsv = "TLS_FALLBACK_SCSV"; 944 List<String> supportedCiphers = 945 Arrays.asList(sslContext.getSocketFactory().getSupportedCipherSuites()); 946 if (!supportedCiphers.contains(tlsFallbackScsv)) { 947 // This only works if the client socket supports TLS_FALLBACK_SCSV. 948 return; 949 } 950 951 server.useHttps(sslContext.getSocketFactory(), false); 952 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE)); 953 // Android-added: Need an extra handshake fail when using TLS 1.3 and 1.2 for testing. 954 // Seems to be a testing quirk due to adding two ConnectionSpecs and has no impact 955 // on the logic being tested or the expected outcomes, so not gonna dig too deep. 956 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE)); 957 958 RecordingSSLSocketFactory clientSocketFactory = 959 new RecordingSSLSocketFactory(sslContext.getSocketFactory()); 960 client.setSslSocketFactory(clientSocketFactory); 961 client.setHostnameVerifier(new RecordingHostnameVerifier()); 962 client.setDns(new SingleInetAddressDns()); 963 // Android-added: Use TLS 1.3 and 1.2 for testing 964 client.setConnectionSpecs(TLS_SPEC_NO_V1); 965 966 Request request = new Request.Builder().url(server.url("/")).build(); 967 try { 968 client.newCall(request).execute(); 969 fail(); 970 } catch (SSLHandshakeException expected) { 971 } 972 973 List<SSLSocket> clientSockets = clientSocketFactory.getSocketsCreated(); 974 SSLSocket firstSocket = clientSockets.get(0); 975 assertFalse(Arrays.asList(firstSocket.getEnabledCipherSuites()).contains(tlsFallbackScsv)); 976 SSLSocket secondSocket = clientSockets.get(1); 977 assertTrue(Arrays.asList(secondSocket.getEnabledCipherSuites()).contains(tlsFallbackScsv)); 978 } 979 recoverFromTlsHandshakeFailure_Async()980 @Test public void recoverFromTlsHandshakeFailure_Async() throws Exception { 981 server.useHttps(sslContext.getSocketFactory(), false); 982 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE)); 983 server.enqueue(new MockResponse().setBody("abc")); 984 985 suppressTlsFallbackScsv(client); 986 client.setHostnameVerifier(new RecordingHostnameVerifier()); 987 // Android-added: Use TLS 1.3 and 1.2 for testing 988 client.setConnectionSpecs(TLS_SPEC_NO_V1); 989 990 Request request = new Request.Builder() 991 .url(server.url("/")) 992 .build(); 993 client.newCall(request).enqueue(callback); 994 995 callback.await(request.httpUrl()).assertBody("abc"); 996 } 997 noRecoveryFromTlsHandshakeFailureWhenTlsFallbackIsDisabled()998 @Test public void noRecoveryFromTlsHandshakeFailureWhenTlsFallbackIsDisabled() throws Exception { 999 client.setConnectionSpecs(Arrays.asList(ConnectionSpec.MODERN_TLS, ConnectionSpec.CLEARTEXT)); 1000 1001 server.useHttps(sslContext.getSocketFactory(), false); 1002 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE)); 1003 1004 suppressTlsFallbackScsv(client); 1005 client.setHostnameVerifier(new RecordingHostnameVerifier()); 1006 client.setDns(new SingleInetAddressDns()); 1007 1008 Request request = new Request.Builder().url(server.url("/")).build(); 1009 try { 1010 client.newCall(request).execute(); 1011 fail(); 1012 } catch (SSLProtocolException expected) { 1013 // RI response to the FAIL_HANDSHAKE 1014 } catch (SSLHandshakeException expected) { 1015 // Android's response to the FAIL_HANDSHAKE 1016 } 1017 } 1018 cleartextCallsFailWhenCleartextIsDisabled()1019 @Test public void cleartextCallsFailWhenCleartextIsDisabled() throws Exception { 1020 // Configure the client with only TLS configurations. No cleartext! 1021 client.setConnectionSpecs( 1022 Arrays.asList(ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS)); 1023 1024 server.enqueue(new MockResponse()); 1025 1026 Request request = new Request.Builder().url(server.url("/")).build(); 1027 try { 1028 client.newCall(request).execute(); 1029 fail(); 1030 } catch (UnknownServiceException expected) { 1031 assertTrue(expected.getMessage().contains("CLEARTEXT communication not supported")); 1032 } 1033 } 1034 setFollowSslRedirectsFalse()1035 @Test public void setFollowSslRedirectsFalse() throws Exception { 1036 enableTls(); 1037 server.enqueue(new MockResponse() 1038 .setResponseCode(301) 1039 .addHeader("Location: http://square.com")); 1040 1041 client.setFollowSslRedirects(false); 1042 1043 Request request = new Request.Builder().url(server.url("/")).build(); 1044 Response response = client.newCall(request).execute(); 1045 assertEquals(301, response.code()); 1046 response.body().close(); 1047 } 1048 matchingPinnedCertificate()1049 @Test public void matchingPinnedCertificate() throws Exception { 1050 enableTls(); 1051 server.enqueue(new MockResponse()); 1052 server.enqueue(new MockResponse()); 1053 1054 // Make a first request without certificate pinning. Use it to collect certificates to pin. 1055 Request request1 = new Request.Builder().url(server.url("/")).build(); 1056 Response response1 = client.newCall(request1).execute(); 1057 CertificatePinner.Builder certificatePinnerBuilder = new CertificatePinner.Builder(); 1058 for (Certificate certificate : response1.handshake().peerCertificates()) { 1059 certificatePinnerBuilder.add(server.getHostName(), CertificatePinner.pin(certificate)); 1060 } 1061 response1.body().close(); 1062 1063 // Make another request with certificate pinning. It should complete normally. 1064 client.setCertificatePinner(certificatePinnerBuilder.build()); 1065 Request request2 = new Request.Builder().url(server.url("/")).build(); 1066 Response response2 = client.newCall(request2).execute(); 1067 assertNotSame(response2.handshake(), response1.handshake()); 1068 response2.body().close(); 1069 } 1070 unmatchingPinnedCertificate()1071 @Test public void unmatchingPinnedCertificate() throws Exception { 1072 enableTls(); 1073 server.enqueue(new MockResponse()); 1074 1075 // Pin publicobject.com's cert. 1076 client.setCertificatePinner(new CertificatePinner.Builder() 1077 .add(server.getHostName(), "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=") 1078 .build()); 1079 1080 // When we pin the wrong certificate, connectivity fails. 1081 Request request = new Request.Builder().url(server.url("/")).build(); 1082 try { 1083 client.newCall(request).execute(); 1084 fail(); 1085 } catch (SSLPeerUnverifiedException expected) { 1086 assertTrue(expected.getMessage().startsWith("Certificate pinning failure!")); 1087 } 1088 } 1089 post_Async()1090 @Test public void post_Async() throws Exception { 1091 server.enqueue(new MockResponse().setBody("abc")); 1092 1093 Request request = new Request.Builder() 1094 .url(server.url("/")) 1095 .post(RequestBody.create(MediaType.parse("text/plain"), "def")) 1096 .build(); 1097 client.newCall(request).enqueue(callback); 1098 1099 callback.await(request.httpUrl()) 1100 .assertCode(200) 1101 .assertBody("abc"); 1102 1103 RecordedRequest recordedRequest = server.takeRequest(); 1104 assertEquals("def", recordedRequest.getBody().readUtf8()); 1105 assertEquals("3", recordedRequest.getHeader("Content-Length")); 1106 assertEquals("text/plain; charset=utf-8", recordedRequest.getHeader("Content-Type")); 1107 } 1108 postBodyRetransmittedOnFailureRecovery()1109 @Test public void postBodyRetransmittedOnFailureRecovery() throws Exception { 1110 server.enqueue(new MockResponse().setBody("abc")); 1111 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AFTER_REQUEST)); 1112 server.enqueue(new MockResponse().setBody("def")); 1113 1114 // Seed the connection pool so we have something that can fail. 1115 Request request1 = new Request.Builder().url(server.url("/")).build(); 1116 Response response1 = client.newCall(request1).execute(); 1117 assertEquals("abc", response1.body().string()); 1118 1119 Request request2 = new Request.Builder() 1120 .url(server.url("/")) 1121 .post(RequestBody.create(MediaType.parse("text/plain"), "body!")) 1122 .build(); 1123 Response response2 = client.newCall(request2).execute(); 1124 assertEquals("def", response2.body().string()); 1125 1126 RecordedRequest get = server.takeRequest(); 1127 assertEquals(0, get.getSequenceNumber()); 1128 1129 RecordedRequest post1 = server.takeRequest(); 1130 assertEquals("body!", post1.getBody().readUtf8()); 1131 assertEquals(1, post1.getSequenceNumber()); 1132 1133 RecordedRequest post2 = server.takeRequest(); 1134 assertEquals("body!", post2.getBody().readUtf8()); 1135 assertEquals(0, post2.getSequenceNumber()); 1136 } 1137 cacheHit()1138 @Test public void cacheHit() throws Exception { 1139 server.enqueue(new MockResponse() 1140 .addHeader("ETag: v1") 1141 .addHeader("Cache-Control: max-age=60") 1142 .addHeader("Vary: Accept-Charset") 1143 .setBody("A")); 1144 1145 client.setCache(cache); 1146 1147 // Store a response in the cache. 1148 HttpUrl url = server.url("/"); 1149 Request cacheStoreRequest = new Request.Builder() 1150 .url(url) 1151 .addHeader("Accept-Language", "fr-CA") 1152 .addHeader("Accept-Charset", "UTF-8") 1153 .build(); 1154 executeSynchronously(cacheStoreRequest) 1155 .assertCode(200) 1156 .assertBody("A"); 1157 assertNull(server.takeRequest().getHeader("If-None-Match")); 1158 1159 // Hit that stored response. 1160 Request cacheHitRequest = new Request.Builder() 1161 .url(url) 1162 .addHeader("Accept-Language", "en-US") // Different, but Vary says it doesn't matter. 1163 .addHeader("Accept-Charset", "UTF-8") 1164 .build(); 1165 RecordedResponse cacheHit = executeSynchronously(cacheHitRequest); 1166 1167 // Check the merged response. The request is the application's original request. 1168 cacheHit.assertCode(200) 1169 .assertBody("A") 1170 .assertHeader("ETag", "v1") 1171 .assertRequestUrl(cacheStoreRequest.url()) 1172 .assertRequestHeader("Accept-Language", "en-US") 1173 .assertRequestHeader("Accept-Charset", "UTF-8"); 1174 1175 // Check the cached response. Its request contains only the saved Vary headers. 1176 cacheHit.cacheResponse() 1177 .assertCode(200) 1178 .assertHeader("ETag", "v1") 1179 .assertRequestMethod("GET") 1180 .assertRequestUrl(cacheStoreRequest.url()) 1181 .assertRequestHeader("Accept-Language") 1182 .assertRequestHeader("Accept-Charset", "UTF-8"); 1183 1184 cacheHit.assertNoNetworkResponse(); 1185 } 1186 conditionalCacheHit()1187 @Test public void conditionalCacheHit() throws Exception { 1188 server.enqueue(new MockResponse() 1189 .addHeader("ETag: v1") 1190 .addHeader("Vary: Accept-Charset") 1191 .addHeader("Donut: a") 1192 .setBody("A")); 1193 server.enqueue(new MockResponse().clearHeaders() 1194 .addHeader("Donut: b") 1195 .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED)); 1196 1197 client.setCache(cache); 1198 1199 // Store a response in the cache. 1200 HttpUrl url = server.url("/"); 1201 Request cacheStoreRequest = new Request.Builder() 1202 .url(url) 1203 .addHeader("Accept-Language", "fr-CA") 1204 .addHeader("Accept-Charset", "UTF-8") 1205 .build(); 1206 executeSynchronously(cacheStoreRequest) 1207 .assertCode(200) 1208 .assertHeader("Donut", "a") 1209 .assertBody("A"); 1210 assertNull(server.takeRequest().getHeader("If-None-Match")); 1211 1212 // Hit that stored response. 1213 Request cacheHitRequest = new Request.Builder() 1214 .url(url) 1215 .addHeader("Accept-Language", "en-US") // Different, but Vary says it doesn't matter. 1216 .addHeader("Accept-Charset", "UTF-8") 1217 .build(); 1218 RecordedResponse cacheHit = executeSynchronously(cacheHitRequest); 1219 assertEquals("v1", server.takeRequest().getHeader("If-None-Match")); 1220 1221 // Check the merged response. The request is the application's original request. 1222 cacheHit.assertCode(200) 1223 .assertBody("A") 1224 .assertHeader("Donut", "b") 1225 .assertRequestUrl(cacheStoreRequest.url()) 1226 .assertRequestHeader("Accept-Language", "en-US") 1227 .assertRequestHeader("Accept-Charset", "UTF-8") 1228 .assertRequestHeader("If-None-Match"); // No If-None-Match on the user's request. 1229 1230 // Check the cached response. Its request contains only the saved Vary headers. 1231 cacheHit.cacheResponse() 1232 .assertCode(200) 1233 .assertHeader("Donut", "a") 1234 .assertHeader("ETag", "v1") 1235 .assertRequestUrl(cacheStoreRequest.url()) 1236 .assertRequestHeader("Accept-Language") // No Vary on Accept-Language. 1237 .assertRequestHeader("Accept-Charset", "UTF-8") // Because of Vary on Accept-Charset. 1238 .assertRequestHeader("If-None-Match"); // This wasn't present in the original request. 1239 1240 // Check the network response. It has the caller's request, plus some caching headers. 1241 cacheHit.networkResponse() 1242 .assertCode(304) 1243 .assertHeader("Donut", "b") 1244 .assertRequestHeader("Accept-Language", "en-US") 1245 .assertRequestHeader("Accept-Charset", "UTF-8") 1246 .assertRequestHeader("If-None-Match", "v1"); // If-None-Match in the validation request. 1247 } 1248 conditionalCacheHit_Async()1249 @Test public void conditionalCacheHit_Async() throws Exception { 1250 server.enqueue(new MockResponse().setBody("A").addHeader("ETag: v1")); 1251 server.enqueue(new MockResponse() 1252 .clearHeaders() 1253 .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED)); 1254 1255 client.setCache(cache); 1256 1257 Request request1 = new Request.Builder() 1258 .url(server.url("/")) 1259 .build(); 1260 client.newCall(request1).enqueue(callback); 1261 callback.await(request1.httpUrl()).assertCode(200).assertBody("A"); 1262 assertNull(server.takeRequest().getHeader("If-None-Match")); 1263 1264 Request request2 = new Request.Builder() 1265 .url(server.url("/")) 1266 .build(); 1267 client.newCall(request2).enqueue(callback); 1268 callback.await(request2.httpUrl()).assertCode(200).assertBody("A"); 1269 assertEquals("v1", server.takeRequest().getHeader("If-None-Match")); 1270 } 1271 conditionalCacheMiss()1272 @Test public void conditionalCacheMiss() throws Exception { 1273 server.enqueue(new MockResponse() 1274 .addHeader("ETag: v1") 1275 .addHeader("Vary: Accept-Charset") 1276 .addHeader("Donut: a") 1277 .setBody("A")); 1278 server.enqueue(new MockResponse() 1279 .addHeader("Donut: b") 1280 .setBody("B")); 1281 1282 client.setCache(cache); 1283 1284 Request cacheStoreRequest = new Request.Builder() 1285 .url(server.url("/")) 1286 .addHeader("Accept-Language", "fr-CA") 1287 .addHeader("Accept-Charset", "UTF-8") 1288 .build(); 1289 executeSynchronously(cacheStoreRequest) 1290 .assertCode(200) 1291 .assertBody("A"); 1292 assertNull(server.takeRequest().getHeader("If-None-Match")); 1293 1294 Request cacheMissRequest = new Request.Builder() 1295 .url(server.url("/")) 1296 .addHeader("Accept-Language", "en-US") // Different, but Vary says it doesn't matter. 1297 .addHeader("Accept-Charset", "UTF-8") 1298 .build(); 1299 RecordedResponse cacheHit = executeSynchronously(cacheMissRequest); 1300 assertEquals("v1", server.takeRequest().getHeader("If-None-Match")); 1301 1302 // Check the user response. It has the application's original request. 1303 cacheHit.assertCode(200) 1304 .assertBody("B") 1305 .assertHeader("Donut", "b") 1306 .assertRequestUrl(cacheStoreRequest.url()); 1307 1308 // Check the cache response. Even though it's a miss, we used the cache. 1309 cacheHit.cacheResponse() 1310 .assertCode(200) 1311 .assertHeader("Donut", "a") 1312 .assertHeader("ETag", "v1") 1313 .assertRequestUrl(cacheStoreRequest.url()); 1314 1315 // Check the network response. It has the network request, plus caching headers. 1316 cacheHit.networkResponse() 1317 .assertCode(200) 1318 .assertHeader("Donut", "b") 1319 .assertRequestHeader("If-None-Match", "v1") // If-None-Match in the validation request. 1320 .assertRequestUrl(cacheStoreRequest.url()); 1321 } 1322 conditionalCacheMiss_Async()1323 @Test public void conditionalCacheMiss_Async() throws Exception { 1324 server.enqueue(new MockResponse().setBody("A").addHeader("ETag: v1")); 1325 server.enqueue(new MockResponse().setBody("B")); 1326 1327 client.setCache(cache); 1328 1329 Request request1 = new Request.Builder() 1330 .url(server.url("/")) 1331 .build(); 1332 client.newCall(request1).enqueue(callback); 1333 callback.await(request1.httpUrl()).assertCode(200).assertBody("A"); 1334 assertNull(server.takeRequest().getHeader("If-None-Match")); 1335 1336 Request request2 = new Request.Builder() 1337 .url(server.url("/")) 1338 .build(); 1339 client.newCall(request2).enqueue(callback); 1340 callback.await(request2.httpUrl()).assertCode(200).assertBody("B"); 1341 assertEquals("v1", server.takeRequest().getHeader("If-None-Match")); 1342 } 1343 onlyIfCachedReturns504WhenNotCached()1344 @Test public void onlyIfCachedReturns504WhenNotCached() throws Exception { 1345 Request request = new Request.Builder() 1346 .url(server.url("/")) 1347 .header("Cache-Control", "only-if-cached") 1348 .build(); 1349 1350 executeSynchronously(request) 1351 .assertCode(504) 1352 .assertBody("") 1353 .assertNoNetworkResponse() 1354 .assertNoCacheResponse(); 1355 } 1356 redirect()1357 @Test public void redirect() throws Exception { 1358 server.enqueue(new MockResponse() 1359 .setResponseCode(301) 1360 .addHeader("Location: /b") 1361 .addHeader("Test", "Redirect from /a to /b") 1362 .setBody("/a has moved!")); 1363 server.enqueue(new MockResponse() 1364 .setResponseCode(302) 1365 .addHeader("Location: /c") 1366 .addHeader("Test", "Redirect from /b to /c") 1367 .setBody("/b has moved!")); 1368 server.enqueue(new MockResponse().setBody("C")); 1369 1370 executeSynchronously(new Request.Builder().url(server.url("/a")).build()) 1371 .assertCode(200) 1372 .assertBody("C") 1373 .priorResponse() 1374 .assertCode(302) 1375 .assertHeader("Test", "Redirect from /b to /c") 1376 .priorResponse() 1377 .assertCode(301) 1378 .assertHeader("Test", "Redirect from /a to /b"); 1379 1380 assertEquals(0, server.takeRequest().getSequenceNumber()); // New connection. 1381 assertEquals(1, server.takeRequest().getSequenceNumber()); // Connection reused. 1382 assertEquals(2, server.takeRequest().getSequenceNumber()); // Connection reused again! 1383 } 1384 postRedirectsToGet()1385 @Test public void postRedirectsToGet() throws Exception { 1386 server.enqueue(new MockResponse() 1387 .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1388 .addHeader("Location: /page2") 1389 .setBody("This page has moved!")); 1390 server.enqueue(new MockResponse().setBody("Page 2")); 1391 1392 Response response = client.newCall(new Request.Builder() 1393 .url(server.url("/page1")) 1394 .post(RequestBody.create(MediaType.parse("text/plain"), "Request Body")) 1395 .build()).execute(); 1396 assertEquals("Page 2", response.body().string()); 1397 1398 RecordedRequest page1 = server.takeRequest(); 1399 assertEquals("POST /page1 HTTP/1.1", page1.getRequestLine()); 1400 assertEquals("Request Body", page1.getBody().readUtf8()); 1401 1402 RecordedRequest page2 = server.takeRequest(); 1403 assertEquals("GET /page2 HTTP/1.1", page2.getRequestLine()); 1404 } 1405 propfindRedirectsToPropfind()1406 @Test public void propfindRedirectsToPropfind() throws Exception { 1407 server.enqueue(new MockResponse() 1408 .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1409 .addHeader("Location: /page2") 1410 .setBody("This page has moved!")); 1411 server.enqueue(new MockResponse().setBody("Page 2")); 1412 1413 Response response = client.newCall(new Request.Builder() 1414 .url(server.url("/page1")) 1415 .method("PROPFIND", RequestBody.create(MediaType.parse("text/plain"), "Request Body")) 1416 .build()).execute(); 1417 assertEquals("Page 2", response.body().string()); 1418 1419 RecordedRequest page1 = server.takeRequest(); 1420 assertEquals("PROPFIND /page1 HTTP/1.1", page1.getRequestLine()); 1421 assertEquals("Request Body", page1.getBody().readUtf8()); 1422 1423 RecordedRequest page2 = server.takeRequest(); 1424 assertEquals("PROPFIND /page2 HTTP/1.1", page2.getRequestLine()); 1425 } 1426 redirectsDoNotIncludeTooManyCookies()1427 @Test public void redirectsDoNotIncludeTooManyCookies() throws Exception { 1428 server2.enqueue(new MockResponse().setBody("Page 2")); 1429 server.enqueue(new MockResponse() 1430 .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1431 .addHeader("Location: " + server2.url("/"))); 1432 1433 CookieManager cookieManager = new CookieManager(null, ACCEPT_ORIGINAL_SERVER); 1434 HttpCookie cookie = new HttpCookie("c", "cookie"); 1435 cookie.setDomain(server.getCookieDomain()); 1436 cookie.setPath("/"); 1437 String portList = Integer.toString(server.getPort()); 1438 cookie.setPortlist(portList); 1439 cookieManager.getCookieStore().add(server.url("/").uri(), cookie); 1440 client.setCookieHandler(cookieManager); 1441 1442 Response response = client.newCall(new Request.Builder() 1443 .url(server.url("/page1")) 1444 .build()).execute(); 1445 assertEquals("Page 2", response.body().string()); 1446 1447 RecordedRequest request1 = server.takeRequest(); 1448 assertEquals("$Version=\"1\"; c=\"cookie\";$Path=\"/\";$Domain=\"" 1449 + server.getCookieDomain() 1450 + "\";$Port=\"" 1451 + portList 1452 + "\"", request1.getHeader("Cookie")); 1453 1454 RecordedRequest request2 = server2.takeRequest(); 1455 assertNull(request2.getHeader("Cookie")); 1456 } 1457 redirectsDoNotIncludeTooManyAuthHeaders()1458 @Test public void redirectsDoNotIncludeTooManyAuthHeaders() throws Exception { 1459 server2.enqueue(new MockResponse().setBody("Page 2")); 1460 server.enqueue(new MockResponse() 1461 .setResponseCode(401)); 1462 server.enqueue(new MockResponse() 1463 .setResponseCode(302) 1464 .addHeader("Location: " + server2.url("/b"))); 1465 1466 client.setAuthenticator(new RecordingOkAuthenticator(Credentials.basic("jesse", "secret"))); 1467 1468 Request request = new Request.Builder().url(server.url("/a")).build(); 1469 Response response = client.newCall(request).execute(); 1470 assertEquals("Page 2", response.body().string()); 1471 1472 RecordedRequest redirectRequest = server2.takeRequest(); 1473 assertNull(redirectRequest.getHeader("Authorization")); 1474 assertEquals("/b", redirectRequest.getPath()); 1475 } 1476 redirect_Async()1477 @Test public void redirect_Async() throws Exception { 1478 server.enqueue(new MockResponse() 1479 .setResponseCode(301) 1480 .addHeader("Location: /b") 1481 .addHeader("Test", "Redirect from /a to /b") 1482 .setBody("/a has moved!")); 1483 server.enqueue(new MockResponse() 1484 .setResponseCode(302) 1485 .addHeader("Location: /c") 1486 .addHeader("Test", "Redirect from /b to /c") 1487 .setBody("/b has moved!")); 1488 server.enqueue(new MockResponse().setBody("C")); 1489 1490 Request request = new Request.Builder().url(server.url("/a")).build(); 1491 client.newCall(request).enqueue(callback); 1492 1493 callback.await(server.url("/c")) 1494 .assertCode(200) 1495 .assertBody("C") 1496 .priorResponse() 1497 .assertCode(302) 1498 .assertHeader("Test", "Redirect from /b to /c") 1499 .priorResponse() 1500 .assertCode(301) 1501 .assertHeader("Test", "Redirect from /a to /b"); 1502 1503 assertEquals(0, server.takeRequest().getSequenceNumber()); // New connection. 1504 assertEquals(1, server.takeRequest().getSequenceNumber()); // Connection reused. 1505 assertEquals(2, server.takeRequest().getSequenceNumber()); // Connection reused again! 1506 } 1507 follow20Redirects()1508 @Test public void follow20Redirects() throws Exception { 1509 for (int i = 0; i < 20; i++) { 1510 server.enqueue(new MockResponse() 1511 .setResponseCode(301) 1512 .addHeader("Location: /" + (i + 1)) 1513 .setBody("Redirecting to /" + (i + 1))); 1514 } 1515 server.enqueue(new MockResponse().setBody("Success!")); 1516 1517 executeSynchronously(new Request.Builder().url(server.url("/0")).build()) 1518 .assertCode(200) 1519 .assertBody("Success!"); 1520 } 1521 follow20Redirects_Async()1522 @Test public void follow20Redirects_Async() throws Exception { 1523 for (int i = 0; i < 20; i++) { 1524 server.enqueue(new MockResponse() 1525 .setResponseCode(301) 1526 .addHeader("Location: /" + (i + 1)) 1527 .setBody("Redirecting to /" + (i + 1))); 1528 } 1529 server.enqueue(new MockResponse().setBody("Success!")); 1530 1531 Request request = new Request.Builder().url(server.url("/0")).build(); 1532 client.newCall(request).enqueue(callback); 1533 callback.await(server.url("/20")) 1534 .assertCode(200) 1535 .assertBody("Success!"); 1536 } 1537 doesNotFollow21Redirects()1538 @Test public void doesNotFollow21Redirects() throws Exception { 1539 for (int i = 0; i < 21; i++) { 1540 server.enqueue(new MockResponse() 1541 .setResponseCode(301) 1542 .addHeader("Location: /" + (i + 1)) 1543 .setBody("Redirecting to /" + (i + 1))); 1544 } 1545 1546 try { 1547 client.newCall(new Request.Builder().url(server.url("/0")).build()).execute(); 1548 fail(); 1549 } catch (IOException expected) { 1550 assertEquals("Too many follow-up requests: 21", expected.getMessage()); 1551 } 1552 } 1553 doesNotFollow21Redirects_Async()1554 @Test public void doesNotFollow21Redirects_Async() throws Exception { 1555 for (int i = 0; i < 21; i++) { 1556 server.enqueue(new MockResponse() 1557 .setResponseCode(301) 1558 .addHeader("Location: /" + (i + 1)) 1559 .setBody("Redirecting to /" + (i + 1))); 1560 } 1561 1562 Request request = new Request.Builder().url(server.url("/0")).build(); 1563 client.newCall(request).enqueue(callback); 1564 callback.await(server.url("/20")).assertFailure("Too many follow-up requests: 21"); 1565 } 1566 http204WithBodyDisallowed()1567 @Test public void http204WithBodyDisallowed() throws IOException { 1568 server.enqueue(new MockResponse() 1569 .setResponseCode(204) 1570 .setBody("I'm not even supposed to be here today.")); 1571 1572 try { 1573 executeSynchronously(new Request.Builder().url(server.url("/")).build()); 1574 fail(); 1575 } catch (ProtocolException e) { 1576 assertEquals("HTTP 204 had non-zero Content-Length: 39", e.getMessage()); 1577 } 1578 } 1579 http205WithBodyDisallowed()1580 @Test public void http205WithBodyDisallowed() throws IOException { 1581 server.enqueue(new MockResponse() 1582 .setResponseCode(205) 1583 .setBody("I'm not even supposed to be here today.")); 1584 1585 try { 1586 executeSynchronously(new Request.Builder().url(server.url("/")).build()); 1587 fail(); 1588 } catch (ProtocolException e) { 1589 assertEquals("HTTP 205 had non-zero Content-Length: 39", e.getMessage()); 1590 } 1591 } 1592 canceledBeforeExecute()1593 @Test public void canceledBeforeExecute() throws Exception { 1594 Call call = client.newCall(new Request.Builder().url(server.url("/a")).build()); 1595 call.cancel(); 1596 1597 try { 1598 call.execute(); 1599 fail(); 1600 } catch (IOException expected) { 1601 } 1602 assertEquals(0, server.getRequestCount()); 1603 } 1604 cancelDuringHttpConnect()1605 @Test public void cancelDuringHttpConnect() throws Exception { 1606 cancelDuringConnect("http"); 1607 } 1608 cancelDuringHttpsConnect()1609 @Test public void cancelDuringHttpsConnect() throws Exception { 1610 cancelDuringConnect("https"); 1611 } 1612 1613 /** Cancel a call that's waiting for connect to complete. */ cancelDuringConnect(String scheme)1614 private void cancelDuringConnect(String scheme) throws Exception { 1615 InetSocketAddress socketAddress = startNullServer(); 1616 1617 HttpUrl url = new HttpUrl.Builder() 1618 .scheme(scheme) 1619 .host(socketAddress.getHostName()) 1620 .port(socketAddress.getPort()) 1621 .build(); 1622 1623 long cancelDelayMillis = 300L; 1624 Call call = client.newCall(new Request.Builder().url(url).build()); 1625 cancelLater(call, cancelDelayMillis); 1626 1627 long startNanos = System.nanoTime(); 1628 try { 1629 call.execute(); 1630 fail(); 1631 } catch (IOException expected) { 1632 } 1633 long elapsedNanos = System.nanoTime() - startNanos; 1634 assertEquals(cancelDelayMillis, TimeUnit.NANOSECONDS.toMillis(elapsedNanos), 100f); 1635 } 1636 startNullServer()1637 private InetSocketAddress startNullServer() throws IOException { 1638 InetSocketAddress address = new InetSocketAddress(InetAddress.getByName("localhost"), 0); 1639 nullServer = ServerSocketFactory.getDefault().createServerSocket(); 1640 nullServer.bind(address); 1641 return new InetSocketAddress(address.getAddress(), nullServer.getLocalPort()); 1642 } 1643 1644 @Test 1645 @Ignore("TODO(b/333847678 - diagnose and fix flake") cancelTagImmediatelyAfterEnqueue()1646 public void cancelTagImmediatelyAfterEnqueue() throws Exception { 1647 server.enqueue(new MockResponse()); 1648 Call call = client.newCall(new Request.Builder() 1649 .url(server.url("/a")) 1650 .tag("request") 1651 .build()); 1652 call.enqueue(callback); 1653 client.cancel("request"); 1654 callback.await(server.url("/a")).assertFailure("Canceled"); 1655 } 1656 cancelBeforeBodyIsRead()1657 @Test public void cancelBeforeBodyIsRead() throws Exception { 1658 server.enqueue(new MockResponse().setBody("def").throttleBody(1, 750, TimeUnit.MILLISECONDS)); 1659 1660 final Call call = client.newCall(new Request.Builder().url(server.url("/a")).build()); 1661 ExecutorService executor = Executors.newSingleThreadExecutor(); 1662 Future<Response> result = executor.submit(new Callable<Response>() { 1663 @Override public Response call() throws Exception { 1664 return call.execute(); 1665 } 1666 }); 1667 1668 Thread.sleep(100); // wait for it to go in flight. 1669 1670 call.cancel(); 1671 try { 1672 result.get().body().bytes(); 1673 fail(); 1674 } catch (IOException expected) { 1675 } 1676 assertEquals(1, server.getRequestCount()); 1677 } 1678 cancelInFlightBeforeResponseReadThrowsIOE()1679 @Test public void cancelInFlightBeforeResponseReadThrowsIOE() throws Exception { 1680 final CountDownLatch cancelSignal = new CountDownLatch(1); 1681 1682 server.setDispatcher(new Dispatcher() { 1683 @Override public MockResponse dispatch(RecordedRequest request) { 1684 client.cancel("request"); 1685 try { 1686 cancelSignal.await(10L, TimeUnit.SECONDS); 1687 } catch (InterruptedException e) { 1688 // Do nothing 1689 } 1690 return new MockResponse().setBody("A"); 1691 } 1692 }); 1693 1694 Request request = new Request.Builder().url(server.url("/a")).tag("request").build(); 1695 try { 1696 client.newCall(request).execute(); 1697 fail(); 1698 } catch (IOException expected) { 1699 cancelSignal.countDown(); 1700 } 1701 } 1702 cancelInFlightBeforeResponseReadThrowsIOE_HTTPS()1703 @Test public void cancelInFlightBeforeResponseReadThrowsIOE_HTTPS() throws Exception { 1704 enableTls(); 1705 cancelInFlightBeforeResponseReadThrowsIOE(); 1706 } 1707 cancelInFlightBeforeResponseReadThrowsIOE_HTTP_2()1708 @Test public void cancelInFlightBeforeResponseReadThrowsIOE_HTTP_2() throws Exception { 1709 enableProtocol(Protocol.HTTP_2); 1710 cancelInFlightBeforeResponseReadThrowsIOE(); 1711 } 1712 cancelInFlightBeforeResponseReadThrowsIOE_SPDY_3()1713 @Test public void cancelInFlightBeforeResponseReadThrowsIOE_SPDY_3() throws Exception { 1714 enableProtocol(Protocol.SPDY_3); 1715 cancelInFlightBeforeResponseReadThrowsIOE(); 1716 } 1717 1718 /** 1719 * This test puts a request in front of one that is to be canceled, so that it is canceled before 1720 * I/O takes place. 1721 */ canceledBeforeIOSignalsOnFailure()1722 @Test public void canceledBeforeIOSignalsOnFailure() throws Exception { 1723 client.getDispatcher().setMaxRequests(1); // Force requests to be executed serially. 1724 server.setDispatcher(new Dispatcher() { 1725 char nextResponse = 'A'; 1726 1727 @Override public MockResponse dispatch(RecordedRequest request) { 1728 client.cancel("request B"); 1729 return new MockResponse().setBody(Character.toString(nextResponse++)); 1730 } 1731 }); 1732 1733 Request requestA = new Request.Builder().url(server.url("/a")).tag("request A").build(); 1734 client.newCall(requestA).enqueue(callback); 1735 assertEquals("/a", server.takeRequest().getPath()); 1736 1737 Request requestB = new Request.Builder().url(server.url("/b")).tag("request B").build(); 1738 client.newCall(requestB).enqueue(callback); 1739 1740 callback.await(requestA.httpUrl()).assertBody("A"); 1741 // At this point we know the callback is ready, and that it will receive a cancel failure. 1742 callback.await(requestB.httpUrl()).assertFailure("Canceled"); 1743 } 1744 canceledBeforeIOSignalsOnFailure_HTTPS()1745 @Test public void canceledBeforeIOSignalsOnFailure_HTTPS() throws Exception { 1746 enableTls(); 1747 canceledBeforeIOSignalsOnFailure(); 1748 } 1749 canceledBeforeIOSignalsOnFailure_HTTP_2()1750 @Test public void canceledBeforeIOSignalsOnFailure_HTTP_2() throws Exception { 1751 enableProtocol(Protocol.HTTP_2); 1752 canceledBeforeIOSignalsOnFailure(); 1753 } 1754 canceledBeforeIOSignalsOnFailure_SPDY_3()1755 @Test public void canceledBeforeIOSignalsOnFailure_SPDY_3() throws Exception { 1756 enableProtocol(Protocol.SPDY_3); 1757 canceledBeforeIOSignalsOnFailure(); 1758 } 1759 canceledBeforeResponseReadSignalsOnFailure()1760 @Test public void canceledBeforeResponseReadSignalsOnFailure() throws Exception { 1761 Request requestA = new Request.Builder().url(server.url("/a")).tag("request A").build(); 1762 final Call call = client.newCall(requestA); 1763 server.setDispatcher(new Dispatcher() { 1764 @Override public MockResponse dispatch(RecordedRequest request) { 1765 call.cancel(); 1766 return new MockResponse().setBody("A"); 1767 } 1768 }); 1769 1770 call.enqueue(callback); 1771 assertEquals("/a", server.takeRequest().getPath()); 1772 1773 callback.await(requestA.httpUrl()).assertFailure("Canceled", "stream was reset: CANCEL", 1774 "Socket closed"); 1775 } 1776 canceledBeforeResponseReadSignalsOnFailure_HTTPS()1777 @Test public void canceledBeforeResponseReadSignalsOnFailure_HTTPS() throws Exception { 1778 enableTls(); 1779 canceledBeforeResponseReadSignalsOnFailure(); 1780 } 1781 canceledBeforeResponseReadSignalsOnFailure_HTTP_2()1782 @Test public void canceledBeforeResponseReadSignalsOnFailure_HTTP_2() throws Exception { 1783 enableProtocol(Protocol.HTTP_2); 1784 canceledBeforeResponseReadSignalsOnFailure(); 1785 } 1786 canceledBeforeResponseReadSignalsOnFailure_SPDY_3()1787 @Test public void canceledBeforeResponseReadSignalsOnFailure_SPDY_3() throws Exception { 1788 enableProtocol(Protocol.SPDY_3); 1789 canceledBeforeResponseReadSignalsOnFailure(); 1790 } 1791 1792 /** 1793 * There's a race condition where the cancel may apply after the stream has already been 1794 * processed. 1795 */ canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce()1796 @Test public void canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce() throws Exception { 1797 server.enqueue(new MockResponse().setBody("A")); 1798 1799 final CountDownLatch latch = new CountDownLatch(1); 1800 final AtomicReference<String> bodyRef = new AtomicReference<>(); 1801 final AtomicBoolean failureRef = new AtomicBoolean(); 1802 1803 Request request = new Request.Builder().url(server.url("/a")).tag("request A").build(); 1804 final Call call = client.newCall(request); 1805 call.enqueue(new Callback() { 1806 @Override public void onFailure(Request request, IOException e) { 1807 failureRef.set(true); 1808 latch.countDown(); 1809 } 1810 1811 @Override public void onResponse(Response response) throws IOException { 1812 call.cancel(); 1813 try { 1814 bodyRef.set(response.body().string()); 1815 } catch (IOException e) { // It is ok if this broke the stream. 1816 bodyRef.set("A"); 1817 throw e; // We expect to not loop into onFailure in this case. 1818 } finally { 1819 latch.countDown(); 1820 } 1821 } 1822 }); 1823 1824 latch.await(); 1825 assertEquals("A", bodyRef.get()); 1826 assertFalse(failureRef.get()); 1827 } 1828 canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce_HTTPS()1829 @Test public void canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce_HTTPS() 1830 throws Exception { 1831 enableTls(); 1832 canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce(); 1833 } 1834 canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce_HTTP_2()1835 @Test public void canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce_HTTP_2() 1836 throws Exception { 1837 enableProtocol(Protocol.HTTP_2); 1838 canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce(); 1839 } 1840 canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce_SPDY_3()1841 @Test public void canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce_SPDY_3() 1842 throws Exception { 1843 enableProtocol(Protocol.SPDY_3); 1844 canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce(); 1845 } 1846 cancelWithInterceptor()1847 @Test public void cancelWithInterceptor() throws Exception { 1848 client.interceptors().add(new Interceptor() { 1849 @Override public Response intercept(Chain chain) throws IOException { 1850 chain.proceed(chain.request()); 1851 throw new AssertionError(); // We expect an exception. 1852 } 1853 }); 1854 1855 Call call = client.newCall(new Request.Builder().url(server.url("/a")).build()); 1856 call.cancel(); 1857 1858 try { 1859 call.execute(); 1860 fail(); 1861 } catch (IOException expected) { 1862 } 1863 assertEquals(0, server.getRequestCount()); 1864 } 1865 gzip()1866 @Test public void gzip() throws Exception { 1867 Buffer gzippedBody = gzip("abcabcabc"); 1868 String bodySize = Long.toString(gzippedBody.size()); 1869 1870 server.enqueue(new MockResponse() 1871 .setBody(gzippedBody) 1872 .addHeader("Content-Encoding: gzip")); 1873 1874 Request request = new Request.Builder() 1875 .url(server.url("/")) 1876 .build(); 1877 1878 // Confirm that the user request doesn't have Accept-Encoding, and the user 1879 // response doesn't have a Content-Encoding or Content-Length. 1880 RecordedResponse userResponse = executeSynchronously(request); 1881 userResponse.assertCode(200) 1882 .assertRequestHeader("Accept-Encoding") 1883 .assertHeader("Content-Encoding") 1884 .assertHeader("Content-Length") 1885 .assertBody("abcabcabc"); 1886 1887 // But the network request doesn't lie. OkHttp used gzip for this call. 1888 userResponse.networkResponse() 1889 .assertHeader("Content-Encoding", "gzip") 1890 .assertHeader("Content-Length", bodySize) 1891 .assertRequestHeader("Accept-Encoding", "gzip"); 1892 } 1893 1894 /** https://github.com/square/okhttp/issues/1927 */ gzipResponseAfterAuthenticationChallenge()1895 @Test public void gzipResponseAfterAuthenticationChallenge() throws Exception { 1896 server.enqueue(new MockResponse() 1897 .setResponseCode(401)); 1898 server.enqueue(new MockResponse() 1899 .setBody(gzip("abcabcabc")) 1900 .addHeader("Content-Encoding: gzip")); 1901 client.setAuthenticator(new RecordingOkAuthenticator("password")); 1902 1903 Request request = new Request.Builder() 1904 .url(server.url("/")) 1905 .build(); 1906 executeSynchronously(request) 1907 .assertBody("abcabcabc"); 1908 } 1909 asyncResponseCanBeConsumedLater()1910 @Test public void asyncResponseCanBeConsumedLater() throws Exception { 1911 server.enqueue(new MockResponse().setBody("abc")); 1912 server.enqueue(new MockResponse().setBody("def")); 1913 1914 Request request = new Request.Builder() 1915 .url(server.url("/")) 1916 .header("User-Agent", "SyncApiTest") 1917 .build(); 1918 1919 final BlockingQueue<Response> responseRef = new SynchronousQueue<>(); 1920 client.newCall(request).enqueue(new Callback() { 1921 @Override public void onFailure(Request request, IOException e) { 1922 throw new AssertionError(); 1923 } 1924 1925 @Override public void onResponse(Response response) throws IOException { 1926 try { 1927 responseRef.put(response); 1928 } catch (InterruptedException e) { 1929 throw new AssertionError(); 1930 } 1931 } 1932 }); 1933 1934 Response response = responseRef.take(); 1935 assertEquals(200, response.code()); 1936 assertEquals("abc", response.body().string()); 1937 1938 // Make another request just to confirm that that connection can be reused... 1939 executeSynchronously(new Request.Builder().url(server.url("/")).build()).assertBody("def"); 1940 assertEquals(0, server.takeRequest().getSequenceNumber()); // New connection. 1941 assertEquals(1, server.takeRequest().getSequenceNumber()); // Connection reused. 1942 1943 // ... even before we close the response body! 1944 response.body().close(); 1945 } 1946 userAgentIsIncludedByDefault()1947 @Test public void userAgentIsIncludedByDefault() throws Exception { 1948 server.enqueue(new MockResponse()); 1949 1950 executeSynchronously(new Request.Builder().url(server.url("/")).build()); 1951 1952 RecordedRequest recordedRequest = server.takeRequest(); 1953 assertTrue(recordedRequest.getHeader("User-Agent") 1954 .matches(Version.userAgent())); 1955 } 1956 setFollowRedirectsFalse()1957 @Test public void setFollowRedirectsFalse() throws Exception { 1958 server.enqueue(new MockResponse() 1959 .setResponseCode(302) 1960 .addHeader("Location: /b") 1961 .setBody("A")); 1962 server.enqueue(new MockResponse().setBody("B")); 1963 1964 client.setFollowRedirects(false); 1965 RecordedResponse recordedResponse = executeSynchronously( 1966 new Request.Builder().url(server.url("/a")).build()); 1967 1968 recordedResponse 1969 .assertBody("A") 1970 .assertCode(302); 1971 } 1972 expect100ContinueNonEmptyRequestBody()1973 @Test public void expect100ContinueNonEmptyRequestBody() throws Exception { 1974 server.enqueue(new MockResponse()); 1975 1976 Request request = new Request.Builder() 1977 .url(server.url("/")) 1978 .header("Expect", "100-continue") 1979 .post(RequestBody.create(MediaType.parse("text/plain"), "abc")) 1980 .build(); 1981 1982 executeSynchronously(request) 1983 .assertCode(200) 1984 .assertSuccessful(); 1985 1986 assertEquals("abc", server.takeRequest().getBody().readUtf8()); 1987 } 1988 expect100ContinueEmptyRequestBody()1989 @Test public void expect100ContinueEmptyRequestBody() throws Exception { 1990 server.enqueue(new MockResponse()); 1991 1992 Request request = new Request.Builder() 1993 .url(server.url("/")) 1994 .header("Expect", "100-continue") 1995 .post(RequestBody.create(MediaType.parse("text/plain"), "")) 1996 .build(); 1997 1998 executeSynchronously(request) 1999 .assertCode(200) 2000 .assertSuccessful(); 2001 } 2002 2003 /** We forbid non-ASCII characters in outgoing request headers, but accept UTF-8. */ responseHeaderParsingIsLenient()2004 @Test public void responseHeaderParsingIsLenient() throws Exception { 2005 Headers headers = new Headers.Builder() 2006 .add("Content-Length", "0") 2007 .addLenient("a\tb: c\u007fd") 2008 .addLenient(": ef") 2009 .addLenient("\ud83c\udf69: \u2615\ufe0f") 2010 .build(); 2011 server.enqueue(new MockResponse().setHeaders(headers)); 2012 2013 Request request = new Request.Builder() 2014 .url(server.url("/")) 2015 .build(); 2016 2017 executeSynchronously(request) 2018 .assertHeader("a\tb", "c\u007fd") 2019 .assertHeader("\ud83c\udf69", "\u2615\ufe0f") 2020 .assertHeader("", "ef"); 2021 } 2022 customDns()2023 @Test public void customDns() throws Exception { 2024 // Configure a DNS that returns our MockWebServer for every hostname. 2025 FakeDns dns = new FakeDns(); 2026 dns.addresses(Dns.SYSTEM.lookup(server.url("/").host())); 2027 client.setDns(dns); 2028 2029 server.enqueue(new MockResponse()); 2030 Request request = new Request.Builder() 2031 .url(server.url("/").newBuilder().host("android.com").build()) 2032 .build(); 2033 executeSynchronously(request).assertCode(200); 2034 2035 dns.assertRequests("android.com"); 2036 } 2037 2038 /** We had a bug where failed HTTP/2 calls could break the entire connection. */ failingCallsDoNotInterfereWithConnection()2039 @Test public void failingCallsDoNotInterfereWithConnection() throws Exception { 2040 enableProtocol(Protocol.HTTP_2); 2041 2042 server.enqueue(new MockResponse().setBody("Response 1")); 2043 server.enqueue(new MockResponse().setBody("Response 2")); 2044 2045 RequestBody requestBody = new RequestBody() { 2046 @Override public MediaType contentType() { 2047 return null; 2048 } 2049 2050 @Override public void writeTo(BufferedSink sink) throws IOException { 2051 sink.writeUtf8("abc"); 2052 sink.flush(); 2053 2054 makeFailingCall(); 2055 2056 sink.writeUtf8("def"); 2057 sink.flush(); 2058 } 2059 }; 2060 Call call = client.newCall(new Request.Builder() 2061 .url(server.url("/")) 2062 .post(requestBody) 2063 .build()); 2064 assertEquals("Response 1", call.execute().body().string()); 2065 } 2066 2067 /** Test which headers are sent unencrypted to the HTTP proxy. */ proxyConnectOmitsApplicationHeaders()2068 @Test public void proxyConnectOmitsApplicationHeaders() throws Exception { 2069 server.useHttps(sslContext.getSocketFactory(), true); 2070 server.enqueue(new MockResponse() 2071 .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) 2072 .clearHeaders()); 2073 server.enqueue(new MockResponse() 2074 .setBody("encrypted response from the origin server")); 2075 2076 client.setSslSocketFactory(sslContext.getSocketFactory()); 2077 client.setProxy(server.toProxyAddress()); 2078 RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier(); 2079 client.setHostnameVerifier(hostnameVerifier); 2080 2081 Request request = new Request.Builder() 2082 .url("https://android.com/foo") 2083 .header("Private", "Secret") 2084 .header("User-Agent", "App 1.0") 2085 .build(); 2086 Response response = client.newCall(request).execute(); 2087 assertEquals("encrypted response from the origin server", response.body().string()); 2088 2089 RecordedRequest connect = server.takeRequest(); 2090 assertNull(connect.getHeader("Private")); 2091 assertEquals(Version.userAgent(), connect.getHeader("User-Agent")); 2092 assertEquals("Keep-Alive", connect.getHeader("Proxy-Connection")); 2093 assertEquals("android.com:443", connect.getHeader("Host")); 2094 2095 RecordedRequest get = server.takeRequest(); 2096 assertEquals("Secret", get.getHeader("Private")); 2097 assertEquals("App 1.0", get.getHeader("User-Agent")); 2098 2099 assertEquals(Arrays.asList("verify android.com"), hostnameVerifier.calls); 2100 } 2101 2102 /** Respond to a proxy authorization challenge. */ proxyAuthenticateOnConnect()2103 @Test public void proxyAuthenticateOnConnect() throws Exception { 2104 server.useHttps(sslContext.getSocketFactory(), true); 2105 server.enqueue(new MockResponse() 2106 .setResponseCode(407) 2107 .addHeader("Proxy-Authenticate: Basic realm=\"localhost\"")); 2108 server.enqueue(new MockResponse() 2109 .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) 2110 .clearHeaders()); 2111 server.enqueue(new MockResponse() 2112 .setBody("response body")); 2113 2114 client.setSslSocketFactory(sslContext.getSocketFactory()); 2115 client.setProxy(server.toProxyAddress()); 2116 client.setAuthenticator(new RecordingOkAuthenticator("password")); 2117 client.setHostnameVerifier(new RecordingHostnameVerifier()); 2118 2119 Request request = new Request.Builder() 2120 .url("https://android.com/foo") 2121 .build(); 2122 Response response = client.newCall(request).execute(); 2123 assertEquals("response body", response.body().string()); 2124 2125 RecordedRequest connect1 = server.takeRequest(); 2126 assertEquals("CONNECT android.com:443 HTTP/1.1", connect1.getRequestLine()); 2127 assertNull(connect1.getHeader("Proxy-Authorization")); 2128 2129 RecordedRequest connect2 = server.takeRequest(); 2130 assertEquals("CONNECT android.com:443 HTTP/1.1", connect2.getRequestLine()); 2131 assertEquals("password", connect2.getHeader("Proxy-Authorization")); 2132 2133 RecordedRequest get = server.takeRequest(); 2134 assertEquals("GET /foo HTTP/1.1", get.getRequestLine()); 2135 assertNull(get.getHeader("Proxy-Authorization")); 2136 } 2137 2138 /** 2139 * Confirm that we don't send the Proxy-Authorization header from the request to the proxy server. 2140 * We used to have that behavior but it is problematic because unrelated requests end up sharing 2141 * credentials. Worse, that approach leaks proxy credentials to the origin server. 2142 */ noProactiveProxyAuthorization()2143 @Test public void noProactiveProxyAuthorization() throws Exception { 2144 server.useHttps(sslContext.getSocketFactory(), true); 2145 server.enqueue(new MockResponse() 2146 .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) 2147 .clearHeaders()); 2148 server.enqueue(new MockResponse() 2149 .setBody("response body")); 2150 2151 client.setSslSocketFactory(sslContext.getSocketFactory()); 2152 client.setProxy(server.toProxyAddress()); 2153 client.setHostnameVerifier(new RecordingHostnameVerifier()); 2154 2155 Request request = new Request.Builder() 2156 .url("https://android.com/foo") 2157 .header("Proxy-Authorization", "password") 2158 .build(); 2159 Response response = client.newCall(request).execute(); 2160 assertEquals("response body", response.body().string()); 2161 2162 RecordedRequest connect = server.takeRequest(); 2163 assertNull(connect.getHeader("Proxy-Authorization")); 2164 2165 RecordedRequest get = server.takeRequest(); 2166 assertEquals("password", get.getHeader("Proxy-Authorization")); 2167 } 2168 2169 /** https://github.com/square/okhttp/issues/2344 */ ipv6HostHasSquareBraces()2170 @Test public void ipv6HostHasSquareBraces() throws Exception { 2171 // Use a proxy to fake IPv6 connectivity, even if localhost doesn't have IPv6. 2172 server.useHttps(sslContext.getSocketFactory(), true); 2173 server.setProtocols(Collections.singletonList(Protocol.HTTP_1_1)); 2174 server.enqueue(new MockResponse() 2175 .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) 2176 .clearHeaders()); 2177 server.enqueue(new MockResponse() 2178 .setBody("response body")); 2179 2180 client 2181 .setSslSocketFactory(sslContext.getSocketFactory()) 2182 .setHostnameVerifier(new RecordingHostnameVerifier()) 2183 .setProxy(server.toProxyAddress()); 2184 2185 Request request = new Request.Builder() 2186 .url("https://[::1]/") 2187 .build(); 2188 Response response = client.newCall(request).execute(); 2189 assertEquals("response body", response.body().string()); 2190 2191 RecordedRequest connect = server.takeRequest(); 2192 assertEquals("CONNECT [::1]:443 HTTP/1.1", connect.getRequestLine()); 2193 assertEquals("[::1]:443", connect.getHeader("Host")); 2194 2195 RecordedRequest get = server.takeRequest(); 2196 assertEquals("GET / HTTP/1.1", get.getRequestLine()); 2197 assertEquals("[::1]", get.getHeader("Host")); 2198 } 2199 makeFailingCall()2200 private void makeFailingCall() { 2201 RequestBody requestBody = new RequestBody() { 2202 @Override public MediaType contentType() { 2203 return null; 2204 } 2205 2206 @Override public long contentLength() throws IOException { 2207 return 1; 2208 } 2209 2210 @Override public void writeTo(BufferedSink sink) throws IOException { 2211 throw new IOException("write body fail!"); 2212 } 2213 }; 2214 Call call = client.newCall(new Request.Builder() 2215 .url(server.url("/")) 2216 .post(requestBody) 2217 .build()); 2218 try { 2219 call.execute(); 2220 fail(); 2221 } catch (IOException expected) { 2222 assertEquals("write body fail!", expected.getMessage()); 2223 } 2224 } 2225 executeSynchronously(Request request)2226 private RecordedResponse executeSynchronously(Request request) throws IOException { 2227 Response response = client.newCall(request).execute(); 2228 return new RecordedResponse(request, response, null, response.body().string(), null); 2229 } 2230 2231 /** 2232 * Tests that use this will fail unless boot classpath is set. Ex. {@code 2233 * -Xbootclasspath/p:/tmp/alpn-boot-8.0.0.v20140317} 2234 */ enableProtocol(Protocol protocol)2235 private void enableProtocol(Protocol protocol) { 2236 enableTls(); 2237 client.setProtocols(Arrays.asList(protocol, Protocol.HTTP_1_1)); 2238 server.setProtocols(client.getProtocols()); 2239 } 2240 enableTls()2241 private void enableTls() { 2242 client.setSslSocketFactory(sslContext.getSocketFactory()); 2243 client.setHostnameVerifier(new RecordingHostnameVerifier()); 2244 server.useHttps(sslContext.getSocketFactory(), false); 2245 } 2246 gzip(String data)2247 private Buffer gzip(String data) throws IOException { 2248 Buffer result = new Buffer(); 2249 BufferedSink sink = Okio.buffer(new GzipSink(result)); 2250 sink.writeUtf8(data); 2251 sink.close(); 2252 return result; 2253 } 2254 cancelLater(final Call call, final long delay)2255 private void cancelLater(final Call call, final long delay) { 2256 new Thread("canceler") { 2257 @Override public void run() { 2258 try { 2259 Thread.sleep(delay); 2260 } catch (InterruptedException e) { 2261 throw new AssertionError(); 2262 } 2263 call.cancel(); 2264 } 2265 }.start(); 2266 } 2267 2268 private static class RecordingSSLSocketFactory extends DelegatingSSLSocketFactory { 2269 2270 private List<SSLSocket> socketsCreated = new ArrayList<>(); 2271 RecordingSSLSocketFactory(SSLSocketFactory delegate)2272 public RecordingSSLSocketFactory(SSLSocketFactory delegate) { 2273 super(delegate); 2274 } 2275 2276 @Override configureSocket(SSLSocket sslSocket)2277 protected SSLSocket configureSocket(SSLSocket sslSocket) throws IOException { 2278 socketsCreated.add(sslSocket); 2279 return sslSocket; 2280 } 2281 getSocketsCreated()2282 public List<SSLSocket> getSocketsCreated() { 2283 return socketsCreated; 2284 } 2285 } 2286 2287 /** 2288 * Used during tests that involve TLS connection fallback attempts. OkHttp includes the 2289 * TLS_FALLBACK_SCSV cipher on fallback connections. See 2290 * {@link com.squareup.okhttp.FallbackTestClientSocketFactory} for details. 2291 */ suppressTlsFallbackScsv(OkHttpClient client)2292 private void suppressTlsFallbackScsv(OkHttpClient client) { 2293 FallbackTestClientSocketFactory clientSocketFactory = 2294 new FallbackTestClientSocketFactory(sslContext.getSocketFactory()); 2295 client.setSslSocketFactory(clientSocketFactory); 2296 } 2297 } 2298