Skip to content

Commit f0a3dce

Browse files
committed
large response test
1 parent 14ee30a commit f0a3dce

File tree

1 file changed

+152
-0
lines changed

1 file changed

+152
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
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 Logger LOG = LoggerFactory.getLogger(LargeResponseTest.class);
47+
private static final int textSize = 200_000;
48+
private static final byte[] textBytes = "z".repeat(textSize).getBytes(StandardCharsets.UTF_8);
49+
50+
private static final long responseSize = ((long)textSize) * (2_000_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+
AtomicLong bytesReceived = new AtomicLong();
100+
AtomicInteger throwableCount = new AtomicInteger();
101+
AtomicInteger bodyPartCount = new AtomicInteger();
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+
return State.CONTINUE;
127+
}
128+
129+
@Override
130+
public void onThrowable(Throwable t) {
131+
throwableCount.incrementAndGet();
132+
}
133+
134+
@Override
135+
public @Nullable Object onCompleted()
136+
throws Exception {
137+
return null;
138+
}
139+
});
140+
141+
future.get(15, TimeUnit.MINUTES);
142+
143+
assertEquals(200, status.get());
144+
assertEquals(0, throwableCount.get());
145+
assertEquals(responseSize, bytesReceived.get());
146+
147+
LOG.info("Body part count: " + bodyPartCount);
148+
LOG.info("Body part average size: " + FileUtils.byteCountToDisplaySize(responseSize / bodyPartCount.get()));
149+
LOG.info("Response size: " + FileUtils.byteCountToDisplaySize(responseSize));
150+
}
151+
}
152+
}

0 commit comments

Comments
 (0)