netty-codec: Manage read-flow explicitly in MessageAggregator
This commit is contained in:
parent
dcd322dda2
commit
4c77e7c55a
@ -399,6 +399,17 @@ public abstract class MessageAggregator<I, S, C extends ByteBufHolder, O extends
|
|||||||
new TooLongFrameException("content length exceeded " + maxContentLength() + " bytes."));
|
new TooLongFrameException("content length exceeded " + maxContentLength() + " bytes."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
|
||||||
|
// We might need keep reading the channel until the full message is aggregated.
|
||||||
|
//
|
||||||
|
// See https://github.com/netty/netty/issues/6583
|
||||||
|
if (currentMessage != null && !ctx.channel().config().isAutoRead()) {
|
||||||
|
ctx.read();
|
||||||
|
}
|
||||||
|
ctx.fireChannelReadComplete();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||||
try {
|
try {
|
||||||
|
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.netty.handler.codec;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.ByteBufHolder;
|
||||||
|
import io.netty.buffer.DefaultByteBufHolder;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.ChannelOutboundHandlerAdapter;
|
||||||
|
import io.netty.channel.embedded.EmbeddedChannel;
|
||||||
|
import io.netty.util.CharsetUtil;
|
||||||
|
|
||||||
|
public class MessageAggregatorTest {
|
||||||
|
private static final class ReadCounter extends ChannelOutboundHandlerAdapter {
|
||||||
|
int value;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ChannelHandlerContext ctx) throws Exception {
|
||||||
|
value++;
|
||||||
|
ctx.read();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract static class MockMessageAggregator
|
||||||
|
extends MessageAggregator<ByteBufHolder, ByteBufHolder, ByteBufHolder, ByteBufHolder> {
|
||||||
|
|
||||||
|
protected MockMessageAggregator() {
|
||||||
|
super(1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ByteBufHolder beginAggregation(ByteBufHolder start, ByteBuf content) throws Exception {
|
||||||
|
return start.replace(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ByteBufHolder message(String string) {
|
||||||
|
return new DefaultByteBufHolder(
|
||||||
|
Unpooled.copiedBuffer(string, CharsetUtil.US_ASCII));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Test
|
||||||
|
public void testReadFlowManagement() throws Exception {
|
||||||
|
ReadCounter counter = new ReadCounter();
|
||||||
|
ByteBufHolder first = message("first");
|
||||||
|
ByteBufHolder chunk = message("chunk");
|
||||||
|
ByteBufHolder last = message("last");
|
||||||
|
|
||||||
|
MockMessageAggregator agg = spy(MockMessageAggregator.class);
|
||||||
|
when(agg.isStartMessage(first)).thenReturn(true);
|
||||||
|
when(agg.isContentMessage(chunk)).thenReturn(true);
|
||||||
|
when(agg.isContentMessage(last)).thenReturn(true);
|
||||||
|
when(agg.isLastContentMessage(last)).thenReturn(true);
|
||||||
|
|
||||||
|
EmbeddedChannel embedded = new EmbeddedChannel(counter, agg);
|
||||||
|
embedded.config().setAutoRead(false);
|
||||||
|
|
||||||
|
assertFalse(embedded.writeInbound(first));
|
||||||
|
assertFalse(embedded.writeInbound(chunk));
|
||||||
|
assertTrue(embedded.writeInbound(last));
|
||||||
|
|
||||||
|
assertEquals(3, counter.value); // 2 reads issued from MockMessageAggregator
|
||||||
|
// 1 read issued from EmbeddedChannel constructor
|
||||||
|
|
||||||
|
ByteBufHolder all = new DefaultByteBufHolder(Unpooled.wrappedBuffer(
|
||||||
|
first.content().retain(), chunk.content().retain(), last.content().retain()));
|
||||||
|
ByteBufHolder out = embedded.readInbound();
|
||||||
|
|
||||||
|
assertEquals(all, out);
|
||||||
|
assertTrue(all.release() && out.release());
|
||||||
|
assertFalse(embedded.finish());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user