1/*
2 * Copyright (C) 2013 Square, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.squareup.okhttp;
17
18import com.squareup.okhttp.internal.DoubleInetAddressDns;
19import com.squareup.okhttp.internal.RecordingOkAuthenticator;
20import com.squareup.okhttp.internal.SingleInetAddressDns;
21import com.squareup.okhttp.internal.SslContextBuilder;
22import com.squareup.okhttp.internal.Util;
23import com.squareup.okhttp.internal.Version;
24import com.squareup.okhttp.internal.http.FakeDns;
25import com.squareup.okhttp.internal.io.InMemoryFileSystem;
26import com.squareup.okhttp.mockwebserver.Dispatcher;
27import com.squareup.okhttp.mockwebserver.MockResponse;
28import com.squareup.okhttp.mockwebserver.MockWebServer;
29import com.squareup.okhttp.mockwebserver.RecordedRequest;
30import com.squareup.okhttp.mockwebserver.SocketPolicy;
31import com.squareup.okhttp.testing.RecordingHostnameVerifier;
32import java.io.File;
33import java.io.IOException;
34import java.io.InputStream;
35import java.io.InterruptedIOException;
36import java.net.CookieManager;
37import java.net.HttpCookie;
38import java.net.HttpURLConnection;
39import java.net.InetAddress;
40import java.net.InetSocketAddress;
41import java.net.ProtocolException;
42import java.net.ServerSocket;
43import java.net.UnknownServiceException;
44import java.security.cert.Certificate;
45import java.util.ArrayList;
46import java.util.Arrays;
47import java.util.Collections;
48import java.util.List;
49import java.util.concurrent.BlockingQueue;
50import java.util.concurrent.Callable;
51import java.util.concurrent.CountDownLatch;
52import java.util.concurrent.ExecutorService;
53import java.util.concurrent.Executors;
54import java.util.concurrent.Future;
55import java.util.concurrent.SynchronousQueue;
56import java.util.concurrent.TimeUnit;
57import java.util.concurrent.atomic.AtomicBoolean;
58import java.util.concurrent.atomic.AtomicReference;
59import javax.net.ServerSocketFactory;
60import javax.net.ssl.SSLContext;
61import javax.net.ssl.SSLHandshakeException;
62import javax.net.ssl.SSLPeerUnverifiedException;
63import javax.net.ssl.SSLProtocolException;
64import javax.net.ssl.SSLSocket;
65import javax.net.ssl.SSLSocketFactory;
66import okio.Buffer;
67import okio.BufferedSink;
68import okio.BufferedSource;
69import okio.GzipSink;
70import okio.Okio;
71import org.junit.After;
72import org.junit.Before;
73import org.junit.Rule;
74import org.junit.Test;
75import org.junit.rules.TestRule;
76import org.junit.rules.Timeout;
77
78import static com.squareup.okhttp.internal.Internal.logger;
79import static java.net.CookiePolicy.ACCEPT_ORIGINAL_SERVER;
80import static org.junit.Assert.assertEquals;
81import static org.junit.Assert.assertFalse;
82import static org.junit.Assert.assertNotSame;
83import static org.junit.Assert.assertNull;
84import static org.junit.Assert.assertTrue;
85import static org.junit.Assert.fail;
86
87public 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
100  @Before public void setUp() throws Exception {
101    logger.addHandler(logHandler);
102  }
103
104  @After public void tearDown() throws Exception {
105    cache.delete();
106    Util.closeQuietly(nullServer);
107    logger.removeHandler(logHandler);
108  }
109
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
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
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
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
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
175  @Test public void get_HTTP_2() throws Exception {
176    enableProtocol(Protocol.HTTP_2);
177    get();
178  }
179
180  @Test public void get_HTTPS() throws Exception {
181    enableTls();
182    get();
183  }
184
185  @Test public void get_SPDY_3() throws Exception {
186    enableProtocol(Protocol.SPDY_3);
187    get();
188  }
189
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
209  @Test public void repeatedHeaderNames_SPDY_3() throws Exception {
210    enableProtocol(Protocol.SPDY_3);
211    repeatedHeaderNames();
212  }
213
214  @Test public void repeatedHeaderNames_HTTP_2() throws Exception {
215    enableProtocol(Protocol.HTTP_2);
216    repeatedHeaderNames();
217  }
218
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
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
249  @Test public void head_HTTPS() throws Exception {
250    enableTls();
251    head();
252  }
253
254  @Test public void head_HTTP_2() throws Exception {
255    enableProtocol(Protocol.HTTP_2);
256    head();
257  }
258
259  @Test public void head_SPDY_3() throws Exception {
260    enableProtocol(Protocol.SPDY_3);
261    head();
262  }
263
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
283  @Test public void post_HTTPS() throws Exception {
284    enableTls();
285    post();
286  }
287
288  @Test public void post_HTTP_2() throws Exception {
289    enableProtocol(Protocol.HTTP_2);
290    post();
291  }
292
293  @Test public void post_SPDY_3() throws Exception {
294    enableProtocol(Protocol.SPDY_3);
295    post();
296  }
297
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
317  @Test public void postZerolength_HTTPS() throws Exception {
318    enableTls();
319    postZeroLength();
320  }
321
322  @Test public void postZerolength_HTTP_2() throws Exception {
323    enableProtocol(Protocol.HTTP_2);
324    postZeroLength();
325  }
326
327  @Test public void postZeroLength_SPDY_3() throws Exception {
328    enableProtocol(Protocol.SPDY_3);
329    postZeroLength();
330  }
331
332  @Test public void postBodyRetransmittedAfterAuthorizationFail() throws Exception {
333    postBodyRetransmittedAfterAuthorizationFail("abc");
334  }
335
336  @Test public void postBodyRetransmittedAfterAuthorizationFail_HTTPS() throws Exception {
337    enableTls();
338    postBodyRetransmittedAfterAuthorizationFail("abc");
339  }
340
341  @Test public void postBodyRetransmittedAfterAuthorizationFail_HTTP_2() throws Exception {
342    enableProtocol(Protocol.HTTP_2);
343    postBodyRetransmittedAfterAuthorizationFail("abc");
344  }
345
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 */
352  @Test public void postEmptyBodyRetransmittedAfterAuthorizationFail() throws Exception {
353    postBodyRetransmittedAfterAuthorizationFail("");
354  }
355
356  @Test public void postEmptyBodyRetransmittedAfterAuthorizationFail_HTTPS() throws Exception {
357    enableTls();
358    postBodyRetransmittedAfterAuthorizationFail("");
359  }
360
361  @Test public void postEmptyBodyRetransmittedAfterAuthorizationFail_HTTP_2() throws Exception {
362    enableProtocol(Protocol.HTTP_2);
363    postBodyRetransmittedAfterAuthorizationFail("");
364  }
365
366  @Test public void postEmptyBodyRetransmittedAfterAuthorizationFail_SPDY_3() throws Exception {
367    enableProtocol(Protocol.SPDY_3);
368    postBodyRetransmittedAfterAuthorizationFail("");
369  }
370
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
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
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
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
447  @Test public void delete_HTTPS() throws Exception {
448    enableTls();
449    delete();
450  }
451
452  @Test public void delete_HTTP_2() throws Exception {
453    enableProtocol(Protocol.HTTP_2);
454    delete();
455  }
456
457  @Test public void delete_SPDY_3() throws Exception {
458    enableProtocol(Protocol.SPDY_3);
459    delete();
460  }
461
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
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
498  @Test public void put_HTTPS() throws Exception {
499    enableTls();
500    put();
501  }
502
503  @Test public void put_HTTP_2() throws Exception {
504    enableProtocol(Protocol.HTTP_2);
505    put();
506  }
507
508  @Test public void put_SPDY_3() throws Exception {
509    enableProtocol(Protocol.SPDY_3);
510    put();
511  }
512
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
532  @Test public void patch_HTTP_2() throws Exception {
533    enableProtocol(Protocol.HTTP_2);
534    patch();
535  }
536
537  @Test public void patch_HTTPS() throws Exception {
538    enableTls();
539    patch();
540  }
541
542  @Test public void patch_SPDY_3() throws Exception {
543    enableProtocol(Protocol.SPDY_3);
544    patch();
545  }
546
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
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
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
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
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
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
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
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
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 */
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 */
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
1580  @Test public void cancelDuringHttpConnect() throws Exception {
1581    cancelDuringConnect("http");
1582  }
1583
1584  @Test public void cancelDuringHttpsConnect() throws Exception {
1585    cancelDuringConnect("https");
1586  }
1587
1588  /** Cancel a call that's waiting for connect to complete. */
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
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
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
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
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
1668  @Test public void cancelInFlightBeforeResponseReadThrowsIOE_HTTPS() throws Exception {
1669    enableTls();
1670    cancelInFlightBeforeResponseReadThrowsIOE();
1671  }
1672
1673  @Test public void cancelInFlightBeforeResponseReadThrowsIOE_HTTP_2() throws Exception {
1674    enableProtocol(Protocol.HTTP_2);
1675    cancelInFlightBeforeResponseReadThrowsIOE();
1676  }
1677
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   */
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
1710  @Test public void canceledBeforeIOSignalsOnFailure_HTTPS() throws Exception {
1711    enableTls();
1712    canceledBeforeIOSignalsOnFailure();
1713  }
1714
1715  @Test public void canceledBeforeIOSignalsOnFailure_HTTP_2() throws Exception {
1716    enableProtocol(Protocol.HTTP_2);
1717    canceledBeforeIOSignalsOnFailure();
1718  }
1719
1720  @Test public void canceledBeforeIOSignalsOnFailure_SPDY_3() throws Exception {
1721    enableProtocol(Protocol.SPDY_3);
1722    canceledBeforeIOSignalsOnFailure();
1723  }
1724
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
1742  @Test public void canceledBeforeResponseReadSignalsOnFailure_HTTPS() throws Exception {
1743    enableTls();
1744    canceledBeforeResponseReadSignalsOnFailure();
1745  }
1746
1747  @Test public void canceledBeforeResponseReadSignalsOnFailure_HTTP_2() throws Exception {
1748    enableProtocol(Protocol.HTTP_2);
1749    canceledBeforeResponseReadSignalsOnFailure();
1750  }
1751
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   */
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
1794  @Test public void canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce_HTTPS()
1795      throws Exception {
1796    enableTls();
1797    canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce();
1798  }
1799
1800  @Test public void canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce_HTTP_2()
1801      throws Exception {
1802    enableProtocol(Protocol.HTTP_2);
1803    canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce();
1804  }
1805
1806  @Test public void canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce_SPDY_3()
1807      throws Exception {
1808    enableProtocol(Protocol.SPDY_3);
1809    canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce();
1810  }
1811
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
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 */
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
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
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
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
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
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. */
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
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. */
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. */
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. */
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   */
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 */
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
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
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   */
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
2206  private void enableTls() {
2207    client.setSslSocketFactory(sslContext.getSocketFactory());
2208    client.setHostnameVerifier(new RecordingHostnameVerifier());
2209    server.useHttps(sslContext.getSocketFactory(), false);
2210  }
2211
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
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
2237    public RecordingSSLSocketFactory(SSLSocketFactory delegate) {
2238      super(delegate);
2239    }
2240
2241    @Override
2242    protected SSLSocket configureSocket(SSLSocket sslSocket) throws IOException {
2243      socketsCreated.add(sslSocket);
2244      return sslSocket;
2245    }
2246
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   */
2257  private void suppressTlsFallbackScsv(OkHttpClient client) {
2258    FallbackTestClientSocketFactory clientSocketFactory =
2259        new FallbackTestClientSocketFactory(sslContext.getSocketFactory());
2260    client.setSslSocketFactory(clientSocketFactory);
2261  }
2262}
2263