diff --git a/pom.xml b/pom.xml index 969987fe2..0e98d71fb 100644 --- a/pom.xml +++ b/pom.xml @@ -444,7 +444,7 @@ org.apache.httpcomponents.client5 httpclient5 - 5.3.1 + 5.6.3 com.nimbusds diff --git a/src/main/java/com/google/firebase/internal/ApacheHttp2Request.java b/src/main/java/com/google/firebase/internal/ApacheHttp2Request.java index ceb213c52..b06692ee8 100644 --- a/src/main/java/com/google/firebase/internal/ApacheHttp2Request.java +++ b/src/main/java/com/google/firebase/internal/ApacheHttp2Request.java @@ -49,6 +49,7 @@ final class ApacheHttp2Request extends LowLevelHttpRequest { private SimpleHttpRequest request; private final RequestConfig.Builder requestConfig; private int writeTimeout; + private int readTimeout; private ApacheHttp2AsyncEntityProducer entityProducer; private ApacheHttp2AsyncEntityConsumer entityConsumer; @@ -57,6 +58,7 @@ final class ApacheHttp2Request extends LowLevelHttpRequest { this.httpAsyncClient = httpAsyncClient; this.requestBuilder = requestBuilder; this.writeTimeout = 0; + this.readTimeout = 0; this.requestConfig = RequestConfig.custom() .setRedirectsEnabled(false); @@ -69,6 +71,7 @@ public void addHeader(String name, String value) { @Override public void setTimeout(int connectionTimeout, int readTimeout) throws IOException { + this.readTimeout = readTimeout; requestConfig .setConnectTimeout(Timeout.ofMilliseconds(connectionTimeout)) .setResponseTimeout(Timeout.ofMilliseconds(readTimeout)); @@ -125,7 +128,10 @@ public void cancelled() { // Wait for response try { - final Message response = responseFuture.get(); + final Message response = + readTimeout > 0 + ? responseFuture.get(readTimeout, TimeUnit.MILLISECONDS) + : responseFuture.get(); return new ApacheHttp2Response(response); } catch (ExecutionException e) { if (e.getCause() instanceof ConnectTimeoutException @@ -142,6 +148,9 @@ public void cancelled() { throw new IOException("Request Interrupted", e); } catch (CancellationException e) { throw new IOException("Request Cancelled", e); + } catch (TimeoutException e) { + responseFuture.cancel(true); + throw new IOException("Stream exception in request", e); } } diff --git a/src/test/java/com/google/firebase/internal/ApacheHttp2TransportTest.java b/src/test/java/com/google/firebase/internal/ApacheHttp2TransportTest.java index ba604d3d1..e6d80847a 100644 --- a/src/test/java/com/google/firebase/internal/ApacheHttp2TransportTest.java +++ b/src/test/java/com/google/firebase/internal/ApacheHttp2TransportTest.java @@ -58,17 +58,16 @@ import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.impl.bootstrap.HttpServer; -import org.apache.hc.core5.http.impl.io.HttpService; +import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap; import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.io.entity.ByteArrayEntity; -import org.apache.hc.core5.http.io.support.BasicHttpServerRequestHandler; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.AsyncRequestProducer; import org.apache.hc.core5.http.nio.AsyncResponseConsumer; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.protocol.HttpContext; -import org.apache.hc.core5.http.protocol.HttpProcessor; +import org.apache.hc.core5.http.protocol.HttpProcessorBuilder; import org.junit.Assert; import org.junit.Test; @@ -423,6 +422,39 @@ public void handle( } } + @Test + public void testReadTimeout() throws Exception { + final HttpRequestHandler handler = new HttpRequestHandler() { + @Override + public void handle( + ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context) + throws HttpException, IOException { + try { + Thread.sleep(1000L); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + response.setCode(HttpStatus.SC_OK); + } + }; + + try (FakeServer server = new FakeServer(handler)) { + HttpTransport transport = new ApacheHttp2Transport(); + GenericUrl testUrl = new GenericUrl("http://localhost/timeout"); + testUrl.setPort(server.getPort()); + com.google.api.client.http.HttpRequest request = transport.createRequestFactory() + .buildGetRequest(testUrl); + request.setReadTimeout(100); + + try { + request.execute(); + Assert.fail("Expected IOException on read timeout"); + } catch (IOException e) { + assertEquals("Stream exception in request", e.getMessage()); + } + } + } + private static class FakeServer implements AutoCloseable { private final HttpServer server; @@ -434,31 +466,11 @@ public HttpRequestHandler resolve(HttpRequest request, HttpContext context) return httpHandler; } }; - server = new HttpServer( - 0, - HttpService.builder() - .withHttpProcessor( - new HttpProcessor() { - @Override - public void process( - HttpRequest request, EntityDetails entity, HttpContext context) - throws HttpException, IOException { - } - - @Override - public void process( - HttpResponse response, EntityDetails entity, HttpContext context) - throws HttpException, IOException { - } - }) - .withHttpServerRequestHandler(new BasicHttpServerRequestHandler(mapper)) - .build(), - null, - null, - null, - null, - null, - null); + server = ServerBootstrap.bootstrap() + .setListenerPort(0) + .setHttpProcessor(HttpProcessorBuilder.create().build()) + .setRequestRouter(mapper) + .create(); server.start(); }