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