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