Skip to content

Commit ff1e4fb

Browse files
committed
Mirror tests from google java client and added more descriptive error messages.
1 parent a86c325 commit ff1e4fb

8 files changed

+927
-91
lines changed

src/main/java/com/google/firebase/internal/ApacheHttp2AsyncEntityProducer.java

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,23 @@
1+
/*
2+
* Copyright 2024 Google Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
117
package com.google.firebase.internal;
218

319
import com.google.api.client.util.StreamingContent;
20+
import com.google.common.annotations.VisibleForTesting;
421

522
import java.io.ByteArrayOutputStream;
623
import java.io.IOException;
@@ -15,8 +32,9 @@
1532

1633
@SuppressWarnings("deprecation")
1734
public class ApacheHttp2AsyncEntityProducer implements AsyncEntityProducer {
18-
private final ByteBuffer bytebuf;
19-
private ByteArrayOutputStream baos = new ByteArrayOutputStream();
35+
private ByteBuffer bytebuf;
36+
private ByteArrayOutputStream baos;
37+
private final StreamingContent content;
2038
private final ContentType contentType;
2139
private final long contentLength;
2240
private final String contentEncoding;
@@ -25,19 +43,14 @@ public class ApacheHttp2AsyncEntityProducer implements AsyncEntityProducer {
2543

2644
public ApacheHttp2AsyncEntityProducer(StreamingContent content, ContentType contentType,
2745
String contentEncoding, long contentLength, CompletableFuture<Void> writeFuture) {
28-
this.writeFuture = writeFuture;
29-
30-
if (content != null) {
31-
try {
32-
content.writeTo(baos);
33-
} catch (IOException e) {
34-
writeFuture.completeExceptionally(e);
35-
}
36-
}
37-
this.bytebuf = ByteBuffer.wrap(baos.toByteArray());
46+
this.content = content;
3847
this.contentType = contentType;
39-
this.contentLength = contentLength;
4048
this.contentEncoding = contentEncoding;
49+
this.contentLength = contentLength;
50+
this.writeFuture = writeFuture;
51+
this.bytebuf = null;
52+
53+
this.baos = new ByteArrayOutputStream((int) (contentLength < 0 ? 0 : contentLength));
4154
this.exception = new AtomicReference<>();
4255
}
4356

@@ -53,7 +66,7 @@ public ApacheHttp2AsyncEntityProducer(ApacheHttp2Request request,
5366

5467
@Override
5568
public boolean isRepeatable() {
56-
return false;
69+
return true;
5770
}
5871

5972
@Override
@@ -78,7 +91,7 @@ public String getContentEncoding() {
7891

7992
@Override
8093
public boolean isChunked() {
81-
return false;
94+
return contentLength == -1;
8295
}
8396

8497
@Override
@@ -88,12 +101,27 @@ public Set<String> getTrailerNames() {
88101

89102
@Override
90103
public void produce(DataStreamChannel channel) throws IOException {
104+
if (bytebuf == null) {
105+
if (content != null) {
106+
try {
107+
content.writeTo(baos);
108+
} catch (IOException e) {
109+
writeFuture.completeExceptionally(e);
110+
// failed(e);
111+
}
112+
}
113+
114+
this.bytebuf = ByteBuffer.wrap(baos.toByteArray());
115+
}
116+
91117
if (bytebuf.hasRemaining()) {
92118
channel.write(bytebuf);
93119
}
120+
94121
if (!bytebuf.hasRemaining()) {
95122
channel.endStream();
96123
writeFuture.complete(null);
124+
releaseResources();
97125
}
98126
}
99127

@@ -113,4 +141,9 @@ public final Exception getException() {
113141
public void releaseResources() {
114142
bytebuf.clear();
115143
}
144+
145+
@VisibleForTesting
146+
ByteBuffer getBytebuf() {
147+
return bytebuf;
148+
}
116149
}
Lines changed: 67 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,34 @@
1+
/*
2+
* Copyright 2024 Google Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
117
package com.google.firebase.internal;
218

319
import com.google.api.client.http.LowLevelHttpRequest;
420
import com.google.api.client.http.LowLevelHttpResponse;
21+
import com.google.common.annotations.VisibleForTesting;
522

623
import java.io.IOException;
24+
import java.util.concurrent.CancellationException;
725
import java.util.concurrent.CompletableFuture;
826
import java.util.concurrent.ExecutionException;
27+
import java.util.concurrent.Future;
928
import java.util.concurrent.TimeUnit;
1029
import java.util.concurrent.TimeoutException;
1130

31+
import org.apache.hc.client5.http.ConnectTimeoutException;
1232
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
1333
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
1434
import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
@@ -17,6 +37,7 @@
1737
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
1838
import org.apache.hc.core5.concurrent.FutureCallback;
1939
import org.apache.hc.core5.http.nio.support.BasicRequestProducer;
40+
import org.apache.hc.core5.http2.H2StreamResetException;
2041
import org.apache.hc.core5.util.Timeout;
2142

2243
final class ApacheHttp2Request extends LowLevelHttpRequest {
@@ -25,6 +46,7 @@ final class ApacheHttp2Request extends LowLevelHttpRequest {
2546
private SimpleHttpRequest request;
2647
private final RequestConfig.Builder requestConfig;
2748
private int writeTimeout;
49+
private ApacheHttp2AsyncEntityProducer entityProducer;
2850

2951
ApacheHttp2Request(
3052
CloseableHttpAsyncClient httpAsyncClient, SimpleRequestBuilder requestBuilder) {
@@ -33,7 +55,7 @@ final class ApacheHttp2Request extends LowLevelHttpRequest {
3355
this.writeTimeout = 0;
3456

3557
this.requestConfig = RequestConfig.custom()
36-
.setRedirectsEnabled(false);
58+
.setRedirectsEnabled(false);
3759
}
3860

3961
@Override
@@ -42,11 +64,10 @@ public void addHeader(String name, String value) {
4264
}
4365

4466
@Override
45-
@SuppressWarnings("deprecation")
4667
public void setTimeout(int connectionTimeout, int readTimeout) throws IOException {
4768
requestConfig
48-
.setConnectTimeout(Timeout.ofMilliseconds(connectionTimeout))
49-
.setResponseTimeout(Timeout.ofMilliseconds(readTimeout));
69+
.setConnectTimeout(Timeout.ofMilliseconds(connectionTimeout))
70+
.setResponseTimeout(Timeout.ofMilliseconds(readTimeout));
5071
}
5172

5273
@Override
@@ -64,44 +85,58 @@ public LowLevelHttpResponse execute() throws IOException {
6485

6586
// Make Producer
6687
CompletableFuture<Void> writeFuture = new CompletableFuture<>();
67-
ApacheHttp2AsyncEntityProducer entityProducer =
68-
new ApacheHttp2AsyncEntityProducer(this, writeFuture);
88+
entityProducer = new ApacheHttp2AsyncEntityProducer(this, writeFuture);
6989

7090
// Execute
71-
final CompletableFuture<SimpleHttpResponse> responseFuture = new CompletableFuture<>();
91+
final Future<SimpleHttpResponse> responseFuture = httpAsyncClient.execute(
92+
new BasicRequestProducer(request, entityProducer),
93+
SimpleResponseConsumer.create(),
94+
new FutureCallback<SimpleHttpResponse>() {
95+
@Override
96+
public void completed(final SimpleHttpResponse response) {
97+
}
98+
99+
@Override
100+
public void failed(final Exception exception) {
101+
}
102+
103+
@Override
104+
public void cancelled() {
105+
}
106+
});
107+
108+
// Wait for write
72109
try {
73-
httpAsyncClient.execute(
74-
new BasicRequestProducer(request, entityProducer),
75-
SimpleResponseConsumer.create(),
76-
new FutureCallback<SimpleHttpResponse>() {
77-
@Override
78-
public void completed(final SimpleHttpResponse response) {
79-
responseFuture.complete(response);
80-
}
81-
82-
@Override
83-
public void failed(final Exception exception) {
84-
responseFuture.completeExceptionally(exception);
85-
}
86-
87-
@Override
88-
public void cancelled() {
89-
responseFuture.cancel(false);
90-
}
91-
});
92-
93110
if (writeTimeout != 0) {
94111
writeFuture.get(writeTimeout, TimeUnit.MILLISECONDS);
95112
}
113+
} catch (TimeoutException e) {
114+
throw new IOException("Write Timeout", e.getCause());
115+
} catch (Exception e) {
116+
throw new IOException("Exception in write", e.getCause());
117+
}
96118

119+
// Wait for response
120+
try {
97121
final SimpleHttpResponse response = responseFuture.get();
98-
return new ApacheHttp2Response(request, response);
122+
return new ApacheHttp2Response(response);
123+
} catch (ExecutionException e) {
124+
if (e.getCause() instanceof ConnectTimeoutException) {
125+
throw new IOException("Connection Timeout", e.getCause());
126+
} else if (e.getCause() instanceof H2StreamResetException) {
127+
throw new IOException("Stream exception in request", e.getCause());
128+
} else {
129+
throw new IOException("Exception in request", e);
130+
}
99131
} catch (InterruptedException e) {
100132
throw new IOException("Request Interrupted", e);
101-
} catch (ExecutionException e) {
102-
throw new IOException("Exception in request", e);
103-
} catch (TimeoutException e) {
104-
throw new IOException("Timed out", e);
133+
} catch (CancellationException e) {
134+
throw new IOException("Request Cancelled", e);
105135
}
106136
}
137+
138+
@VisibleForTesting
139+
ApacheHttp2AsyncEntityProducer getEntityProducer() {
140+
return entityProducer;
141+
}
107142
}

src/main/java/com/google/firebase/internal/ApacheHttp2Response.java

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,37 @@
1+
/*
2+
* Copyright 2024 Google Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
117
package com.google.firebase.internal;
218

319
import com.google.api.client.http.LowLevelHttpResponse;
20+
import com.google.common.annotations.VisibleForTesting;
421

522
import java.io.ByteArrayInputStream;
623
import java.io.IOException;
724
import java.io.InputStream;
825

9-
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
1026
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
27+
import org.apache.hc.core5.http.ContentType;
1128
import org.apache.hc.core5.http.Header;
1229

1330
public class ApacheHttp2Response extends LowLevelHttpResponse {
14-
1531
private final SimpleHttpResponse response;
1632
private final Header[] allHeaders;
1733

18-
ApacheHttp2Response(SimpleHttpRequest request, SimpleHttpResponse response) {
34+
ApacheHttp2Response(SimpleHttpResponse response) {
1935
this.response = response;
2036
allHeaders = response.getHeaders();
2137
}
@@ -33,20 +49,19 @@ public InputStream getContent() throws IOException {
3349
@Override
3450
public String getContentEncoding() {
3551
Header contentEncodingHeader = response.getFirstHeader("Content-Encoding");
36-
if (contentEncodingHeader == null) {
37-
return null;
38-
}
39-
return contentEncodingHeader.getValue();
52+
return contentEncodingHeader == null ? null : contentEncodingHeader.getValue();
4053
}
4154

4255
@Override
4356
public long getContentLength() {
44-
return response.getBodyText().length();
57+
String bodyText = response.getBodyText();
58+
return bodyText == null ? 0 : bodyText.length();
4559
}
4660

4761
@Override
4862
public String getContentType() {
49-
return response.getContentType().toString();
63+
ContentType contentType = response.getContentType();
64+
return contentType == null ? null : contentType.toString();
5065
}
5166

5267
@Override
@@ -60,11 +75,7 @@ public String getStatusLine() {
6075
}
6176

6277
public String getHeaderValue(String name) {
63-
Header header = response.getLastHeader(name);
64-
if (header == null) {
65-
return null;
66-
}
67-
return header.getValue();
78+
return response.getLastHeader(name).getValue();
6879
}
6980

7081
@Override
@@ -81,4 +92,9 @@ public int getHeaderCount() {
8192
public String getHeaderName(int index) {
8293
return allHeaders[index].getName();
8394
}
95+
96+
@VisibleForTesting
97+
public SimpleHttpResponse getResponse() {
98+
return response;
99+
}
84100
}

0 commit comments

Comments
 (0)