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();
}