Skip to content

Commit b251d43

Browse files
dgolombekDavid Golombekhyperxpro
authored
Extend Request to support ByteBuf inputs (#1952)
* Extend Request to support ByteBuf inputs This extends RequestBuilderBase+Request+NettyRequestFactory to allow providing a Netty ByteBuf as input in an efficient manner, avoiding having to convert that ByteBuf to a ByteBuffer and then back. Fixes #1951 * Fix up test for repeatability --------- Co-authored-by: David Golombek <[email protected]> Co-authored-by: Aayush Atharva <[email protected]>
1 parent e8193ae commit b251d43

File tree

6 files changed

+161
-0
lines changed

6 files changed

+161
-0
lines changed

Diff for: client/src/main/java/org/asynchttpclient/DefaultRequest.java

+9
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.asynchttpclient;
1717

18+
import io.netty.buffer.ByteBuf;
1819
import io.netty.handler.codec.http.HttpHeaders;
1920
import io.netty.handler.codec.http.cookie.Cookie;
2021
import io.netty.resolver.NameResolver;
@@ -51,6 +52,7 @@ public class DefaultRequest implements Request {
5152
private final @Nullable List<byte[]> compositeByteData;
5253
private final @Nullable String stringData;
5354
private final @Nullable ByteBuffer byteBufferData;
55+
private final @Nullable ByteBuf byteBufData;
5456
private final @Nullable InputStream streamData;
5557
private final @Nullable BodyGenerator bodyGenerator;
5658
private final List<Param> formParams;
@@ -79,6 +81,7 @@ public DefaultRequest(String method,
7981
@Nullable List<byte[]> compositeByteData,
8082
@Nullable String stringData,
8183
@Nullable ByteBuffer byteBufferData,
84+
@Nullable ByteBuf byteBufData,
8285
@Nullable InputStream streamData,
8386
@Nullable BodyGenerator bodyGenerator,
8487
List<Param> formParams,
@@ -104,6 +107,7 @@ public DefaultRequest(String method,
104107
this.compositeByteData = compositeByteData;
105108
this.stringData = stringData;
106109
this.byteBufferData = byteBufferData;
110+
this.byteBufData = byteBufData;
107111
this.streamData = streamData;
108112
this.bodyGenerator = bodyGenerator;
109113
this.formParams = formParams;
@@ -176,6 +180,11 @@ public List<Cookie> getCookies() {
176180
return byteBufferData;
177181
}
178182

183+
@Override
184+
public @Nullable ByteBuf getByteBufData() {
185+
return byteBufData;
186+
}
187+
179188
@Override
180189
public @Nullable InputStream getStreamData() {
181190
return streamData;

Diff for: client/src/main/java/org/asynchttpclient/Request.java

+6
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717
package org.asynchttpclient;
1818

19+
import io.netty.buffer.ByteBuf;
1920
import io.netty.handler.codec.http.HttpHeaders;
2021
import io.netty.handler.codec.http.cookie.Cookie;
2122
import io.netty.resolver.NameResolver;
@@ -103,6 +104,11 @@ public interface Request {
103104
*/
104105
@Nullable ByteBuffer getByteBufferData();
105106

107+
/**
108+
* @return the request's body ByteBuf (only non-null if it was set this way)
109+
*/
110+
@Nullable ByteBuf getByteBufData();
111+
106112
/**
107113
* @return the request's body InputStream (only non-null if it was set this way)
108114
*/

Diff for: client/src/main/java/org/asynchttpclient/RequestBuilderBase.java

+12
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.asynchttpclient;
1717

18+
import io.netty.buffer.ByteBuf;
1819
import io.netty.handler.codec.http.DefaultHttpHeaders;
1920
import io.netty.handler.codec.http.HttpHeaders;
2021
import io.netty.handler.codec.http.cookie.Cookie;
@@ -76,6 +77,7 @@ public abstract class RequestBuilderBase<T extends RequestBuilderBase<T>> {
7677
protected @Nullable List<byte[]> compositeByteData;
7778
protected @Nullable String stringData;
7879
protected @Nullable ByteBuffer byteBufferData;
80+
protected @Nullable ByteBuf byteBufData;
7981
protected @Nullable InputStream streamData;
8082
protected @Nullable BodyGenerator bodyGenerator;
8183
protected @Nullable List<Param> formParams;
@@ -121,6 +123,7 @@ protected RequestBuilderBase(Request prototype, boolean disableUrlEncoding, bool
121123
compositeByteData = prototype.getCompositeByteData();
122124
stringData = prototype.getStringData();
123125
byteBufferData = prototype.getByteBufferData();
126+
byteBufData = prototype.getByteBufData();
124127
streamData = prototype.getStreamData();
125128
bodyGenerator = prototype.getBodyGenerator();
126129
if (isNonEmpty(prototype.getFormParams())) {
@@ -361,6 +364,7 @@ public void resetNonMultipartData() {
361364
byteData = null;
362365
compositeByteData = null;
363366
byteBufferData = null;
367+
byteBufData = null;
364368
stringData = null;
365369
streamData = null;
366370
bodyGenerator = null;
@@ -405,6 +409,12 @@ public T setBody(ByteBuffer data) {
405409
return asDerivedType();
406410
}
407411

412+
public T setBody(ByteBuf data) {
413+
resetBody();
414+
byteBufData = data;
415+
return asDerivedType();
416+
}
417+
408418
public T setBody(InputStream stream) {
409419
resetBody();
410420
streamData = stream;
@@ -586,6 +596,7 @@ private RequestBuilderBase<?> executeSignatureCalculator() {
586596
rb.compositeByteData = compositeByteData;
587597
rb.stringData = stringData;
588598
rb.byteBufferData = byteBufferData;
599+
rb.byteBufData = byteBufData;
589600
rb.streamData = streamData;
590601
rb.bodyGenerator = bodyGenerator;
591602
rb.virtualHost = virtualHost;
@@ -647,6 +658,7 @@ public Request build() {
647658
rb.compositeByteData,
648659
rb.stringData,
649660
rb.byteBufferData,
661+
rb.byteBufData,
650662
rb.streamData,
651663
rb.bodyGenerator,
652664
formParamsCopy,

Diff for: client/src/main/java/org/asynchttpclient/netty/request/NettyRequestFactory.java

+3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.asynchttpclient.netty.request.body.NettyBody;
3333
import org.asynchttpclient.netty.request.body.NettyBodyBody;
3434
import org.asynchttpclient.netty.request.body.NettyByteArrayBody;
35+
import org.asynchttpclient.netty.request.body.NettyByteBufBody;
3536
import org.asynchttpclient.netty.request.body.NettyByteBufferBody;
3637
import org.asynchttpclient.netty.request.body.NettyCompositeByteArrayBody;
3738
import org.asynchttpclient.netty.request.body.NettyDirectBody;
@@ -96,6 +97,8 @@ private NettyBody body(Request request) {
9697
nettyBody = new NettyByteBufferBody(StringUtils.charSequence2ByteBuffer(request.getStringData(), bodyCharset));
9798
} else if (request.getByteBufferData() != null) {
9899
nettyBody = new NettyByteBufferBody(request.getByteBufferData());
100+
} else if (request.getByteBufData() != null) {
101+
nettyBody = new NettyByteBufBody(request.getByteBufData());
99102
} else if (request.getStreamData() != null) {
100103
nettyBody = new NettyInputStreamBody(request.getStreamData());
101104
} else if (isNonEmpty(request.getFormParams())) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright (c) 2015-2023 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.netty.request.body;
17+
18+
import io.netty.buffer.ByteBuf;
19+
20+
public class NettyByteBufBody extends NettyDirectBody {
21+
22+
private final ByteBuf bb;
23+
private final CharSequence contentTypeOverride;
24+
private final long length;
25+
26+
public NettyByteBufBody(ByteBuf bb) {
27+
this(bb, null);
28+
}
29+
30+
public NettyByteBufBody(ByteBuf bb, CharSequence contentTypeOverride) {
31+
this.bb = bb;
32+
length = bb.readableBytes();
33+
this.contentTypeOverride = contentTypeOverride;
34+
}
35+
36+
@Override
37+
public long getContentLength() {
38+
return length;
39+
}
40+
41+
@Override
42+
public CharSequence getContentTypeOverride() {
43+
return contentTypeOverride;
44+
}
45+
46+
@Override
47+
public ByteBuf byteBuf() {
48+
return bb;
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright (c) 2010-2012 Sonatype, Inc. All rights reserved.
3+
*
4+
* This program is licensed to you under the Apache License Version 2.0,
5+
* and you may not use this file except in compliance with the Apache License Version 2.0.
6+
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
7+
*
8+
* Unless required by applicable law or agreed to in writing,
9+
* software distributed under the Apache License Version 2.0 is distributed on an
10+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
12+
*/
13+
package org.asynchttpclient.request.body;
14+
15+
import io.github.artsok.RepeatedIfExceptionsTest;
16+
import io.netty.buffer.ByteBuf;
17+
import io.netty.buffer.Unpooled;
18+
import jakarta.servlet.http.HttpServletRequest;
19+
import jakarta.servlet.http.HttpServletResponse;
20+
import org.asynchttpclient.AbstractBasicTest;
21+
import org.asynchttpclient.AsyncHttpClient;
22+
import org.asynchttpclient.Response;
23+
import org.eclipse.jetty.server.Request;
24+
import org.eclipse.jetty.server.handler.AbstractHandler;
25+
26+
import java.io.IOException;
27+
import java.nio.charset.Charset;
28+
import java.time.Duration;
29+
import java.util.Arrays;
30+
31+
import static org.asynchttpclient.Dsl.asyncHttpClient;
32+
import static org.asynchttpclient.Dsl.config;
33+
import static org.junit.jupiter.api.Assertions.assertEquals;
34+
35+
public class PutByteBufTest extends AbstractBasicTest {
36+
37+
private void put(String message) throws Exception {
38+
ByteBuf byteBuf = Unpooled.wrappedBuffer(message.getBytes());
39+
try (AsyncHttpClient client = asyncHttpClient(config().setRequestTimeout(Duration.ofSeconds(2)))) {
40+
Response response = client.preparePut(getTargetUrl()).setBody(byteBuf).execute().get();
41+
assertEquals(response.getStatusCode(), 200);
42+
assertEquals(response.getResponseBody(), message);
43+
}
44+
}
45+
46+
@RepeatedIfExceptionsTest(repeats = 5)
47+
public void testPutSmallBody() throws Exception {
48+
put("Hello Test");
49+
}
50+
51+
@RepeatedIfExceptionsTest(repeats = 5)
52+
public void testPutBigBody() throws Exception {
53+
byte[] array = new byte[2048];
54+
Arrays.fill(array, (byte) 97);
55+
String longString = new String(array, Charset.forName("UTF-8"));
56+
57+
put(longString);
58+
}
59+
60+
@Override
61+
public AbstractHandler configureHandler() throws Exception {
62+
return new AbstractHandler() {
63+
64+
@Override
65+
public void handle(String s, Request request, HttpServletRequest httpRequest, HttpServletResponse response) throws IOException {
66+
int size = 1024;
67+
if (request.getContentLength() > 0) {
68+
size = request.getContentLength();
69+
}
70+
byte[] bytes = new byte[size];
71+
if (bytes.length > 0) {
72+
final int read = request.getInputStream().read(bytes);
73+
response.getOutputStream().write(bytes, 0, read);
74+
}
75+
76+
response.setStatus(200);
77+
response.getOutputStream().flush();
78+
}
79+
};
80+
}
81+
}

0 commit comments

Comments
 (0)