From ddd2c1f67379b49e9996abe9ff2c69fc6f70f61d Mon Sep 17 00:00:00 2001 From: thomdev <73877922+thomdev@users.noreply.github.com> Date: Mon, 9 Nov 2020 22:06:30 -0800 Subject: [PATCH] Fix various errors while dealing with long streams (#3) Motivation: Server should be able to return an arbitrarily long stream, i.e. keep writing data as long as the channel is writable, and resume writing data when getting the writabilityChanged event. Modications: - Fix infinite loop in QuicheQuicChannel.streamSendMultiple() when Quiche.quiche_conn_stream_send() returns 0. - Fix StackOverflowError when calling fireChannelReadComplete() when no read happened. - Fix IndexOutOfBoundsException when Quiche.quiche_conn_stream_recv() returns Quiche.QUICHE_ERR_DONE. Result: Server can send a long stream. Co-authored-by: Thomas Devanneaux --- .../netty/incubator/codec/quic/QuicheQuicChannel.java | 11 +++++------ .../incubator/codec/quic/QuicheQuicStreamChannel.java | 6 +++++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/netty/incubator/codec/quic/QuicheQuicChannel.java b/src/main/java/io/netty/incubator/codec/quic/QuicheQuicChannel.java index 1c32a68a0..88f58244f 100644 --- a/src/main/java/io/netty/incubator/codec/quic/QuicheQuicChannel.java +++ b/src/main/java/io/netty/incubator/codec/quic/QuicheQuicChannel.java @@ -261,7 +261,7 @@ boolean streamSendMultiple(long streamId, ByteBufAllocator allocator, ChannelOut res = streamSend(streamId, buffer, false); } - if (Quiche.throwIfError(res)) { + if (Quiche.throwIfError(res) || res == 0) { // stream has no capacity left stop trying to send. return false; } @@ -305,11 +305,10 @@ boolean streamRecv(long streamId, ByteBuf buffer) throws Exception { long memoryAddress = buffer.memoryAddress(); int recvLen = Quiche.quiche_conn_stream_recv(connectionAddressChecked(), streamId, memoryAddress + writerIndex, buffer.writableBytes(), finBuffer.memoryAddress()); - Quiche.throwIfError(recvLen); - boolean fin = finBuffer.getBoolean(writerIndex); - // Skip the FIN - buffer.setIndex(0, writerIndex + recvLen); - return fin; + if (!Quiche.throwIfError(recvLen)) { + buffer.setIndex(0, writerIndex + recvLen); + } + return finBuffer.getBoolean(0); } private void handleWriteEgress() { diff --git a/src/main/java/io/netty/incubator/codec/quic/QuicheQuicStreamChannel.java b/src/main/java/io/netty/incubator/codec/quic/QuicheQuicStreamChannel.java index 64b2c61e7..32a91d8f7 100644 --- a/src/main/java/io/netty/incubator/codec/quic/QuicheQuicStreamChannel.java +++ b/src/main/java/io/netty/incubator/codec/quic/QuicheQuicStreamChannel.java @@ -305,6 +305,7 @@ void recv() { allocHandle.reset(config); ByteBuf byteBuf = null; boolean close = false; + boolean readCompleteNeeded = false; QuicheQuicChannel parent = parentQuicChannel(); try { do { @@ -317,12 +318,15 @@ void recv() { break; } readPending = false; + readCompleteNeeded = true; pipeline.fireChannelRead(byteBuf); byteBuf = null; } while (allocHandle.continueReading() && !close); allocHandle.readComplete(); - pipeline.fireChannelReadComplete(); + if (readCompleteNeeded) { + pipeline.fireChannelReadComplete(); + } if (close) { closeOnRead(pipeline); }