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