Skip to content

Commit 4b8dc67

Browse files
Spikhalskiyslandelle
authored andcommitted
Add built-in support for InputStream body with defined Content-Length (AsyncHttpClient#1282)
Current state: Hi, now if you pass InputStream as a body source - you always get request with chunked encoding. Motivation: Provide built-in ability to send requests with static Content-Length if we have fully in-memory InputStream (like ByteArrayInputStream) + length. Changes: Adopt InputStreamBodyGenerator and supporting code to work with configurable contentLength.
1 parent bf890be commit 4b8dc67

File tree

4 files changed

+104
-5
lines changed

4 files changed

+104
-5
lines changed

client/src/main/java/org/asynchttpclient/netty/request/NettyRequestFactory.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ private NettyBody body(Request request, boolean connect) {
103103
nettyBody = new NettyFileBody(fileBodyGenerator.getFile(), fileBodyGenerator.getRegionSeek(), fileBodyGenerator.getRegionLength(), config);
104104

105105
} else if (request.getBodyGenerator() instanceof InputStreamBodyGenerator) {
106-
nettyBody = new NettyInputStreamBody(InputStreamBodyGenerator.class.cast(request.getBodyGenerator()).getInputStream());
106+
InputStreamBodyGenerator inStreamGenerator = InputStreamBodyGenerator.class.cast(request.getBodyGenerator());
107+
nettyBody = new NettyInputStreamBody(inStreamGenerator.getInputStream(), inStreamGenerator.getContentLength());
107108

108109
} else if (request.getBodyGenerator() instanceof ReactiveStreamsBodyGenerator) {
109110
ReactiveStreamsBodyGenerator reactiveStreamsBodyGenerator = (ReactiveStreamsBodyGenerator)request.getBodyGenerator();

client/src/main/java/org/asynchttpclient/netty/request/body/NettyInputStreamBody.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,15 @@ public class NettyInputStreamBody implements NettyBody {
3333
private static final Logger LOGGER = LoggerFactory.getLogger(NettyInputStreamBody.class);
3434

3535
private final InputStream inputStream;
36+
private final long contentLength;
3637

3738
public NettyInputStreamBody(InputStream inputStream) {
39+
this(inputStream, -1L);
40+
}
41+
42+
public NettyInputStreamBody(InputStream inputStream, long contentLength) {
3843
this.inputStream = inputStream;
44+
this.contentLength = contentLength;
3945
}
4046

4147
public InputStream getInputStream() {
@@ -44,7 +50,7 @@ public InputStream getInputStream() {
4450

4551
@Override
4652
public long getContentLength() {
47-
return -1L;
53+
return contentLength;
4854
}
4955

5056
@Override

client/src/main/java/org/asynchttpclient/request/body/generator/InputStreamBodyGenerator.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,34 +32,46 @@ public final class InputStreamBodyGenerator implements BodyGenerator {
3232

3333
private static final Logger LOGGER = LoggerFactory.getLogger(InputStreamBody.class);
3434
private final InputStream inputStream;
35+
private final long contentLength;
3536

3637
public InputStreamBodyGenerator(InputStream inputStream) {
38+
this(inputStream, -1L);
39+
}
40+
41+
public InputStreamBodyGenerator(InputStream inputStream, long contentLength) {
3742
this.inputStream = inputStream;
43+
this.contentLength = contentLength;
3844
}
3945

4046
public InputStream getInputStream() {
4147
return inputStream;
4248
}
4349

50+
public long getContentLength() {
51+
return contentLength;
52+
}
53+
4454
/**
4555
* {@inheritDoc}
4656
*/
4757
@Override
4858
public Body createBody() {
49-
return new InputStreamBody(inputStream);
59+
return new InputStreamBody(inputStream, contentLength);
5060
}
5161

5262
private class InputStreamBody implements Body {
5363

5464
private final InputStream inputStream;
65+
private final long contentLength;
5566
private byte[] chunk;
5667

57-
private InputStreamBody(InputStream inputStream) {
68+
private InputStreamBody(InputStream inputStream, long contentLength) {
5869
this.inputStream = inputStream;
70+
this.contentLength = contentLength;
5971
}
6072

6173
public long getContentLength() {
62-
return -1L;
74+
return contentLength;
6375
}
6476

6577
public BodyState transferTo(ByteBuf target) throws IOException {

client/src/test/java/org/asynchttpclient/BasicHttpTest.java

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import io.netty.handler.codec.http.HttpHeaders;
2626

2727
import java.io.ByteArrayInputStream;
28+
import java.io.IOException;
29+
import java.io.InputStream;
2830
import java.net.ConnectException;
2931
import java.net.UnknownHostException;
3032
import java.nio.charset.StandardCharsets;
@@ -42,14 +44,20 @@
4244
import java.util.concurrent.atomic.AtomicReference;
4345

4446
import javax.net.ssl.SSLException;
47+
import javax.servlet.ServletException;
48+
import javax.servlet.http.HttpServletRequest;
49+
import javax.servlet.http.HttpServletResponse;
4550

4651
import org.asynchttpclient.cookie.Cookie;
4752
import org.asynchttpclient.handler.MaxRedirectException;
53+
import org.asynchttpclient.request.body.generator.InputStreamBodyGenerator;
4854
import org.asynchttpclient.request.body.multipart.StringPart;
4955
import org.asynchttpclient.test.EventCollectingHandler;
5056
import org.asynchttpclient.test.TestUtils.AsyncCompletionHandlerAdapter;
5157
import org.asynchttpclient.testserver.HttpServer;
58+
import org.asynchttpclient.testserver.HttpServer.EchoHandler;
5259
import org.asynchttpclient.testserver.HttpTest;
60+
import org.eclipse.jetty.server.handler.AbstractHandler;
5361
import org.testng.annotations.AfterClass;
5462
import org.testng.annotations.BeforeClass;
5563
import org.testng.annotations.Test;
@@ -911,4 +919,76 @@ public void requestingPlainHttpEndpointOverHttpsThrowsSslException() throws Thro
911919
});
912920
});
913921
}
922+
923+
@Test
924+
public void postUnboundedInputStreamAsBodyStream() throws Throwable {
925+
withClient().run(client -> {
926+
withServer(server).run(server -> {
927+
HttpHeaders h = new DefaultHttpHeaders();
928+
h.add(CONTENT_TYPE, APPLICATION_JSON);
929+
server.enqueue(new AbstractHandler() {
930+
EchoHandler chain = new EchoHandler();
931+
@Override
932+
public void handle(String target, org.eclipse.jetty.server.Request request,
933+
HttpServletRequest httpServletRequest,
934+
HttpServletResponse httpServletResponse) throws IOException, ServletException {
935+
assertEquals(request.getHeader(TRANSFER_ENCODING), CHUNKED);
936+
assertNull(request.getHeader(CONTENT_LENGTH));
937+
chain.handle(target, request, httpServletRequest, httpServletResponse);
938+
}
939+
});
940+
server.enqueueEcho();
941+
942+
client.preparePost(getTargetUrl())//
943+
.setHeaders(h)//
944+
.setBody(new ByteArrayInputStream("{}".getBytes(StandardCharsets.ISO_8859_1)))//
945+
.execute(new AsyncCompletionHandlerAdapter() {
946+
@Override
947+
public Response onCompleted(Response response) throws Exception {
948+
assertEquals(response.getStatusCode(), 200);
949+
assertEquals(response.getResponseBody(), "{}");
950+
return response;
951+
}
952+
}).get(TIMEOUT, SECONDS);
953+
});
954+
});
955+
}
956+
957+
@Test
958+
public void postInputStreamWithContentLengthAsBodyGenerator() throws Throwable {
959+
withClient().run(client -> {
960+
withServer(server).run(server -> {
961+
HttpHeaders h = new DefaultHttpHeaders();
962+
h.add(CONTENT_TYPE, APPLICATION_JSON);
963+
server.enqueue(new AbstractHandler() {
964+
EchoHandler chain = new EchoHandler();
965+
@Override
966+
public void handle(String target, org.eclipse.jetty.server.Request request,
967+
HttpServletRequest httpServletRequest,
968+
HttpServletResponse httpServletResponse) throws IOException, ServletException {
969+
assertNull(request.getHeader(TRANSFER_ENCODING));
970+
assertEquals(request.getHeader(CONTENT_LENGTH),//
971+
Integer.toString("{}".getBytes(StandardCharsets.ISO_8859_1).length));
972+
chain.handle(target, request, httpServletRequest, httpServletResponse);
973+
}
974+
});
975+
976+
byte[] bodyBytes = "{}".getBytes(StandardCharsets.ISO_8859_1);
977+
InputStream bodyStream = new ByteArrayInputStream(bodyBytes);
978+
979+
client.preparePost(getTargetUrl())//
980+
.setHeaders(h)//
981+
.setBody(new InputStreamBodyGenerator(bodyStream, bodyBytes.length))//
982+
.execute(new AsyncCompletionHandlerAdapter() {
983+
984+
@Override
985+
public Response onCompleted(Response response) throws Exception {
986+
assertEquals(response.getStatusCode(), 200);
987+
assertEquals(response.getResponseBody(), "{}");
988+
return response;
989+
}
990+
}).get(TIMEOUT, SECONDS);
991+
});
992+
});
993+
}
914994
}

0 commit comments

Comments
 (0)