Skip to content

Commit ec1e350

Browse files
committed
Prototype HttpBodyDecoder
1 parent c3e0902 commit ec1e350

9 files changed

Lines changed: 142 additions & 34 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package io.vertx.core.http.impl;
2+
3+
import io.netty.buffer.ByteBuf;
4+
5+
public interface HttpBodyDecoder {
6+
7+
int handle(ByteBuf content);
8+
9+
void next();
10+
11+
void end();
12+
13+
}

vertx-core/src/main/java/io/vertx/core/http/impl/HttpClientStream.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,8 @@ public interface HttpClientStream extends HttpStream {
4646
HttpClientStream fetch(long amount);
4747

4848
HttpClientStream updatePriority(StreamPriority streamPriority);
49-
49+
@Override
50+
default HttpClientStream bodyDecoder(HttpBodyDecoder decoder) {
51+
return (HttpClientStream)HttpStream.super.bodyDecoder(decoder);
52+
}
5053
}

vertx-core/src/main/java/io/vertx/core/http/impl/HttpServerStream.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ public interface HttpServerStream extends HttpStream {
5454

5555
void sendFile(ChunkedInput<ByteBuf> file, Promise<Void> promise);
5656

57-
5857
HttpServerStream updatePriority(StreamPriority streamPriority);
59-
58+
@Override
59+
default HttpServerStream bodyDecoder(HttpBodyDecoder decoder) {
60+
return (HttpServerStream)HttpStream.super.bodyDecoder(decoder);
61+
}
6062
}

vertx-core/src/main/java/io/vertx/core/http/impl/HttpStream.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,7 @@
1515
import io.vertx.core.Handler;
1616
import io.vertx.core.MultiMap;
1717
import io.vertx.core.buffer.Buffer;
18-
import io.vertx.core.http.HttpConnection;
19-
import io.vertx.core.http.HttpFrame;
20-
import io.vertx.core.http.HttpVersion;
21-
import io.vertx.core.http.StreamPriority;
18+
import io.vertx.core.http.*;
2219
import io.vertx.core.internal.ContextInternal;
2320

2421
/**
@@ -49,6 +46,10 @@ public interface HttpStream {
4946

5047
Future<Boolean> cancel();
5148

49+
default HttpStream bodyDecoder(HttpBodyDecoder decoder) {
50+
throw new UnsupportedOperationException();
51+
}
52+
5253
HttpStream resetHandler(Handler<Long> handler);
5354
HttpStream exceptionHandler(Handler<Throwable> handler);
5455
HttpStream customFrameHandler(Handler<HttpFrame> handler);

vertx-core/src/main/java/io/vertx/core/http/impl/http2/DefaultHttp2Stream.java

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import io.netty.buffer.ByteBufAllocator;
1616
import io.netty.buffer.Unpooled;
1717
import io.netty.channel.EventLoop;
18-
import io.netty.handler.codec.http2.EmptyHttp2Headers;
1918
import io.netty.handler.stream.ChunkedInput;
2019
import io.vertx.core.Future;
2120
import io.vertx.core.Handler;
@@ -28,22 +27,24 @@
2827
import io.vertx.core.http.impl.HttpUtils;
2928
import io.vertx.core.http.impl.headers.HttpHeaders;
3029
import io.vertx.core.http.impl.observability.StreamObserver;
30+
import io.vertx.core.http.impl.HttpBodyDecoder;
3131
import io.vertx.core.internal.ContextInternal;
3232
import io.vertx.core.internal.VertxInternal;
3333
import io.vertx.core.internal.buffer.BufferInternal;
3434
import io.vertx.core.internal.concurrent.InboundMessageQueue;
3535
import io.vertx.core.internal.concurrent.OutboundMessageQueue;
3636
import io.vertx.core.net.impl.MessageWrite;
37+
import io.vertx.core.net.impl.VertxHandler;
3738

3839
/**
3940
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
4041
*/
4142
abstract class DefaultHttp2Stream<S extends DefaultHttp2Stream<S>> implements HttpStream, Http2Stream {
4243

43-
private static final Buffer EMPTY = BufferInternal.buffer(Unpooled.EMPTY_BUFFER);
44+
private static final ByteBuf EMPTY = Unpooled.wrappedBuffer(new byte[1]);
4445

4546
private final OutboundMessageQueue<MessageWrite> outboundQueue;
46-
private final InboundMessageQueue<Buffer> inboundQueue;
47+
private final InboundMessageQueue<ByteBuf> inboundQueue;
4748
private final Http2Connection connection;
4849
protected final VertxInternal vertx;
4950
protected final ContextInternal context;
@@ -77,6 +78,9 @@ abstract class DefaultHttp2Stream<S extends DefaultHttp2Stream<S>> implements Ht
7778
private Handler<StreamPriority> priorityChangeHandler;
7879
private Handler<Void> drainHandler;
7980

81+
//
82+
private HttpBodyDecoder decoder;
83+
8084
DefaultHttp2Stream(Http2Connection connection, ContextInternal context) {
8185
this(-1, connection, context, true);
8286
}
@@ -88,13 +92,23 @@ abstract class DefaultHttp2Stream<S extends DefaultHttp2Stream<S>> implements Ht
8892
this.id = id_;
8993
this.inboundQueue = new InboundMessageQueue<>(connection.context().eventLoop(), context.executor()) {
9094
@Override
91-
protected void handleMessage(Buffer item) {
92-
if (item == EMPTY) {
93-
handleTrailers(trailers);
95+
protected long evalMessage(ByteBuf data) {
96+
if (data == EMPTY) {
97+
return 1;
9498
} else {
95-
int len = item.length();
99+
int len = data.readableBytes();
96100
connection.context().execute(len, v -> connection.consumeCredits(DefaultHttp2Stream.this.id, v));
97-
handleData(item);
101+
return decoder.handle(data);
102+
}
103+
}
104+
@Override
105+
protected void handleMessage(ByteBuf data, long amount) {
106+
if (data == EMPTY) {
107+
handleTrailers(trailers);
108+
} else {
109+
for (int i = 0;i < amount;i++) {
110+
decoder.next();
111+
}
98112
}
99113
}
100114
};
@@ -134,6 +148,26 @@ protected void handleDrained() {
134148
this.trailersReceived = true;
135149
}
136150
}
151+
this.decoder = new HttpBodyDecoder() {
152+
private Buffer buffer;
153+
@Override
154+
public int handle(ByteBuf content) {
155+
buffer = BufferInternal.buffer(VertxHandler.safeBuffer(content));
156+
return 1;
157+
}
158+
@Override
159+
public void next() {
160+
Buffer item = buffer;
161+
buffer = null;
162+
Handler<Buffer> handler = dataHandler;
163+
if (handler != null) {
164+
context.dispatch(item, handler);
165+
}
166+
}
167+
@Override
168+
public void end() {
169+
}
170+
};
137171
}
138172

139173
public final HttpVersion version() {
@@ -230,8 +264,12 @@ public void onCustomFrame(int type, int flags, Buffer payload) {
230264
context.execute(new HttpFrameImpl(type, flags, payload), this::handleCustomFrame);
231265
}
232266

233-
public void onData(Buffer data) {
234-
bytesRead += data.length();
267+
public void onData(ByteBuf data) {
268+
// Warning : we retain the buffer and we don't release it on close
269+
// when close happens we should flush the queue and cumulate the result
270+
// for later delivery
271+
data.retain();
272+
bytesRead += data.readableBytes();
235273
inboundQueue.write(data);
236274
}
237275

@@ -426,6 +464,12 @@ void writeData0(ByteBuf buf, boolean end, Promise<Void> promise) {
426464
connection.writeData(id, chunk, end, promise);
427465
}
428466

467+
@Override
468+
public S bodyDecoder(HttpBodyDecoder decoder) {
469+
this.decoder = decoder;
470+
return (S)this;
471+
}
472+
429473
@Override
430474
public Future<Boolean> cancel() {
431475
return writeReset(0x08L).map(true);
@@ -481,13 +525,6 @@ public S dataHandler(Handler<Buffer> handler) {
481525
return (S)this;
482526
}
483527

484-
private void handleData(Buffer buf) {
485-
Handler<Buffer> handler = dataHandler;
486-
if (handler != null) {
487-
context.dispatch(buf, handler);
488-
}
489-
}
490-
491528
public S customFrameHandler(Handler<HttpFrame> handler) {
492529
customFrameHandler = handler;
493530
return (S)this;
@@ -510,6 +547,7 @@ private void handleTrailers(MultiMap trailers) {
510547
if (handler != null) {
511548
context.dispatch(trailers, handler);
512549
}
550+
decoder.end();
513551
}
514552

515553
public S resetHandler(Handler<Long> handler) {

vertx-core/src/main/java/io/vertx/core/http/impl/http2/Http2Stream.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
*/
1111
package io.vertx.core.http.impl.http2;
1212

13+
import io.netty.buffer.ByteBuf;
1314
import io.vertx.core.buffer.Buffer;
1415
import io.vertx.core.http.GoAway;
1516
import io.vertx.core.http.StreamPriority;
@@ -36,7 +37,7 @@ public interface Http2Stream {
3637

3738
void onPriorityChange(StreamPriority streamPriority);
3839
void onHeaders(HttpHeaders headers);
39-
void onData(Buffer buffer);
40+
void onData(ByteBuf buffer);
4041
void onCustomFrame(int type, int flags, Buffer payload);
4142
void onTrailers();
4243
void onTrailers(HttpHeaders trailers);

vertx-core/src/main/java/io/vertx/core/http/impl/http2/codec/Http2ConnectionImpl.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ abstract class Http2ConnectionImpl extends ConnectionBase implements Http2FrameL
5353

5454
private static final Logger log = LoggerFactory.getLogger(Http2ConnectionImpl.class);
5555

56-
private static ByteBuf safeBuffer(ByteBuf buf) {
56+
public static ByteBuf safeBuffer(ByteBuf buf) {
5757
ByteBuf buffer = VertxByteBufAllocator.DEFAULT.heapBuffer(buf.readableBytes());
5858
buffer.writeBytes(buf);
5959
return buffer;
@@ -300,9 +300,7 @@ public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorC
300300
public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream) {
301301
Http2Stream stream = stream(streamId);
302302
if (stream != null) {
303-
data = safeBuffer(data);
304-
Buffer buff = BufferInternal.buffer(data);
305-
stream.onData(buff);
303+
stream.onData(data);
306304
if (endOfStream) {
307305
stream.onTrailers();
308306
}

vertx-core/src/main/java/io/vertx/core/http/impl/http2/multiplex/Http2MultiplexConnection.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,12 @@ final void registerChannel(S stream, Http2FrameStream frameStream, ChannelHandle
106106

107107
void receiveData(ChannelHandlerContext chctx, int streamId, ByteBuf content, boolean ended, int initialWindowSize) {
108108
StreamChannel channel = channels.get(streamId);
109-
ByteBuf buffer = VertxByteBufAllocator.DEFAULT.heapBuffer(content.readableBytes());
110-
buffer.writeBytes(content, content.readerIndex(), content.readableBytes());
111-
Buffer buff = BufferInternal.buffer(buffer);
112-
channel.bytesConsumed += buff.length();
109+
channel.bytesConsumed += content.readableBytes();
113110
if (channel.bytesConsumed > initialWindowSize) {
114111
chctx.channel().config().setAutoRead(false);
115112
}
116113
S stream = channel.stream;
117-
stream.onData(buff);
114+
stream.onData(content);
118115
if (ended) {
119116
stream.onTrailers();
120117
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package io.vertx.tests.http;
2+
3+
import io.netty.buffer.ByteBuf;
4+
import io.netty.util.ReferenceCountUtil;
5+
import io.vertx.core.buffer.Buffer;
6+
import io.vertx.core.http.*;
7+
import io.vertx.core.http.impl.HttpResponseHead;
8+
import io.vertx.core.http.impl.HttpServerConnection;
9+
import io.vertx.core.http.impl.headers.HttpResponseHeaders;
10+
import io.vertx.core.http.impl.HttpBodyDecoder;
11+
import io.vertx.test.core.TestUtils;
12+
import io.vertx.test.http.HttpConfigurator;
13+
import io.vertx.test.http.SimpleHttpTest2;
14+
import org.junit.Test;
15+
16+
import static org.junit.Assert.fail;
17+
18+
public class HttpBodyCodecTest extends SimpleHttpTest2 {
19+
20+
public HttpBodyCodecTest() {
21+
super(HttpConfigurator.H2.MULTIPLEX);
22+
}
23+
24+
@Test
25+
public void testBodyDecoder() throws Exception {
26+
Buffer body = Buffer.buffer(TestUtils.randomAlphaString(1024));
27+
server.connectionHandler(connection -> {
28+
HttpServerConnection serverConnection = (HttpServerConnection) connection;
29+
serverConnection.streamHandler(stream -> {
30+
stream.bodyDecoder(new HttpBodyDecoder() {
31+
@Override
32+
public int handle(ByteBuf content) {
33+
ReferenceCountUtil.release(content);
34+
return 10;
35+
}
36+
@Override
37+
public void next() {
38+
}
39+
@Override
40+
public void end() {
41+
stream.writeHead(new HttpResponseHead(200, null, new HttpResponseHeaders(serverConnection.newHeaders())), null, true);
42+
}
43+
});
44+
});
45+
});
46+
server.requestHandler(request -> {
47+
fail();
48+
});
49+
startServer(testAddress);
50+
client.request(new RequestOptions(requestOptions).setMethod(HttpMethod.POST))
51+
.compose(request -> {
52+
return request.send(body).compose(HttpClientResponse::end);
53+
}).await();
54+
}
55+
}

0 commit comments

Comments
 (0)