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  package org.apache.hc.client5.testing.sync;
28  
29  import java.io.ByteArrayInputStream;
30  import java.io.IOException;
31  import java.net.URI;
32  import java.util.Random;
33  import java.util.concurrent.Executors;
34  import java.util.concurrent.ScheduledExecutorService;
35  import java.util.concurrent.TimeUnit;
36  
37  import org.apache.hc.client5.http.HttpRequestRetryStrategy;
38  import org.apache.hc.client5.http.classic.methods.HttpGet;
39  import org.apache.hc.client5.http.classic.methods.HttpPost;
40  import org.apache.hc.client5.http.protocol.HttpClientContext;
41  import org.apache.hc.client5.http.protocol.RedirectLocations;
42  import org.apache.hc.client5.http.utils.URIUtils;
43  import org.apache.hc.core5.http.ClassicHttpRequest;
44  import org.apache.hc.core5.http.ClassicHttpResponse;
45  import org.apache.hc.core5.http.EntityDetails;
46  import org.apache.hc.core5.http.Header;
47  import org.apache.hc.core5.http.HttpException;
48  import org.apache.hc.core5.http.HttpHost;
49  import org.apache.hc.core5.http.HttpRequest;
50  import org.apache.hc.core5.http.HttpRequestInterceptor;
51  import org.apache.hc.core5.http.HttpResponse;
52  import org.apache.hc.core5.http.HttpStatus;
53  import org.apache.hc.core5.http.impl.io.HttpRequestExecutor;
54  import org.apache.hc.core5.http.io.HttpClientConnection;
55  import org.apache.hc.core5.http.io.HttpRequestHandler;
56  import org.apache.hc.core5.http.io.entity.EntityUtils;
57  import org.apache.hc.core5.http.io.entity.InputStreamEntity;
58  import org.apache.hc.core5.http.io.entity.StringEntity;
59  import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
60  import org.apache.hc.core5.http.protocol.HttpContext;
61  import org.apache.hc.core5.net.URIBuilder;
62  import org.apache.hc.core5.util.TimeValue;
63  import org.junit.Assert;
64  import org.junit.Test;
65  
66  /**
67   * Client protocol handling tests.
68   */
69  public class TestClientRequestExecution extends LocalServerTestBase {
70  
71      private static class SimpleService implements HttpRequestHandler {
72  
73          public SimpleService() {
74              super();
75          }
76  
77          @Override
78          public void handle(
79                  final ClassicHttpRequest request,
80                  final ClassicHttpResponse response,
81                  final HttpContext context) throws HttpException, IOException {
82              response.setCode(HttpStatus.SC_OK);
83              final StringEntity entity = new StringEntity("Whatever");
84              response.setEntity(entity);
85          }
86      }
87  
88      private static class FaultyHttpRequestExecutor extends HttpRequestExecutor {
89  
90          private static final String MARKER = "marker";
91  
92          private final String failureMsg;
93  
94          public FaultyHttpRequestExecutor(final String failureMsg) {
95              this.failureMsg = failureMsg;
96          }
97  
98          @Override
99          public ClassicHttpResponse execute(
100                 final ClassicHttpRequest request,
101                 final HttpClientConnection conn,
102                 final HttpContext context) throws IOException, HttpException {
103 
104             final ClassicHttpResponse response = super.execute(request, conn, context);
105             final Object marker = context.getAttribute(MARKER);
106             if (marker == null) {
107                 context.setAttribute(MARKER, Boolean.TRUE);
108                 throw new IOException(failureMsg);
109             }
110             return response;
111         }
112 
113     }
114 
115     @Test
116     public void testAutoGeneratedHeaders() throws Exception {
117         this.server.registerHandler("*", new SimpleService());
118 
119         final HttpRequestInterceptor interceptor = new HttpRequestInterceptor() {
120 
121             @Override
122             public void process(
123                     final HttpRequest request,
124                     final EntityDetails entityDetails,
125                     final HttpContext context) throws HttpException, IOException {
126                 request.addHeader("my-header", "stuff");
127             }
128 
129         };
130 
131         final HttpRequestRetryStrategy requestRetryStrategy = new HttpRequestRetryStrategy() {
132 
133             @Override
134             public boolean retryRequest(
135                     final HttpRequest request,
136                     final IOException exception,
137                     final int executionCount,
138                     final HttpContext context) {
139                 return true;
140             }
141 
142             @Override
143             public boolean retryRequest(
144                     final HttpResponse response,
145                     final int executionCount,
146                     final HttpContext context) {
147                 return false;
148             }
149 
150             @Override
151             public TimeValue getRetryInterval(
152                     final HttpResponse response,
153                     final int executionCount,
154                     final HttpContext context) {
155                 return TimeValue.ofSeconds(1L);
156             }
157 
158         };
159 
160         this.httpclient = this.clientBuilder
161             .addRequestInterceptorFirst(interceptor)
162             .setRequestExecutor(new FaultyHttpRequestExecutor("Oppsie"))
163             .setRetryStrategy(requestRetryStrategy)
164             .build();
165 
166         final HttpHost target = start();
167 
168         final HttpClientContext context = HttpClientContext.create();
169 
170         final HttpGet httpget = new HttpGet("/");
171 
172         final ClassicHttpResponse response = this.httpclient.execute(target, httpget, context);
173         EntityUtils.consume(response.getEntity());
174 
175         final HttpRequest reqWrapper = context.getRequest();
176 
177         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
178 
179         final Header[] myheaders = reqWrapper.getHeaders("my-header");
180         Assert.assertNotNull(myheaders);
181         Assert.assertEquals(1, myheaders.length);
182     }
183 
184     @Test(expected=IOException.class)
185     public void testNonRepeatableEntity() throws Exception {
186         this.server.registerHandler("*", new SimpleService());
187 
188         final HttpRequestRetryStrategy requestRetryStrategy = new HttpRequestRetryStrategy() {
189 
190             @Override
191             public boolean retryRequest(
192                     final HttpRequest request,
193                     final IOException exception,
194                     final int executionCount,
195                     final HttpContext context) {
196                 return true;
197             }
198 
199             @Override
200             public boolean retryRequest(
201                     final HttpResponse response,
202                     final int executionCount,
203                     final HttpContext context) {
204                 return false;
205             }
206 
207             @Override
208             public TimeValue getRetryInterval(
209                     final HttpResponse response,
210                     final int executionCount,
211                     final HttpContext context) {
212                 return TimeValue.ofSeconds(1L);
213             }
214 
215         };
216 
217         this.httpclient = this.clientBuilder
218             .setRequestExecutor(new FaultyHttpRequestExecutor("a message showing that this failed"))
219             .setRetryStrategy(requestRetryStrategy)
220             .build();
221 
222         final HttpHost target = start();
223 
224         final HttpClientContext context = HttpClientContext.create();
225 
226         final HttpPost httppost = new HttpPost("/");
227         httppost.setEntity(new InputStreamEntity(
228                 new ByteArrayInputStream(
229                         new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 } ),
230                         -1, null));
231         this.httpclient.execute(target, httppost, context);
232     }
233 
234     @Test
235     public void testNonCompliantURI() throws Exception {
236         this.server.registerHandler("*", new SimpleService());
237 
238         final HttpHost target = start();
239 
240         final HttpClientContext context = HttpClientContext.create();
241         final ClassicHttpRequest request = new BasicClassicHttpRequest("GET", "{{|boom|}}");
242         final ClassicHttpResponse response = this.httpclient.execute(target, request, context);
243         EntityUtils.consume(response.getEntity());
244 
245         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
246 
247         final HttpRequest reqWrapper = context.getRequest();
248 
249         Assert.assertEquals("{{|boom|}}", reqWrapper.getRequestUri());
250     }
251 
252     @Test
253     public void testRelativeRequestURIWithFragment() throws Exception {
254         this.server.registerHandler("*", new SimpleService());
255 
256         final HttpHost target = start();
257 
258         final HttpGet httpget = new HttpGet("/stuff#blahblah");
259         final HttpClientContext context = HttpClientContext.create();
260 
261         final ClassicHttpResponse response = this.httpclient.execute(target, httpget, context);
262         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
263         EntityUtils.consume(response.getEntity());
264 
265         final HttpRequest request = context.getRequest();
266         Assert.assertEquals("/stuff", request.getRequestUri());
267     }
268 
269     @Test
270     public void testAbsoluteRequestURIWithFragment() throws Exception {
271         this.server.registerHandler("*", new SimpleService());
272 
273         final HttpHost target = start();
274 
275         final URI uri = new URIBuilder()
276             .setHost(target.getHostName())
277             .setPort(target.getPort())
278             .setScheme(target.getSchemeName())
279             .setPath("/stuff")
280             .setFragment("blahblah")
281             .build();
282 
283         final HttpGet httpget = new HttpGet(uri);
284         final HttpClientContext context = HttpClientContext.create();
285 
286         final ClassicHttpResponse response = this.httpclient.execute(httpget, context);
287         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
288         EntityUtils.consume(response.getEntity());
289 
290         final HttpRequest request = context.getRequest();
291         Assert.assertEquals("/stuff", request.getRequestUri());
292 
293         final RedirectLocations redirectLocations = context.getRedirectLocations();
294         final URI location = URIUtils.resolve(uri, target, redirectLocations.getAll());
295         Assert.assertEquals(uri, location);
296     }
297 
298     @Test
299     public void testRequestCancellation() throws Exception {
300         this.connManager.setDefaultMaxPerRoute(1);
301         this.connManager.setMaxTotal(1);
302 
303         final HttpHost target = start();
304 
305         final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
306         try {
307 
308             for (int i = 0; i < 20; i++) {
309                 final HttpGet httpget = new HttpGet("/random/1000");
310 
311                 executorService.schedule(new Runnable() {
312 
313                     @Override
314                     public void run() {
315                         httpget.cancel();
316                     }
317                 }, 1, TimeUnit.MILLISECONDS);
318 
319                 try (final ClassicHttpResponse response = this.httpclient.execute(target, httpget)) {
320                     EntityUtils.consume(response.getEntity());
321                 } catch (final Exception ignore) {
322                 }
323             }
324 
325             final Random rnd = new Random();
326             for (int i = 0; i < 20; i++) {
327                 final HttpGet httpget = new HttpGet("/random/1000");
328 
329                 executorService.schedule(new Runnable() {
330 
331                     @Override
332                     public void run() {
333                         httpget.cancel();
334                     }
335                 }, rnd.nextInt(200), TimeUnit.MILLISECONDS);
336 
337                 try (final ClassicHttpResponse response = this.httpclient.execute(target, httpget)) {
338                     EntityUtils.consume(response.getEntity());
339                 } catch (final Exception ignore) {
340                 }
341 
342             }
343 
344             for (int i = 0; i < 5; i++) {
345                 final HttpGet httpget = new HttpGet("/random/1000");
346                 try (final ClassicHttpResponse response = this.httpclient.execute(target, httpget)) {
347                     EntityUtils.consume(response.getEntity());
348                 }
349             }
350 
351         } finally {
352             executorService.shutdownNow();
353         }
354     }
355 
356 }