View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.hc.core5.http.impl;
29  
30  import org.apache.hc.core5.http.ConnectionReuseStrategy;
31  import org.apache.hc.core5.http.HttpRequest;
32  import org.apache.hc.core5.http.HttpResponse;
33  import org.apache.hc.core5.http.HttpStatus;
34  import org.apache.hc.core5.http.HttpVersion;
35  import org.apache.hc.core5.http.Method;
36  import org.apache.hc.core5.http.message.BasicHttpRequest;
37  import org.apache.hc.core5.http.message.BasicHttpResponse;
38  import org.apache.hc.core5.http.protocol.BasicHttpContext;
39  import org.apache.hc.core5.http.protocol.HttpContext;
40  import org.junit.jupiter.api.Assertions;
41  import org.junit.jupiter.api.BeforeEach;
42  import org.junit.jupiter.api.Test;
43  
44  public class TestDefaultConnectionReuseStrategy {
45  
46      /** HTTP context. */
47      private HttpContext context;
48  
49      /** The reuse strategy to be tested. */
50      private ConnectionReuseStrategy reuseStrategy;
51  
52      @BeforeEach
53      public void setUp() {
54          reuseStrategy = DefaultConnectionReuseStrategy.INSTANCE;
55          context = new BasicHttpContext(null);
56      }
57  
58      @Test
59      public void testInvalidResponseArg() throws Exception {
60          Assertions.assertThrows(NullPointerException.class, () ->
61                  reuseStrategy.keepAlive(null, null, this.context));
62      }
63  
64      @Test
65      public void testNoContentLengthResponseHttp1_0() throws Exception {
66          context.setProtocolVersion(HttpVersion.HTTP_1_0);
67          final HttpResponse response = new BasicHttpResponse(200, "OK");
68          Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context));
69      }
70  
71      @Test
72      public void testNoContentLengthResponseHttp1_1() throws Exception {
73          final HttpResponse response = new BasicHttpResponse(200, "OK");
74          Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context));
75      }
76  
77      @Test
78      public void testChunkedContent() throws Exception {
79          final HttpResponse response = new BasicHttpResponse(200, "OK");
80          response.addHeader("Transfer-Encoding", "chunked");
81          Assertions.assertTrue(reuseStrategy.keepAlive(null, response, context));
82      }
83  
84      @Test
85      public void testIgnoreInvalidKeepAlive() throws Exception {
86          context.setProtocolVersion(HttpVersion.HTTP_1_0);
87          final HttpResponse response = new BasicHttpResponse(200, "OK");
88          response.addHeader("Connection", "keep-alive");
89          Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context));
90      }
91  
92      @Test
93      public void testExplicitClose() throws Exception {
94          // Use HTTP 1.1
95          final HttpResponse response = new BasicHttpResponse(200, "OK");
96          response.addHeader("Transfer-Encoding", "chunked");
97          response.addHeader("Connection", "close");
98          Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context));
99      }
100 
101     @Test
102     public void testExplicitKeepAlive() throws Exception {
103         // Use HTTP 1.0
104         final HttpResponse response = new BasicHttpResponse(200, "OK");
105         response.setVersion(HttpVersion.HTTP_1_0);
106         response.addHeader("Content-Length", "10");
107         response.addHeader("Connection", "keep-alive");
108 
109         Assertions.assertTrue(reuseStrategy.keepAlive(null, response, context));
110     }
111 
112     @Test
113     public void testHTTP10Default() throws Exception {
114         final HttpResponse response = new BasicHttpResponse(200, "OK");
115         response.setVersion(HttpVersion.HTTP_1_0);
116         response.addHeader("Content-Length", "10");
117         Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context));
118     }
119 
120     @Test
121     public void testHTTP11Default() throws Exception {
122         final HttpResponse response = new BasicHttpResponse(200, "OK");
123         response.addHeader("Content-Length", "10");
124         Assertions.assertTrue(reuseStrategy.keepAlive(null, response, context));
125     }
126 
127     @Test
128     public void testBrokenConnectionDirective1() throws Exception {
129         // Use HTTP 1.0
130         final HttpResponse response = new BasicHttpResponse(200, "OK");
131         response.setVersion(HttpVersion.HTTP_1_0);
132         response.addHeader("Content-Length", "10");
133         response.addHeader("Connection", "keep--alive");
134         Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context));
135     }
136 
137     @Test
138     public void testBrokenConnectionDirective2() throws Exception {
139         // Use HTTP 1.0
140         final HttpResponse response = new BasicHttpResponse(200, "OK");
141         response.setVersion(HttpVersion.HTTP_1_0);
142         response.addHeader("Content-Length", "10");
143         response.addHeader("Connection", null);
144         Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context));
145     }
146 
147     @Test
148     public void testConnectionTokens1() throws Exception {
149         // Use HTTP 1.1
150         final HttpResponse response = new BasicHttpResponse(200, "OK");
151         response.addHeader("Transfer-Encoding", "chunked");
152         response.addHeader("Connection", "yadda, cLOSe, dumdy");
153         Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context));
154     }
155 
156     @Test
157     public void testConnectionTokens2() throws Exception {
158         // Use HTTP 1.1
159         final HttpResponse response = new BasicHttpResponse(200, "OK");
160         response.addHeader("Transfer-Encoding", "chunked");
161         response.addHeader("Connection", "yadda, kEEP-alive, dumdy");
162         Assertions.assertTrue(reuseStrategy.keepAlive(null, response, context));
163     }
164 
165     @Test
166     public void testConnectionTokens3() throws Exception {
167         // Use HTTP 1.1
168         final HttpResponse response = new BasicHttpResponse(200, "OK");
169         response.addHeader("Transfer-Encoding", "chunked");
170         response.addHeader("Connection", "yadda, keep-alive, close, dumdy");
171         Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context));
172     }
173 
174     @Test
175     public void testConnectionTokens4() throws Exception {
176         // Use HTTP 1.1
177         final HttpResponse response = new BasicHttpResponse(200, "OK");
178         response.addHeader("Transfer-Encoding", "chunked");
179         response.addHeader("Connection", "yadda, close, dumdy");
180         response.addHeader("Proxy-Connection", "keep-alive");
181         // Connection takes precedence over Proxy-Connection
182         Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context));
183     }
184 
185     @Test
186     public void testConnectionTokens5() throws Exception {
187         // Use HTTP 1.1
188         final HttpResponse response = new BasicHttpResponse(200, "OK");
189         response.addHeader("Transfer-Encoding", "chunked");
190         response.addHeader("Connection", "yadda, dumdy");
191         response.addHeader("Proxy-Connection", "close");
192         // Connection takes precedence over Proxy-Connection,
193         // even if it doesn't contain a recognized token.
194         // Default for HTTP/1.1 is to keep alive.
195         Assertions.assertTrue(reuseStrategy.keepAlive(null, response, context));
196     }
197 
198     @Test
199     public void testConnectionTokens6() throws Exception {
200         // Use HTTP 1.1
201         final HttpResponse response = new BasicHttpResponse(200, "OK");
202         response.addHeader("Transfer-Encoding", "chunked");
203         response.addHeader("Connection", "");
204         response.addHeader("Proxy-Connection", "close");
205         // Connection takes precedence over Proxy-Connection,
206         // even if it is empty. Default for HTTP/1.1 is to keep alive.
207         Assertions.assertTrue(reuseStrategy.keepAlive(null, response, context));
208     }
209 
210     @Test
211     public void testMultipleContentLength() throws Exception {
212         // Use HTTP 1.1
213         final HttpResponse response = new BasicHttpResponse(200, "OK");
214         response.addHeader("Content-Length", "10");
215         response.addHeader("Content-Length", "11");
216         Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context));
217     }
218 
219     @Test
220     public void testNoContentResponse() throws Exception {
221         // Use HTTP 1.1
222         final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content");
223         Assertions.assertTrue(reuseStrategy.keepAlive(null, response, context));
224     }
225 
226     @Test
227     public void testNoContentResponseHttp10() throws Exception {
228         // Use HTTP 1.0
229         final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content");
230         response.setVersion(HttpVersion.HTTP_1_0);
231         Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context));
232     }
233 
234     @Test
235     public void testRequestExplicitClose() throws Exception {
236         final HttpRequest request = new BasicHttpRequest(Method.GET, "/");
237         request.addHeader("Connection", "close");
238 
239         final HttpResponse response = new BasicHttpResponse(200, "OK");
240         response.addHeader("Transfer-Encoding", "chunked");
241         response.addHeader("Connection", "keep-alive");
242         Assertions.assertFalse(reuseStrategy.keepAlive(request, response, context));
243     }
244 
245     @Test
246     public void testRequestNoExplicitClose() throws Exception {
247         final HttpRequest request = new BasicHttpRequest(Method.GET, "/");
248         request.addHeader("Connection", "blah, blah, blah");
249 
250         final HttpResponse response = new BasicHttpResponse(200, "OK");
251         response.addHeader("Transfer-Encoding", "chunked");
252         response.addHeader("Connection", "keep-alive");
253         Assertions.assertTrue(reuseStrategy.keepAlive(request, response, context));
254     }
255 
256     @Test
257     public void testRequestExplicitCloseMultipleTokens() throws Exception {
258         final HttpRequest request = new BasicHttpRequest(Method.GET, "/");
259         request.addHeader("Connection", "blah, blah, blah");
260         request.addHeader("Connection", "keep-alive");
261         request.addHeader("Connection", "close");
262 
263         final HttpResponse response = new BasicHttpResponse(200, "OK");
264         response.addHeader("Transfer-Encoding", "chunked");
265         response.addHeader("Connection", "keep-alive");
266         Assertions.assertFalse(reuseStrategy.keepAlive(request, response, context));
267     }
268 
269     @Test
270     public void testRequestClose() throws Exception {
271         final HttpRequest request = new BasicHttpRequest(Method.GET, "/");
272         request.addHeader("Connection", "close");
273 
274         final HttpResponse response = new BasicHttpResponse(200, "OK");
275         response.addHeader("Content-Length", "10");
276         response.addHeader("Connection", "keep-alive");
277 
278         Assertions.assertFalse(reuseStrategy.keepAlive(request, response, context));
279     }
280 
281     @Test
282     public void testHeadRequestWithout() throws Exception {
283         final HttpRequest request = new BasicHttpRequest(Method.HEAD, "/");
284         final HttpResponse response = new BasicHttpResponse(200, "OK");
285 
286         Assertions.assertTrue(reuseStrategy.keepAlive(request, response, context));
287     }
288 
289     @Test
290     public void testHttp204ContentLengthGreaterThanZero() throws Exception {
291         final HttpResponse response = new BasicHttpResponse(204, "OK");
292         response.addHeader("Content-Length", "10");
293         response.addHeader("Connection", "keep-alive");
294         Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context));
295     }
296 
297     @Test
298     public void testHttp204ContentLengthEqualToZero() throws Exception {
299         final HttpResponse response = new BasicHttpResponse(204, "OK");
300         response.addHeader("Content-Length", "0");
301         response.addHeader("Connection", "keep-alive");
302         Assertions.assertTrue(reuseStrategy.keepAlive(null, response, context));
303     }
304 
305     @Test
306     public void testHttp204ChunkedContent() throws Exception {
307         final HttpResponse response = new BasicHttpResponse(204, "OK");
308         response.addHeader("Transfer-Encoding", "chunked");
309         response.addHeader("Connection", "keep-alive");
310         Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context));
311     }
312 }
313