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