Skip to content

Commit 2554bfa

Browse files
authored
add large response test (#2051)
1 parent 34648ef commit 2554bfa

File tree

1 file changed

+158
-0
lines changed

1 file changed

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

0 commit comments

Comments
 (0)