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