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