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