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