Skip to content

Commit 0d24a11

Browse files
committed
large response test
1 parent 34648ef commit 0d24a11

File tree

1 file changed

+160
-0
lines changed

1 file changed

+160
-0
lines changed
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/*
2+
* Copyright (c) 2015-2025 AsyncHttpClient Project. All rights reserved.
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+
package org.asynchttpclient;
17+
18+
import com.sun.net.httpserver.HttpExchange;
19+
import com.sun.net.httpserver.HttpHandler;
20+
import com.sun.net.httpserver.HttpServer;
21+
import io.github.nettyplus.leakdetector.junit.NettyLeakDetectorExtension;
22+
import io.netty.handler.codec.http.HttpHeaders;
23+
import java.io.IOException;
24+
import java.io.OutputStream;
25+
import java.net.InetSocketAddress;
26+
import java.nio.charset.StandardCharsets;
27+
import java.time.Duration;
28+
import java.util.concurrent.TimeUnit;
29+
import java.util.concurrent.atomic.AtomicInteger;
30+
import java.util.concurrent.atomic.AtomicLong;
31+
import org.apache.commons.io.FileUtils;
32+
import org.jetbrains.annotations.Nullable;
33+
import org.junit.jupiter.api.AfterAll;
34+
import org.junit.jupiter.api.BeforeAll;
35+
import org.junit.jupiter.api.Test;
36+
import org.junit.jupiter.api.Timeout;
37+
import org.junit.jupiter.api.extension.ExtendWith;
38+
import org.slf4j.Logger;
39+
import org.slf4j.LoggerFactory;
40+
41+
import static org.junit.jupiter.api.Assertions.assertEquals;
42+
43+
44+
@ExtendWith(NettyLeakDetectorExtension.class)
45+
public class LargeResponseTest {
46+
private static final Logger LOG = LoggerFactory.getLogger(LargeResponseTest.class);
47+
private static final int textSize = 4 * 1024;
48+
private static final byte[] textBytes = "z".repeat(textSize).getBytes(StandardCharsets.UTF_8);
49+
50+
private static final long responseSize = ((long)textSize) * (1_500_000L);
51+
52+
private static HttpServer HTTP_SERVER;
53+
54+
private static AsyncHttpClient createClient() {
55+
AsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
56+
.setEnableAutomaticDecompression(true)
57+
.setCompressionEnforced(true)
58+
.setReadTimeout(Duration.ofMinutes(15))
59+
.setRequestTimeout(Duration.ofMinutes(15))
60+
.setConnectTimeout(Duration.ofSeconds(1))
61+
.build();
62+
return new DefaultAsyncHttpClient(config);
63+
}
64+
65+
@BeforeAll
66+
static void setupServer() throws Exception {
67+
HTTP_SERVER = HttpServer.create(new InetSocketAddress(0), 0);
68+
69+
HTTP_SERVER.createContext("/large").setHandler(new HttpHandler() {
70+
@Override
71+
public void handle(HttpExchange exchange)
72+
throws IOException {
73+
exchange.sendResponseHeaders(200, 0);
74+
long bytesWritten = 0;
75+
OutputStream out = exchange.getResponseBody();
76+
while (bytesWritten < responseSize) {
77+
out.write(textBytes);
78+
out.flush();
79+
bytesWritten += textBytes.length;
80+
}
81+
out.close();
82+
}
83+
});
84+
85+
HTTP_SERVER.start();
86+
}
87+
88+
@AfterAll
89+
static void stopServer() {
90+
if (HTTP_SERVER != null) {
91+
HTTP_SERVER.stop(0);
92+
}
93+
}
94+
95+
@Test
96+
@Timeout(value = 15, unit = TimeUnit.MINUTES)
97+
void handleLargeResponse() throws Throwable {
98+
AtomicInteger status = new AtomicInteger(-1);
99+
AtomicInteger throwableCount = new AtomicInteger();
100+
AtomicLong bytesReceived = new AtomicLong();
101+
AtomicLong bodyPartCount = new AtomicLong();
102+
103+
try (AsyncHttpClient client = createClient()) {
104+
Request request = new RequestBuilder("GET")
105+
.setUrl("http://localhost:" + HTTP_SERVER.getAddress().getPort() + "/large")
106+
.build();
107+
var future = client.executeRequest(request, new AsyncHandler<Object>() {
108+
@Override
109+
public State onStatusReceived(HttpResponseStatus responseStatus)
110+
throws Exception {
111+
status.set(responseStatus.getStatusCode());
112+
return State.CONTINUE;
113+
}
114+
115+
@Override
116+
public State onHeadersReceived(HttpHeaders headers)
117+
throws Exception {
118+
return State.CONTINUE;
119+
}
120+
121+
@Override
122+
public State onBodyPartReceived(HttpResponseBodyPart bodyPart)
123+
throws Exception {
124+
bodyPartCount.incrementAndGet();
125+
bytesReceived.addAndGet(bodyPart.length());
126+
127+
/*
128+
LOG.info("onBodyPartReceived: "
129+
+ "bodyPartCount=" + bodyPartCount.get()
130+
+ " bodyPartLength: "
131+
+ FileUtils.byteCountToDisplaySize(bodyPart.length()));
132+
*/
133+
134+
return State.CONTINUE;
135+
}
136+
137+
@Override
138+
public void onThrowable(Throwable t) {
139+
throwableCount.incrementAndGet();
140+
}
141+
142+
@Override
143+
public @Nullable Object onCompleted()
144+
throws Exception {
145+
return null;
146+
}
147+
});
148+
149+
future.get(15, TimeUnit.MINUTES);
150+
151+
assertEquals(200, status.get());
152+
assertEquals(0, throwableCount.get());
153+
assertEquals(responseSize, bytesReceived.get());
154+
155+
LOG.info("Body part count: " + bodyPartCount);
156+
LOG.info("Body part average size: " + FileUtils.byteCountToDisplaySize(responseSize / bodyPartCount.get()));
157+
LOG.info("Response size: " + FileUtils.byteCountToDisplaySize(responseSize));
158+
}
159+
}
160+
}

0 commit comments

Comments
 (0)