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