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 final CountDownLatch cancelSignal = new CountDownLatch(1); 1654 1655 server.setDispatcher(new Dispatcher() { 1656 @Override public MockResponse dispatch(RecordedRequest request) { 1657 client.cancel("request"); 1658 try { 1659 cancelSignal.await(10L, TimeUnit.SECONDS); 1660 } catch (InterruptedException e) { 1661 // Do nothing 1662 } 1663 return new MockResponse().setBody("A"); 1664 } 1665 }); 1666 1667 Request request = new Request.Builder().url(server.url("/a")).tag("request").build(); 1668 try { 1669 client.newCall(request).execute(); 1670 fail(); 1671 } catch (IOException expected) { 1672 cancelSignal.countDown(); 1673 } 1674 } 1675 cancelInFlightBeforeResponseReadThrowsIOE_HTTPS()1676 @Test public void cancelInFlightBeforeResponseReadThrowsIOE_HTTPS() throws Exception { 1677 enableTls(); 1678 cancelInFlightBeforeResponseReadThrowsIOE(); 1679 } 1680 cancelInFlightBeforeResponseReadThrowsIOE_HTTP_2()1681 @Test public void cancelInFlightBeforeResponseReadThrowsIOE_HTTP_2() throws Exception { 1682 enableProtocol(Protocol.HTTP_2); 1683 cancelInFlightBeforeResponseReadThrowsIOE(); 1684 } 1685 cancelInFlightBeforeResponseReadThrowsIOE_SPDY_3()1686 @Test public void cancelInFlightBeforeResponseReadThrowsIOE_SPDY_3() throws Exception { 1687 enableProtocol(Protocol.SPDY_3); 1688 cancelInFlightBeforeResponseReadThrowsIOE(); 1689 } 1690 1691 /** 1692 * This test puts a request in front of one that is to be canceled, so that it is canceled before 1693 * I/O takes place. 1694 */ canceledBeforeIOSignalsOnFailure()1695 @Test public void canceledBeforeIOSignalsOnFailure() throws Exception { 1696 client.getDispatcher().setMaxRequests(1); // Force requests to be executed serially. 1697 server.setDispatcher(new Dispatcher() { 1698 char nextResponse = 'A'; 1699 1700 @Override public MockResponse dispatch(RecordedRequest request) { 1701 client.cancel("request B"); 1702 return new MockResponse().setBody(Character.toString(nextResponse++)); 1703 } 1704 }); 1705 1706 Request requestA = new Request.Builder().url(server.url("/a")).tag("request A").build(); 1707 client.newCall(requestA).enqueue(callback); 1708 assertEquals("/a", server.takeRequest().getPath()); 1709 1710 Request requestB = new Request.Builder().url(server.url("/b")).tag("request B").build(); 1711 client.newCall(requestB).enqueue(callback); 1712 1713 callback.await(requestA.httpUrl()).assertBody("A"); 1714 // At this point we know the callback is ready, and that it will receive a cancel failure. 1715 callback.await(requestB.httpUrl()).assertFailure("Canceled"); 1716 } 1717 canceledBeforeIOSignalsOnFailure_HTTPS()1718 @Test public void canceledBeforeIOSignalsOnFailure_HTTPS() throws Exception { 1719 enableTls(); 1720 canceledBeforeIOSignalsOnFailure(); 1721 } 1722 canceledBeforeIOSignalsOnFailure_HTTP_2()1723 @Test public void canceledBeforeIOSignalsOnFailure_HTTP_2() throws Exception { 1724 enableProtocol(Protocol.HTTP_2); 1725 canceledBeforeIOSignalsOnFailure(); 1726 } 1727 canceledBeforeIOSignalsOnFailure_SPDY_3()1728 @Test public void canceledBeforeIOSignalsOnFailure_SPDY_3() throws Exception { 1729 enableProtocol(Protocol.SPDY_3); 1730 canceledBeforeIOSignalsOnFailure(); 1731 } 1732 canceledBeforeResponseReadSignalsOnFailure()1733 @Test public void canceledBeforeResponseReadSignalsOnFailure() throws Exception { 1734 Request requestA = new Request.Builder().url(server.url("/a")).tag("request A").build(); 1735 final Call call = client.newCall(requestA); 1736 server.setDispatcher(new Dispatcher() { 1737 @Override public MockResponse dispatch(RecordedRequest request) { 1738 call.cancel(); 1739 return new MockResponse().setBody("A"); 1740 } 1741 }); 1742 1743 call.enqueue(callback); 1744 assertEquals("/a", server.takeRequest().getPath()); 1745 1746 callback.await(requestA.httpUrl()).assertFailure("Canceled", "stream was reset: CANCEL", 1747 "Socket closed"); 1748 } 1749 canceledBeforeResponseReadSignalsOnFailure_HTTPS()1750 @Test public void canceledBeforeResponseReadSignalsOnFailure_HTTPS() throws Exception { 1751 enableTls(); 1752 canceledBeforeResponseReadSignalsOnFailure(); 1753 } 1754 canceledBeforeResponseReadSignalsOnFailure_HTTP_2()1755 @Test public void canceledBeforeResponseReadSignalsOnFailure_HTTP_2() throws Exception { 1756 enableProtocol(Protocol.HTTP_2); 1757 canceledBeforeResponseReadSignalsOnFailure(); 1758 } 1759 canceledBeforeResponseReadSignalsOnFailure_SPDY_3()1760 @Test public void canceledBeforeResponseReadSignalsOnFailure_SPDY_3() throws Exception { 1761 enableProtocol(Protocol.SPDY_3); 1762 canceledBeforeResponseReadSignalsOnFailure(); 1763 } 1764 1765 /** 1766 * There's a race condition where the cancel may apply after the stream has already been 1767 * processed. 1768 */ canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce()1769 @Test public void canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce() throws Exception { 1770 server.enqueue(new MockResponse().setBody("A")); 1771 1772 final CountDownLatch latch = new CountDownLatch(1); 1773 final AtomicReference<String> bodyRef = new AtomicReference<>(); 1774 final AtomicBoolean failureRef = new AtomicBoolean(); 1775 1776 Request request = new Request.Builder().url(server.url("/a")).tag("request A").build(); 1777 final Call call = client.newCall(request); 1778 call.enqueue(new Callback() { 1779 @Override public void onFailure(Request request, IOException e) { 1780 failureRef.set(true); 1781 latch.countDown(); 1782 } 1783 1784 @Override public void onResponse(Response response) throws IOException { 1785 call.cancel(); 1786 try { 1787 bodyRef.set(response.body().string()); 1788 } catch (IOException e) { // It is ok if this broke the stream. 1789 bodyRef.set("A"); 1790 throw e; // We expect to not loop into onFailure in this case. 1791 } finally { 1792 latch.countDown(); 1793 } 1794 } 1795 }); 1796 1797 latch.await(); 1798 assertEquals("A", bodyRef.get()); 1799 assertFalse(failureRef.get()); 1800 } 1801 canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce_HTTPS()1802 @Test public void canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce_HTTPS() 1803 throws Exception { 1804 enableTls(); 1805 canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce(); 1806 } 1807 canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce_HTTP_2()1808 @Test public void canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce_HTTP_2() 1809 throws Exception { 1810 enableProtocol(Protocol.HTTP_2); 1811 canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce(); 1812 } 1813 canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce_SPDY_3()1814 @Test public void canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce_SPDY_3() 1815 throws Exception { 1816 enableProtocol(Protocol.SPDY_3); 1817 canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce(); 1818 } 1819 cancelWithInterceptor()1820 @Test public void cancelWithInterceptor() throws Exception { 1821 client.interceptors().add(new Interceptor() { 1822 @Override public Response intercept(Chain chain) throws IOException { 1823 chain.proceed(chain.request()); 1824 throw new AssertionError(); // We expect an exception. 1825 } 1826 }); 1827 1828 Call call = client.newCall(new Request.Builder().url(server.url("/a")).build()); 1829 call.cancel(); 1830 1831 try { 1832 call.execute(); 1833 fail(); 1834 } catch (IOException expected) { 1835 } 1836 assertEquals(0, server.getRequestCount()); 1837 } 1838 gzip()1839 @Test public void gzip() throws Exception { 1840 Buffer gzippedBody = gzip("abcabcabc"); 1841 String bodySize = Long.toString(gzippedBody.size()); 1842 1843 server.enqueue(new MockResponse() 1844 .setBody(gzippedBody) 1845 .addHeader("Content-Encoding: gzip")); 1846 1847 Request request = new Request.Builder() 1848 .url(server.url("/")) 1849 .build(); 1850 1851 // Confirm that the user request doesn't have Accept-Encoding, and the user 1852 // response doesn't have a Content-Encoding or Content-Length. 1853 RecordedResponse userResponse = executeSynchronously(request); 1854 userResponse.assertCode(200) 1855 .assertRequestHeader("Accept-Encoding") 1856 .assertHeader("Content-Encoding") 1857 .assertHeader("Content-Length") 1858 .assertBody("abcabcabc"); 1859 1860 // But the network request doesn't lie. OkHttp used gzip for this call. 1861 userResponse.networkResponse() 1862 .assertHeader("Content-Encoding", "gzip") 1863 .assertHeader("Content-Length", bodySize) 1864 .assertRequestHeader("Accept-Encoding", "gzip"); 1865 } 1866 1867 /** https://github.com/square/okhttp/issues/1927 */ gzipResponseAfterAuthenticationChallenge()1868 @Test public void gzipResponseAfterAuthenticationChallenge() throws Exception { 1869 server.enqueue(new MockResponse() 1870 .setResponseCode(401)); 1871 server.enqueue(new MockResponse() 1872 .setBody(gzip("abcabcabc")) 1873 .addHeader("Content-Encoding: gzip")); 1874 client.setAuthenticator(new RecordingOkAuthenticator("password")); 1875 1876 Request request = new Request.Builder() 1877 .url(server.url("/")) 1878 .build(); 1879 executeSynchronously(request) 1880 .assertBody("abcabcabc"); 1881 } 1882 asyncResponseCanBeConsumedLater()1883 @Test public void asyncResponseCanBeConsumedLater() throws Exception { 1884 server.enqueue(new MockResponse().setBody("abc")); 1885 server.enqueue(new MockResponse().setBody("def")); 1886 1887 Request request = new Request.Builder() 1888 .url(server.url("/")) 1889 .header("User-Agent", "SyncApiTest") 1890 .build(); 1891 1892 final BlockingQueue<Response> responseRef = new SynchronousQueue<>(); 1893 client.newCall(request).enqueue(new Callback() { 1894 @Override public void onFailure(Request request, IOException e) { 1895 throw new AssertionError(); 1896 } 1897 1898 @Override public void onResponse(Response response) throws IOException { 1899 try { 1900 responseRef.put(response); 1901 } catch (InterruptedException e) { 1902 throw new AssertionError(); 1903 } 1904 } 1905 }); 1906 1907 Response response = responseRef.take(); 1908 assertEquals(200, response.code()); 1909 assertEquals("abc", response.body().string()); 1910 1911 // Make another request just to confirm that that connection can be reused... 1912 executeSynchronously(new Request.Builder().url(server.url("/")).build()).assertBody("def"); 1913 assertEquals(0, server.takeRequest().getSequenceNumber()); // New connection. 1914 assertEquals(1, server.takeRequest().getSequenceNumber()); // Connection reused. 1915 1916 // ... even before we close the response body! 1917 response.body().close(); 1918 } 1919 userAgentIsIncludedByDefault()1920 @Test public void userAgentIsIncludedByDefault() throws Exception { 1921 server.enqueue(new MockResponse()); 1922 1923 executeSynchronously(new Request.Builder().url(server.url("/")).build()); 1924 1925 RecordedRequest recordedRequest = server.takeRequest(); 1926 assertTrue(recordedRequest.getHeader("User-Agent") 1927 .matches(Version.userAgent())); 1928 } 1929 setFollowRedirectsFalse()1930 @Test public void setFollowRedirectsFalse() throws Exception { 1931 server.enqueue(new MockResponse() 1932 .setResponseCode(302) 1933 .addHeader("Location: /b") 1934 .setBody("A")); 1935 server.enqueue(new MockResponse().setBody("B")); 1936 1937 client.setFollowRedirects(false); 1938 RecordedResponse recordedResponse = executeSynchronously( 1939 new Request.Builder().url(server.url("/a")).build()); 1940 1941 recordedResponse 1942 .assertBody("A") 1943 .assertCode(302); 1944 } 1945 expect100ContinueNonEmptyRequestBody()1946 @Test public void expect100ContinueNonEmptyRequestBody() throws Exception { 1947 server.enqueue(new MockResponse()); 1948 1949 Request request = new Request.Builder() 1950 .url(server.url("/")) 1951 .header("Expect", "100-continue") 1952 .post(RequestBody.create(MediaType.parse("text/plain"), "abc")) 1953 .build(); 1954 1955 executeSynchronously(request) 1956 .assertCode(200) 1957 .assertSuccessful(); 1958 1959 assertEquals("abc", server.takeRequest().getBody().readUtf8()); 1960 } 1961 expect100ContinueEmptyRequestBody()1962 @Test public void expect100ContinueEmptyRequestBody() throws Exception { 1963 server.enqueue(new MockResponse()); 1964 1965 Request request = new Request.Builder() 1966 .url(server.url("/")) 1967 .header("Expect", "100-continue") 1968 .post(RequestBody.create(MediaType.parse("text/plain"), "")) 1969 .build(); 1970 1971 executeSynchronously(request) 1972 .assertCode(200) 1973 .assertSuccessful(); 1974 } 1975 1976 /** We forbid non-ASCII characters in outgoing request headers, but accept UTF-8. */ responseHeaderParsingIsLenient()1977 @Test public void responseHeaderParsingIsLenient() throws Exception { 1978 Headers headers = new Headers.Builder() 1979 .add("Content-Length", "0") 1980 .addLenient("a\tb: c\u007fd") 1981 .addLenient(": ef") 1982 .addLenient("\ud83c\udf69: \u2615\ufe0f") 1983 .build(); 1984 server.enqueue(new MockResponse().setHeaders(headers)); 1985 1986 Request request = new Request.Builder() 1987 .url(server.url("/")) 1988 .build(); 1989 1990 executeSynchronously(request) 1991 .assertHeader("a\tb", "c\u007fd") 1992 .assertHeader("\ud83c\udf69", "\u2615\ufe0f") 1993 .assertHeader("", "ef"); 1994 } 1995 customDns()1996 @Test public void customDns() throws Exception { 1997 // Configure a DNS that returns our MockWebServer for every hostname. 1998 FakeDns dns = new FakeDns(); 1999 dns.addresses(Dns.SYSTEM.lookup(server.url("/").host())); 2000 client.setDns(dns); 2001 2002 server.enqueue(new MockResponse()); 2003 Request request = new Request.Builder() 2004 .url(server.url("/").newBuilder().host("android.com").build()) 2005 .build(); 2006 executeSynchronously(request).assertCode(200); 2007 2008 dns.assertRequests("android.com"); 2009 } 2010 2011 /** We had a bug where failed HTTP/2 calls could break the entire connection. */ failingCallsDoNotInterfereWithConnection()2012 @Test public void failingCallsDoNotInterfereWithConnection() throws Exception { 2013 enableProtocol(Protocol.HTTP_2); 2014 2015 server.enqueue(new MockResponse().setBody("Response 1")); 2016 server.enqueue(new MockResponse().setBody("Response 2")); 2017 2018 RequestBody requestBody = new RequestBody() { 2019 @Override public MediaType contentType() { 2020 return null; 2021 } 2022 2023 @Override public void writeTo(BufferedSink sink) throws IOException { 2024 sink.writeUtf8("abc"); 2025 sink.flush(); 2026 2027 makeFailingCall(); 2028 2029 sink.writeUtf8("def"); 2030 sink.flush(); 2031 } 2032 }; 2033 Call call = client.newCall(new Request.Builder() 2034 .url(server.url("/")) 2035 .post(requestBody) 2036 .build()); 2037 assertEquals("Response 1", call.execute().body().string()); 2038 } 2039 2040 /** Test which headers are sent unencrypted to the HTTP proxy. */ proxyConnectOmitsApplicationHeaders()2041 @Test public void proxyConnectOmitsApplicationHeaders() throws Exception { 2042 server.useHttps(sslContext.getSocketFactory(), true); 2043 server.enqueue(new MockResponse() 2044 .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) 2045 .clearHeaders()); 2046 server.enqueue(new MockResponse() 2047 .setBody("encrypted response from the origin server")); 2048 2049 client.setSslSocketFactory(sslContext.getSocketFactory()); 2050 client.setProxy(server.toProxyAddress()); 2051 RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier(); 2052 client.setHostnameVerifier(hostnameVerifier); 2053 2054 Request request = new Request.Builder() 2055 .url("https://android.com/foo") 2056 .header("Private", "Secret") 2057 .header("User-Agent", "App 1.0") 2058 .build(); 2059 Response response = client.newCall(request).execute(); 2060 assertEquals("encrypted response from the origin server", response.body().string()); 2061 2062 RecordedRequest connect = server.takeRequest(); 2063 assertNull(connect.getHeader("Private")); 2064 assertEquals(Version.userAgent(), connect.getHeader("User-Agent")); 2065 assertEquals("Keep-Alive", connect.getHeader("Proxy-Connection")); 2066 assertEquals("android.com:443", connect.getHeader("Host")); 2067 2068 RecordedRequest get = server.takeRequest(); 2069 assertEquals("Secret", get.getHeader("Private")); 2070 assertEquals("App 1.0", get.getHeader("User-Agent")); 2071 2072 assertEquals(Arrays.asList("verify android.com"), hostnameVerifier.calls); 2073 } 2074 2075 /** Respond to a proxy authorization challenge. */ proxyAuthenticateOnConnect()2076 @Test public void proxyAuthenticateOnConnect() throws Exception { 2077 server.useHttps(sslContext.getSocketFactory(), true); 2078 server.enqueue(new MockResponse() 2079 .setResponseCode(407) 2080 .addHeader("Proxy-Authenticate: Basic realm=\"localhost\"")); 2081 server.enqueue(new MockResponse() 2082 .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) 2083 .clearHeaders()); 2084 server.enqueue(new MockResponse() 2085 .setBody("response body")); 2086 2087 client.setSslSocketFactory(sslContext.getSocketFactory()); 2088 client.setProxy(server.toProxyAddress()); 2089 client.setAuthenticator(new RecordingOkAuthenticator("password")); 2090 client.setHostnameVerifier(new RecordingHostnameVerifier()); 2091 2092 Request request = new Request.Builder() 2093 .url("https://android.com/foo") 2094 .build(); 2095 Response response = client.newCall(request).execute(); 2096 assertEquals("response body", response.body().string()); 2097 2098 RecordedRequest connect1 = server.takeRequest(); 2099 assertEquals("CONNECT android.com:443 HTTP/1.1", connect1.getRequestLine()); 2100 assertNull(connect1.getHeader("Proxy-Authorization")); 2101 2102 RecordedRequest connect2 = server.takeRequest(); 2103 assertEquals("CONNECT android.com:443 HTTP/1.1", connect2.getRequestLine()); 2104 assertEquals("password", connect2.getHeader("Proxy-Authorization")); 2105 2106 RecordedRequest get = server.takeRequest(); 2107 assertEquals("GET /foo HTTP/1.1", get.getRequestLine()); 2108 assertNull(get.getHeader("Proxy-Authorization")); 2109 } 2110 2111 /** 2112 * Confirm that we don't send the Proxy-Authorization header from the request to the proxy server. 2113 * We used to have that behavior but it is problematic because unrelated requests end up sharing 2114 * credentials. Worse, that approach leaks proxy credentials to the origin server. 2115 */ noProactiveProxyAuthorization()2116 @Test public void noProactiveProxyAuthorization() throws Exception { 2117 server.useHttps(sslContext.getSocketFactory(), true); 2118 server.enqueue(new MockResponse() 2119 .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) 2120 .clearHeaders()); 2121 server.enqueue(new MockResponse() 2122 .setBody("response body")); 2123 2124 client.setSslSocketFactory(sslContext.getSocketFactory()); 2125 client.setProxy(server.toProxyAddress()); 2126 client.setHostnameVerifier(new RecordingHostnameVerifier()); 2127 2128 Request request = new Request.Builder() 2129 .url("https://android.com/foo") 2130 .header("Proxy-Authorization", "password") 2131 .build(); 2132 Response response = client.newCall(request).execute(); 2133 assertEquals("response body", response.body().string()); 2134 2135 RecordedRequest connect = server.takeRequest(); 2136 assertNull(connect.getHeader("Proxy-Authorization")); 2137 2138 RecordedRequest get = server.takeRequest(); 2139 assertEquals("password", get.getHeader("Proxy-Authorization")); 2140 } 2141 2142 /** https://github.com/square/okhttp/issues/2344 */ ipv6HostHasSquareBraces()2143 @Test public void ipv6HostHasSquareBraces() throws Exception { 2144 // Use a proxy to fake IPv6 connectivity, even if localhost doesn't have IPv6. 2145 server.useHttps(sslContext.getSocketFactory(), true); 2146 server.setProtocols(Collections.singletonList(Protocol.HTTP_1_1)); 2147 server.enqueue(new MockResponse() 2148 .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) 2149 .clearHeaders()); 2150 server.enqueue(new MockResponse() 2151 .setBody("response body")); 2152 2153 client 2154 .setSslSocketFactory(sslContext.getSocketFactory()) 2155 .setHostnameVerifier(new RecordingHostnameVerifier()) 2156 .setProxy(server.toProxyAddress()); 2157 2158 Request request = new Request.Builder() 2159 .url("https://[::1]/") 2160 .build(); 2161 Response response = client.newCall(request).execute(); 2162 assertEquals("response body", response.body().string()); 2163 2164 RecordedRequest connect = server.takeRequest(); 2165 assertEquals("CONNECT [::1]:443 HTTP/1.1", connect.getRequestLine()); 2166 assertEquals("[::1]:443", connect.getHeader("Host")); 2167 2168 RecordedRequest get = server.takeRequest(); 2169 assertEquals("GET / HTTP/1.1", get.getRequestLine()); 2170 assertEquals("[::1]", get.getHeader("Host")); 2171 } 2172 makeFailingCall()2173 private void makeFailingCall() { 2174 RequestBody requestBody = new RequestBody() { 2175 @Override public MediaType contentType() { 2176 return null; 2177 } 2178 2179 @Override public long contentLength() throws IOException { 2180 return 1; 2181 } 2182 2183 @Override public void writeTo(BufferedSink sink) throws IOException { 2184 throw new IOException("write body fail!"); 2185 } 2186 }; 2187 Call call = client.newCall(new Request.Builder() 2188 .url(server.url("/")) 2189 .post(requestBody) 2190 .build()); 2191 try { 2192 call.execute(); 2193 fail(); 2194 } catch (IOException expected) { 2195 assertEquals("write body fail!", expected.getMessage()); 2196 } 2197 } 2198 executeSynchronously(Request request)2199 private RecordedResponse executeSynchronously(Request request) throws IOException { 2200 Response response = client.newCall(request).execute(); 2201 return new RecordedResponse(request, response, null, response.body().string(), null); 2202 } 2203 2204 /** 2205 * Tests that use this will fail unless boot classpath is set. Ex. {@code 2206 * -Xbootclasspath/p:/tmp/alpn-boot-8.0.0.v20140317} 2207 */ enableProtocol(Protocol protocol)2208 private void enableProtocol(Protocol protocol) { 2209 enableTls(); 2210 client.setProtocols(Arrays.asList(protocol, Protocol.HTTP_1_1)); 2211 server.setProtocols(client.getProtocols()); 2212 } 2213 enableTls()2214 private void enableTls() { 2215 client.setSslSocketFactory(sslContext.getSocketFactory()); 2216 client.setHostnameVerifier(new RecordingHostnameVerifier()); 2217 server.useHttps(sslContext.getSocketFactory(), false); 2218 } 2219 gzip(String data)2220 private Buffer gzip(String data) throws IOException { 2221 Buffer result = new Buffer(); 2222 BufferedSink sink = Okio.buffer(new GzipSink(result)); 2223 sink.writeUtf8(data); 2224 sink.close(); 2225 return result; 2226 } 2227 cancelLater(final Call call, final long delay)2228 private void cancelLater(final Call call, final long delay) { 2229 new Thread("canceler") { 2230 @Override public void run() { 2231 try { 2232 Thread.sleep(delay); 2233 } catch (InterruptedException e) { 2234 throw new AssertionError(); 2235 } 2236 call.cancel(); 2237 } 2238 }.start(); 2239 } 2240 2241 private static class RecordingSSLSocketFactory extends DelegatingSSLSocketFactory { 2242 2243 private List<SSLSocket> socketsCreated = new ArrayList<>(); 2244 RecordingSSLSocketFactory(SSLSocketFactory delegate)2245 public RecordingSSLSocketFactory(SSLSocketFactory delegate) { 2246 super(delegate); 2247 } 2248 2249 @Override configureSocket(SSLSocket sslSocket)2250 protected SSLSocket configureSocket(SSLSocket sslSocket) throws IOException { 2251 socketsCreated.add(sslSocket); 2252 return sslSocket; 2253 } 2254 getSocketsCreated()2255 public List<SSLSocket> getSocketsCreated() { 2256 return socketsCreated; 2257 } 2258 } 2259 2260 /** 2261 * Used during tests that involve TLS connection fallback attempts. OkHttp includes the 2262 * TLS_FALLBACK_SCSV cipher on fallback connections. See 2263 * {@link com.squareup.okhttp.FallbackTestClientSocketFactory} for details. 2264 */ suppressTlsFallbackScsv(OkHttpClient client)2265 private void suppressTlsFallbackScsv(OkHttpClient client) { 2266 FallbackTestClientSocketFactory clientSocketFactory = 2267 new FallbackTestClientSocketFactory(sslContext.getSocketFactory()); 2268 client.setSslSocketFactory(clientSocketFactory); 2269 } 2270 } 2271