Home | History | Annotate | Download | only in okhttp
      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 
     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 
     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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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