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