From faf8becf2e12fdb46ba256c39eedab501ed3cdcb Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 14 Jan 2014 16:08:29 +0100 Subject: [PATCH] Make use of ByteBufProcessor for extract initial line and headers This gives some nice performance boost as readByte() is quite expensive because of the index / replay checks. --- .../handler/codec/http/HttpObjectDecoder.java | 168 ++++++++++-------- .../codec/http/HttpResponseDecoderTest.java | 1 - .../handler/codec/ReplayingDecoderBuffer.java | 2 +- 3 files changed, 93 insertions(+), 78 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java index 15798abbee..5569cb3876 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java @@ -16,6 +16,7 @@ package io.netty.handler.codec.http; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufProcessor; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; @@ -106,12 +107,14 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder 0) { @@ -485,7 +488,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder 0); // Add the last header. @@ -518,7 +521,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder 0) { LastHttpContent trailer = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, validateHeaders); @@ -544,7 +547,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder 0); return trailer; @@ -553,46 +556,6 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder= maxHeaderSize) { - // TODO: Respond with Bad Request and discard the traffic - // or close the connection. - // No need to notify the upstream handlers - just log. - // If decoding a response, just throw an exception. - throw new TooLongFrameException( - "HTTP header is larger than " + - maxHeaderSize + " bytes."); - } - - sb.append(nextByte); - } - - this.headerSize = headerSize; - return sb; - } - protected abstract boolean isDecodingRequest(); protected abstract HttpMessage createMessage(String[] initialLine) throws Exception; protected abstract HttpMessage createInvalidMessage(); @@ -610,35 +573,6 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder= maxLineLength) { - // TODO: Respond with Bad Request and discard the traffic - // or close the connection. - // No need to notify the upstream handlers - just log. - // If decoding a response, just throw an exception. - throw new TooLongFrameException( - "An HTTP line is larger than " + maxLineLength + - " bytes."); - } - lineLength ++; - sb.append((char) nextByte); - } - } - } - private static String[] splitInitialLine(AppendableCharSequence sb) { int aStart; int aEnd; @@ -729,4 +663,86 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder= maxHeaderSize) { + // TODO: Respond with Bad Request and discard the traffic + // or close the connection. + // No need to notify the upstream handlers - just log. + // If decoding a response, just throw an exception. + throw new TooLongFrameException( + "HTTP header is larger than " + + maxHeaderSize + " bytes."); + } + + seq.append(nextByte); + return true; + } + } + + private final class LineParser implements ByteBufProcessor { + private final AppendableCharSequence seq; + private int size; + + LineParser(AppendableCharSequence seq) { + this.seq = seq; + } + + public AppendableCharSequence parse(ByteBuf buffer) { + seq.reset(); + size = 0; + int i = buffer.forEachByte(this); + buffer.readerIndex(i + 1); + return seq; + } + + @Override + public boolean process(byte value) throws Exception { + char nextByte = (char) value; + if (nextByte == HttpConstants.CR) { + return true; + } else if (nextByte == HttpConstants.LF) { + return false; + } else { + if (size >= maxInitialLineLength) { + // TODO: Respond with Bad Request and discard the traffic + // or close the connection. + // No need to notify the upstream handlers - just log. + // If decoding a response, just throw an exception. + throw new TooLongFrameException( + "An HTTP line is larger than " + maxInitialLineLength + + " bytes."); + } + size ++; + seq.append(nextByte); + return true; + } + } + } } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java index 8caecd3911..1aeead7b6d 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java @@ -189,7 +189,6 @@ public class HttpResponseDecoderTest { // Close the connection without sending anything. ch.finish(); - // The decoder should not generate the last chunk because it's closed prematurely. assertThat(ch.readInbound(), is(nullValue())); } diff --git a/codec/src/main/java/io/netty/handler/codec/ReplayingDecoderBuffer.java b/codec/src/main/java/io/netty/handler/codec/ReplayingDecoderBuffer.java index b3d4d686d1..108608dea5 100644 --- a/codec/src/main/java/io/netty/handler/codec/ReplayingDecoderBuffer.java +++ b/codec/src/main/java/io/netty/handler/codec/ReplayingDecoderBuffer.java @@ -345,7 +345,7 @@ final class ReplayingDecoderBuffer extends ByteBuf { @Override public int forEachByte(ByteBufProcessor processor) { int ret = buffer.forEachByte(processor); - if (!terminated && ret < 0) { + if (ret < 0) { throw REPLAY; } else { return ret;