• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.SslContextBuilder;
19 import com.squareup.okhttp.mockwebserver.MockResponse;
20 import com.squareup.okhttp.mockwebserver.MockWebServer;
21 import com.squareup.okhttp.mockwebserver.SocketPolicy;
22 import com.squareup.okhttp.mockwebserver.SocketShutdownListener;
23 import com.squareup.okhttp.testing.RecordingHostnameVerifier;
24 import java.util.Arrays;
25 import java.util.concurrent.TimeUnit;
26 import javax.net.ssl.SSLContext;
27 import org.junit.Rule;
28 import org.junit.Test;
29 import org.junit.rules.TestRule;
30 import org.junit.rules.Timeout;
31 
32 import static org.junit.Assert.assertEquals;
33 
34 public final class ConnectionReuseTest {
35   @Rule public final TestRule timeout = new Timeout(30_000);
36   @Rule public final MockWebServer server = new MockWebServer();
37 
38   private SSLContext sslContext = SslContextBuilder.localhost();
39   private OkHttpClient client = new OkHttpClient();
40 
connectionsAreReused()41   @Test public void connectionsAreReused() throws Exception {
42     server.enqueue(new MockResponse().setBody("a"));
43     server.enqueue(new MockResponse().setBody("b"));
44 
45     Request request = new Request.Builder()
46         .url(server.url("/"))
47         .build();
48     assertConnectionReused(request, request);
49   }
50 
connectionsAreReusedWithHttp2()51   @Test public void connectionsAreReusedWithHttp2() throws Exception {
52     enableHttp2();
53     server.enqueue(new MockResponse().setBody("a"));
54     server.enqueue(new MockResponse().setBody("b"));
55 
56     Request request = new Request.Builder()
57         .url(server.url("/"))
58         .build();
59     assertConnectionReused(request, request);
60   }
61 
connectionsAreNotReusedWithRequestConnectionClose()62   @Test public void connectionsAreNotReusedWithRequestConnectionClose() throws Exception {
63     server.enqueue(new MockResponse().setBody("a"));
64     server.enqueue(new MockResponse().setBody("b"));
65 
66     Request requestA = new Request.Builder()
67         .url(server.url("/"))
68         .header("Connection", "close")
69         .build();
70     Request requestB = new Request.Builder()
71         .url(server.url("/"))
72         .build();
73     assertConnectionNotReused(requestA, requestB);
74   }
75 
connectionsAreNotReusedWithResponseConnectionClose()76   @Test public void connectionsAreNotReusedWithResponseConnectionClose() throws Exception {
77     server.enqueue(new MockResponse()
78         .addHeader("Connection", "close")
79         .setBody("a"));
80     server.enqueue(new MockResponse().setBody("b"));
81 
82     Request requestA = new Request.Builder()
83         .url(server.url("/"))
84         .build();
85     Request requestB = new Request.Builder()
86         .url(server.url("/"))
87         .build();
88     assertConnectionNotReused(requestA, requestB);
89   }
90 
connectionsAreNotReusedWithUnknownLengthResponseBody()91   @Test public void connectionsAreNotReusedWithUnknownLengthResponseBody() throws Exception {
92     server.enqueue(new MockResponse()
93         .setBody("a")
94         .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END)
95         .clearHeaders());
96     server.enqueue(new MockResponse().setBody("b"));
97 
98     Request request = new Request.Builder()
99         .url(server.url("/"))
100         .build();
101     assertConnectionNotReused(request, request);
102   }
103 
connectionsAreNotReusedIfPoolIsSizeZero()104   @Test public void connectionsAreNotReusedIfPoolIsSizeZero() throws Exception {
105     client.setConnectionPool(new ConnectionPool(0, 5000));
106     server.enqueue(new MockResponse().setBody("a"));
107     server.enqueue(new MockResponse().setBody("b"));
108 
109     Request request = new Request.Builder()
110         .url(server.url("/"))
111         .build();
112     assertConnectionNotReused(request, request);
113   }
114 
connectionsReusedWithRedirectEvenIfPoolIsSizeZero()115   @Test public void connectionsReusedWithRedirectEvenIfPoolIsSizeZero() throws Exception {
116     client.setConnectionPool(new ConnectionPool(0, 5000));
117     server.enqueue(new MockResponse()
118         .setResponseCode(301)
119         .addHeader("Location: /b")
120         .setBody("a"));
121     server.enqueue(new MockResponse().setBody("b"));
122 
123     Request request = new Request.Builder()
124         .url(server.url("/"))
125         .build();
126     Response response = client.newCall(request).execute();
127     assertEquals("b", response.body().string());
128     assertEquals(0, server.takeRequest().getSequenceNumber());
129     assertEquals(1, server.takeRequest().getSequenceNumber());
130   }
131 
connectionsNotReusedWithRedirectIfDiscardingResponseIsSlow()132   @Test public void connectionsNotReusedWithRedirectIfDiscardingResponseIsSlow() throws Exception {
133     client.setConnectionPool(new ConnectionPool(0, 5000));
134     server.enqueue(new MockResponse()
135         .setResponseCode(301)
136         .addHeader("Location: /b")
137         .setBodyDelay(1, TimeUnit.SECONDS)
138         .setBody("a"));
139     server.enqueue(new MockResponse().setBody("b"));
140 
141     Request request = new Request.Builder()
142         .url(server.url("/"))
143         .build();
144     Response response = client.newCall(request).execute();
145     assertEquals("b", response.body().string());
146     assertEquals(0, server.takeRequest().getSequenceNumber());
147     assertEquals(0, server.takeRequest().getSequenceNumber());
148   }
149 
silentRetryWhenIdempotentRequestFailsOnReusedConnection()150   @Test public void silentRetryWhenIdempotentRequestFailsOnReusedConnection() throws Exception {
151     server.enqueue(new MockResponse().setBody("a"));
152     server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AFTER_REQUEST));
153     server.enqueue(new MockResponse().setBody("b"));
154 
155     Request request = new Request.Builder()
156         .url(server.url("/"))
157         .build();
158 
159     Response responseA = client.newCall(request).execute();
160     assertEquals("a", responseA.body().string());
161     assertEquals(0, server.takeRequest().getSequenceNumber());
162 
163     Response responseB = client.newCall(request).execute();
164     assertEquals("b", responseB.body().string());
165     assertEquals(1, server.takeRequest().getSequenceNumber());
166     assertEquals(0, server.takeRequest().getSequenceNumber());
167   }
168 
staleConnectionNotReusedForNonIdempotentRequest()169   @Test public void staleConnectionNotReusedForNonIdempotentRequest() throws Exception {
170     SocketShutdownListener shutdownListener = new SocketShutdownListener();
171     server.enqueue(new MockResponse().setBody("a")
172         .setSocketPolicy(SocketPolicy.SHUTDOWN_OUTPUT_AT_END)
173         .setSocketShutdownListener(shutdownListener));
174     server.enqueue(new MockResponse().setBody("b"));
175 
176     Request requestA = new Request.Builder()
177         .url(server.url("/"))
178         .build();
179     Response responseA = client.newCall(requestA).execute();
180     assertEquals("a", responseA.body().string());
181     assertEquals(0, server.takeRequest().getSequenceNumber());
182 
183     shutdownListener.waitForSocketShutdown();
184 
185     Request requestB = new Request.Builder()
186         .url(server.url("/"))
187         .post(RequestBody.create(MediaType.parse("text/plain"), "b"))
188         .build();
189     Response responseB = client.newCall(requestB).execute();
190     assertEquals("b", responseB.body().string());
191     assertEquals(0, server.takeRequest().getSequenceNumber());
192   }
193 
http2ConnectionsAreSharedBeforeResponseIsConsumed()194   @Test public void http2ConnectionsAreSharedBeforeResponseIsConsumed() throws Exception {
195     enableHttp2();
196     server.enqueue(new MockResponse().setBody("a"));
197     server.enqueue(new MockResponse().setBody("b"));
198 
199     Request request = new Request.Builder()
200         .url(server.url("/"))
201         .build();
202     Response response1 = client.newCall(request).execute();
203     Response response2 = client.newCall(request).execute();
204     response1.body().string(); // Discard the response body.
205     response2.body().string(); // Discard the response body.
206     assertEquals(0, server.takeRequest().getSequenceNumber());
207     assertEquals(1, server.takeRequest().getSequenceNumber());
208   }
209 
connectionsAreEvicted()210   @Test public void connectionsAreEvicted() throws Exception {
211     server.enqueue(new MockResponse().setBody("a"));
212     server.enqueue(new MockResponse().setBody("b"));
213 
214     client.setConnectionPool(new ConnectionPool(5, 250, TimeUnit.MILLISECONDS));
215     Request request = new Request.Builder()
216         .url(server.url("/"))
217         .build();
218 
219     Response response1 = client.newCall(request).execute();
220     assertEquals("a", response1.body().string());
221 
222     // Give the thread pool a chance to evict.
223     Thread.sleep(500);
224 
225     Response response2 = client.newCall(request).execute();
226     assertEquals("b", response2.body().string());
227 
228     assertEquals(0, server.takeRequest().getSequenceNumber());
229     assertEquals(0, server.takeRequest().getSequenceNumber());
230   }
231 
enableHttp2()232   private void enableHttp2() {
233     client.setSslSocketFactory(sslContext.getSocketFactory());
234     client.setHostnameVerifier(new RecordingHostnameVerifier());
235     client.setProtocols(Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1));
236     server.useHttps(sslContext.getSocketFactory(), false);
237     server.setProtocols(client.getProtocols());
238   }
239 
assertConnectionReused(Request... requests)240   private void assertConnectionReused(Request... requests) throws Exception {
241     for (int i = 0; i < requests.length; i++) {
242       Response response = client.newCall(requests[i]).execute();
243       response.body().string(); // Discard the response body.
244       assertEquals(i, server.takeRequest().getSequenceNumber());
245     }
246   }
247 
assertConnectionNotReused(Request... requests)248   private void assertConnectionNotReused(Request... requests) throws Exception {
249     for (Request request : requests) {
250       Response response = client.newCall(request).execute();
251       response.body().string(); // Discard the response body.
252       assertEquals(0, server.takeRequest().getSequenceNumber());
253     }
254   }
255 }
256