|
28 | 28 | import com.google.api.client.util.ByteArrayStreamingContent; |
29 | 29 | import java.io.IOException; |
30 | 30 | import java.nio.charset.StandardCharsets; |
| 31 | +import java.util.concurrent.CountDownLatch; |
| 32 | +import java.util.concurrent.ExecutorService; |
| 33 | +import java.util.concurrent.Executors; |
31 | 34 | import java.util.concurrent.TimeUnit; |
32 | 35 | import java.util.concurrent.atomic.AtomicBoolean; |
33 | 36 | import java.util.concurrent.atomic.AtomicInteger; |
|
36 | 39 | import org.apache.hc.client5.http.classic.HttpClient; |
37 | 40 | import org.apache.hc.client5.http.config.RequestConfig; |
38 | 41 | import org.apache.hc.client5.http.impl.classic.HttpClients; |
39 | | -import org.apache.hc.client5.http.protocol.HttpClientContext; |
| 42 | +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; |
40 | 43 | import org.apache.hc.core5.http.ClassicHttpRequest; |
41 | 44 | import org.apache.hc.core5.http.ClassicHttpResponse; |
| 45 | +import org.apache.hc.core5.http.ConnectionRequestTimeoutException; |
42 | 46 | import org.apache.hc.core5.http.ContentType; |
43 | 47 | import org.apache.hc.core5.http.EntityDetails; |
44 | 48 | import org.apache.hc.core5.http.Header; |
|
59 | 63 | import org.apache.hc.core5.http.io.support.BasicHttpServerRequestHandler; |
60 | 64 | import org.apache.hc.core5.http.protocol.HttpContext; |
61 | 65 | import org.apache.hc.core5.http.protocol.HttpProcessor; |
62 | | -import org.apache.hc.core5.util.Timeout; |
63 | 66 | import org.junit.Assert; |
64 | 67 | import org.junit.Test; |
65 | 68 |
|
@@ -212,39 +215,67 @@ public void testConnectTimeout() { |
212 | 215 | } |
213 | 216 | } |
214 | 217 |
|
215 | | - @Test |
216 | | - public void testDefaultRequestConfig() throws IOException { |
217 | | - RequestConfig requestConfig = |
218 | | - RequestConfig.custom() |
219 | | - .setConnectTimeout(100, TimeUnit.MILLISECONDS) |
220 | | - .setConnectionRequestTimeout(200, TimeUnit.MILLISECONDS) |
221 | | - .setResponseTimeout(300, TimeUnit.MILLISECONDS) |
222 | | - .build(); |
223 | | - final AtomicBoolean interceptorCalled = new AtomicBoolean(false); |
224 | | - HttpClient client = |
225 | | - HttpClients.custom() |
226 | | - .addRequestInterceptorFirst( |
227 | | - (request, entity, context) -> { |
228 | | - HttpClientContext clientContext = HttpClientContext.adapt(context); |
229 | | - RequestConfig config = clientContext.getRequestConfig(); |
230 | | - assertEquals(Timeout.of(100, TimeUnit.MILLISECONDS), config.getConnectTimeout()); |
231 | | - assertEquals( |
232 | | - Timeout.of(200, TimeUnit.MILLISECONDS), config.getConnectionRequestTimeout()); |
233 | | - assertEquals(Timeout.of(300, TimeUnit.MILLISECONDS), config.getResponseTimeout()); |
234 | | - interceptorCalled.set(true); |
235 | | - throw new IOException("cancelling request"); |
236 | | - }) |
237 | | - .build(); |
| 218 | + @Test(timeout = 5000) |
| 219 | + public void testConnectionRequestTimeoutFromDefaultRequestConfig() throws Exception { |
| 220 | + final CountDownLatch latch = new CountDownLatch(1); |
| 221 | + final HttpRequestHandler handler = |
| 222 | + new HttpRequestHandler() { |
| 223 | + @Override |
| 224 | + public void handle( |
| 225 | + ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context) |
| 226 | + throws HttpException, IOException { |
| 227 | + try { |
| 228 | + latch.await(); // Wait for the signal to proceed |
| 229 | + } catch (InterruptedException e) { |
| 230 | + Thread.currentThread().interrupt(); |
| 231 | + } |
| 232 | + response.setCode(HttpStatus.SC_OK); |
| 233 | + } |
| 234 | + }; |
238 | 235 |
|
239 | | - Apache5HttpTransport transport = new Apache5HttpTransport(client, requestConfig, false); |
240 | | - Apache5HttpRequest request = transport.buildRequest("GET", "https://google.com"); |
241 | | - try { |
242 | | - request.execute(); |
243 | | - fail("should not actually make the request"); |
244 | | - } catch (IOException exception) { |
245 | | - assertEquals("cancelling request", exception.getMessage()); |
| 236 | + try (FakeServer server = new FakeServer(handler)) { |
| 237 | + PoolingHttpClientConnectionManager connectionManager = |
| 238 | + new PoolingHttpClientConnectionManager(); |
| 239 | + connectionManager.setMaxTotal(1); // Only one connection in the pool |
| 240 | + |
| 241 | + RequestConfig requestConfig = |
| 242 | + RequestConfig.custom().setConnectionRequestTimeout(1, TimeUnit.SECONDS).build(); |
| 243 | + |
| 244 | + HttpClient httpClient = |
| 245 | + Apache5HttpTransport.newDefaultHttpClientBuilder() |
| 246 | + .setConnectionManager(connectionManager) |
| 247 | + .setDefaultRequestConfig(requestConfig) |
| 248 | + .build(); |
| 249 | + |
| 250 | + HttpTransport transport = new Apache5HttpTransport(httpClient, requestConfig, false); |
| 251 | + final GenericUrl url = new GenericUrl("http://localhost:" + server.getPort()); |
| 252 | + |
| 253 | + ExecutorService executor = Executors.newFixedThreadPool(2); |
| 254 | + |
| 255 | + // First request takes the only connection |
| 256 | + executor.submit( |
| 257 | + () -> { |
| 258 | + try { |
| 259 | + transport.createRequestFactory().buildGetRequest(url).execute(); |
| 260 | + } catch (IOException e) { |
| 261 | + // This request might fail if the test finishes before it completes, which is fine. |
| 262 | + } |
| 263 | + }); |
| 264 | + |
| 265 | + // Give the first request time to acquire the connection |
| 266 | + Thread.sleep(100); |
| 267 | + |
| 268 | + // Second request should time out waiting for a connection |
| 269 | + try { |
| 270 | + transport.createRequestFactory().buildGetRequest(url).execute(); |
| 271 | + fail("Should have thrown ConnectionRequestTimeoutException"); |
| 272 | + } catch (ConnectionRequestTimeoutException e) { |
| 273 | + // Expected |
| 274 | + } finally { |
| 275 | + latch.countDown(); // Allow the first request to complete |
| 276 | + executor.shutdownNow(); |
| 277 | + } |
246 | 278 | } |
247 | | - assertTrue("Expected to have called our test interceptor", interceptorCalled.get()); |
248 | 279 | } |
249 | 280 |
|
250 | 281 | private static class FakeServer implements AutoCloseable { |
|
0 commit comments