Package io.netty.handler.codec.http
Class HttpObjectDecoder
- java.lang.Object
-
- io.netty.channel.ChannelHandlerAdapter
-
- io.netty.channel.ChannelInboundHandlerAdapter
-
- io.netty.handler.codec.ByteToMessageDecoder
-
- io.netty.handler.codec.http.HttpObjectDecoder
-
- All Implemented Interfaces:
ChannelHandler,ChannelInboundHandler
- Direct Known Subclasses:
HttpRequestDecoder,HttpResponseDecoder,RtspDecoder,RtspObjectDecoder
public abstract class HttpObjectDecoder extends ByteToMessageDecoder
DecodesByteBufs intoHttpMessages andHttpContents.Parameters that prevents excessive memory consumption
Name Default value Meaning maxInitialLineLength4096 The maximum length of the initial line (e.g. "GET / HTTP/1.0"or"HTTP/1.0 200 OK") If the length of the initial line exceeds this value, aTooLongHttpLineExceptionwill be raised.maxHeaderSize8192 The maximum length of all headers. If the sum of the length of each header exceeds this value, a TooLongHttpHeaderExceptionwill be raised.maxChunkSize8192 The maximum length of the content or each chunk. If the content length (or the length of each chunk) exceeds this value, the content or chunk will be split into multiple HttpContents whose length ismaxChunkSizeat maximum.Parameters that control parsing behavior
Name Default value Meaning allowDuplicateContentLengthsfalse When set to false, will reject any messages that contain multiple Content-Length header fields. When set totrue, will allow multiple Content-Length headers only if they are all the same decimal value. The duplicated field-values will be replaced with a single valid Content-Length field. See RFC 7230, Section 3.3.2.allowPartialChunkstrue If the length of a chunk exceeds the ByteBufs readable bytes andallowPartialChunksis set totrue, the chunk will be split into multipleHttpContents. Otherwise, if the chunk size does not exceedmaxChunkSizeandallowPartialChunksis set tofalse, theByteBufis not decoded into anHttpContentuntil the readable bytes are greater or equal to the chunk size.Chunked Content
If the content of an HTTP message is greater thanmaxChunkSizeor the transfer encoding of the HTTP message is 'chunked', this decoder generates oneHttpMessageinstance and its followingHttpContents per single HTTP message to avoid excessive memory consumption. For example, the following HTTP message:GET / HTTP/1.1 Transfer-Encoding: chunked 1a abcdefghijklmnopqrstuvwxyz 10 1234567890abcdef 0 Content-MD5: ... [blank line]
triggersHttpRequestDecoderto generate 3 objects:- An
HttpRequest, - The first
HttpContentwhose content is'abcdefghijklmnopqrstuvwxyz', - The second
LastHttpContentwhose content is'1234567890abcdef', which marks the end of the content.
HttpContents by yourself for your convenience, insertHttpObjectAggregatorafter this decoder in theChannelPipeline. However, please note that your server might not be as memory efficient as without the aggregator.Extensibility
Please note that this decoder is designed to be extended to implement a protocol derived from HTTP, such as RTSP and ICAP. To implement the decoder of such a derived protocol, extend this class and implement all abstract methods properly.Header Validation
It is recommended to always enable header validation.Without header validation, your system can become vulnerable to CWE-113: Improper Neutralization of CRLF Sequences in HTTP Headers ('HTTP Response Splitting') .
This recommendation stands even when both peers in the HTTP exchange are trusted, as it helps with defence-in-depth.
-
-
Nested Class Summary
Nested Classes Modifier and Type Class Description private static classHttpObjectDecoder.HeaderParserprivate classHttpObjectDecoder.LineParserprivate static classHttpObjectDecoder.StateThe internal state ofHttpObjectDecoder.-
Nested classes/interfaces inherited from class io.netty.handler.codec.ByteToMessageDecoder
ByteToMessageDecoder.Cumulator
-
Nested classes/interfaces inherited from interface io.netty.channel.ChannelHandler
ChannelHandler.Sharable
-
-
Field Summary
-
Fields inherited from class io.netty.handler.codec.ByteToMessageDecoder
COMPOSITE_CUMULATOR, MERGE_CUMULATOR
-
-
Constructor Summary
Constructors Modifier Constructor Description protectedHttpObjectDecoder()Creates a new instance with the defaultmaxInitialLineLength (4096),maxHeaderSize (8192), andmaxChunkSize (8192).protectedHttpObjectDecoder(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean chunkedSupported)Deprecated.UseHttpObjectDecoder(HttpDecoderConfig)instead.protectedHttpObjectDecoder(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean chunkedSupported, boolean validateHeaders)Deprecated.UseHttpObjectDecoder(HttpDecoderConfig)instead.protectedHttpObjectDecoder(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean chunkedSupported, boolean validateHeaders, int initialBufferSize)Deprecated.UseHttpObjectDecoder(HttpDecoderConfig)instead.protectedHttpObjectDecoder(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean chunkedSupported, boolean validateHeaders, int initialBufferSize, boolean allowDuplicateContentLengths)Deprecated.UseHttpObjectDecoder(HttpDecoderConfig)instead.protectedHttpObjectDecoder(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean chunkedSupported, boolean validateHeaders, int initialBufferSize, boolean allowDuplicateContentLengths, boolean allowPartialChunks)Deprecated.UseHttpObjectDecoder(HttpDecoderConfig)instead.protectedHttpObjectDecoder(HttpDecoderConfig config)Creates a new instance with the specified configuration.
-
Method Summary
All Methods Static Methods Instance Methods Abstract Methods Concrete Methods Modifier and Type Method Description private voidaddCurrentMessage(java.util.List<java.lang.Object> out)private static voidcheckChunkExtensions(ByteBuf line)protected abstract HttpMessagecreateInvalidMessage()protected abstract HttpMessagecreateMessage(java.lang.String[] initialLine)protected voiddecode(ChannelHandlerContext ctx, ByteBuf buffer, java.util.List<java.lang.Object> out)Decode the from oneByteBufto an other.protected voiddecodeLast(ChannelHandlerContext ctx, ByteBuf in, java.util.List<java.lang.Object> out)Is called one last time when theChannelHandlerContextgoes in-active.private static intfindEndOfString(byte[] sb, int start, int end)private static intfindNonSPLenient(byte[] sb, int offset, int end)private static intfindNonWhitespace(byte[] sb, int offset, int end)private static intfindSPLenient(byte[] sb, int offset, int end)private static intgetChunkSize(byte[] hex, int start, int length)protected voidhandlerRemoved0(ChannelHandlerContext ctx)Gets called after theByteToMessageDecoderwas removed from the actual context and it doesn't handle events anymore.protected voidhandleTransferEncodingChunkedWithContentLength(HttpMessage message)Invoked when a message with both a "Transfer-Encoding: chunked" and a "Content-Length" header field is detected.private HttpContentinvalidChunk(ByteBuf in, java.lang.Exception cause)private HttpMessageinvalidMessage(HttpMessage current, ByteBuf in, java.lang.Exception cause)protected booleanisContentAlwaysEmpty(HttpMessage msg)private static booleanisControlOrWhitespaceAsciiChar(byte b)protected abstract booleanisDecodingRequest()private static booleanisOWS(byte ch)private static booleanisPermittedTrailingHeader(AsciiString name)Checks whether the given trailer field name is permitted per RFC 9110 section 6.5private static booleanisSPLenient(byte c)protected booleanisSwitchingToNonHttp1Protocol(HttpResponse msg)Returns true if the server switched to a different protocol than HTTP/1.0 or HTTP/1.1, e.g.protected booleanisValidating(HttpHeadersFactory headersFactory)private static booleanisWhitespace(byte b)private static java.lang.StringlangAsciiString(byte[] asciiContent, int start, int length)This method shouldn't exist: look at https://bugs.openjdk.org/browse/JDK-8295496 for more contextprivate HttpObjectDecoder.StatereadHeaders(ByteBuf buffer)private LastHttpContentreadTrailingHeaders(ByteBuf buffer)voidreset()Resets the state of the decoder so that it is ready to decode a new message.private voidresetNow()private static intskipWhiteSpaces(byte[] hex, int start, int length)It skips any whitespace char and return the number of skipped bytes.protected java.lang.StringsplitFirstWordInitialLine(byte[] asciiContent, int start, int length)private voidsplitHeader(byte[] line, int start, int length)protected AsciiStringsplitHeaderName(byte[] sb, int start, int length)private java.lang.String[]splitInitialLine(ByteBuf asciiBuffer)protected java.lang.StringsplitSecondWordInitialLine(byte[] asciiContent, int start, int length)protected java.lang.StringsplitThirdWordInitialLine(byte[] asciiContent, int start, int length)voiduserEventTriggered(ChannelHandlerContext ctx, java.lang.Object evt)CallsChannelHandlerContext.fireUserEventTriggered(Object)to forward to the nextChannelInboundHandlerin theChannelPipeline.-
Methods inherited from class io.netty.handler.codec.ByteToMessageDecoder
actualReadableBytes, callDecode, channelInactive, channelRead, channelReadComplete, discardSomeReadBytes, handlerRemoved, internalBuffer, isSingleDecode, setCumulator, setDiscardAfterReads, setSingleDecode
-
Methods inherited from class io.netty.channel.ChannelInboundHandlerAdapter
channelActive, channelRegistered, channelUnregistered, channelWritabilityChanged, exceptionCaught
-
Methods inherited from class io.netty.channel.ChannelHandlerAdapter
ensureNotSharable, handlerAdded, isSharable
-
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
-
Methods inherited from interface io.netty.channel.ChannelHandler
handlerAdded
-
-
-
-
Field Detail
-
DEFAULT_MAX_INITIAL_LINE_LENGTH
public static final int DEFAULT_MAX_INITIAL_LINE_LENGTH
- See Also:
- Constant Field Values
-
DEFAULT_MAX_HEADER_SIZE
public static final int DEFAULT_MAX_HEADER_SIZE
- See Also:
- Constant Field Values
-
DEFAULT_CHUNKED_SUPPORTED
public static final boolean DEFAULT_CHUNKED_SUPPORTED
- See Also:
- Constant Field Values
-
DEFAULT_ALLOW_PARTIAL_CHUNKS
public static final boolean DEFAULT_ALLOW_PARTIAL_CHUNKS
- See Also:
- Constant Field Values
-
DEFAULT_MAX_CHUNK_SIZE
public static final int DEFAULT_MAX_CHUNK_SIZE
- See Also:
- Constant Field Values
-
DEFAULT_VALIDATE_HEADERS
public static final boolean DEFAULT_VALIDATE_HEADERS
- See Also:
- Constant Field Values
-
DEFAULT_INITIAL_BUFFER_SIZE
public static final int DEFAULT_INITIAL_BUFFER_SIZE
- See Also:
- Constant Field Values
-
DEFAULT_ALLOW_DUPLICATE_CONTENT_LENGTHS
public static final boolean DEFAULT_ALLOW_DUPLICATE_CONTENT_LENGTHS
- See Also:
- Constant Field Values
-
DEFAULT_STRICT_LINE_PARSING
public static final boolean DEFAULT_STRICT_LINE_PARSING
-
THROW_INVALID_CHUNK_EXTENSION
private static final java.lang.Runnable THROW_INVALID_CHUNK_EXTENSION
-
THROW_INVALID_LINE_SEPARATOR
private static final java.lang.Runnable THROW_INVALID_LINE_SEPARATOR
-
maxChunkSize
private final int maxChunkSize
-
chunkedSupported
private final boolean chunkedSupported
-
allowPartialChunks
private final boolean allowPartialChunks
-
validateHeaders
@Deprecated protected final boolean validateHeaders
Deprecated.This field is no longer used. It is only kept around for backwards compatibility purpose.
-
headersFactory
protected final HttpHeadersFactory headersFactory
-
trailersFactory
protected final HttpHeadersFactory trailersFactory
-
allowDuplicateContentLengths
private final boolean allowDuplicateContentLengths
-
parserScratchBuffer
private final ByteBuf parserScratchBuffer
-
defaultStrictCRLFCheck
private final java.lang.Runnable defaultStrictCRLFCheck
-
headerParser
private final HttpObjectDecoder.HeaderParser headerParser
-
lineParser
private final HttpObjectDecoder.LineParser lineParser
-
message
private HttpMessage message
-
chunkSize
private long chunkSize
-
contentLength
private long contentLength
-
chunked
private boolean chunked
-
isSwitchingToNonHttp1Protocol
private boolean isSwitchingToNonHttp1Protocol
-
resetRequested
private final java.util.concurrent.atomic.AtomicBoolean resetRequested
-
name
private AsciiString name
-
value
private java.lang.String value
-
trailer
private LastHttpContent trailer
-
currentState
private HttpObjectDecoder.State currentState
-
SP_LENIENT_BYTES
private static final boolean[] SP_LENIENT_BYTES
-
LATIN_WHITESPACE
private static final boolean[] LATIN_WHITESPACE
-
ISO_CONTROL_OR_WHITESPACE
private static final boolean[] ISO_CONTROL_OR_WHITESPACE
-
SKIP_CONTROL_CHARS_BYTES
private static final ByteProcessor SKIP_CONTROL_CHARS_BYTES
-
-
Constructor Detail
-
HttpObjectDecoder
protected HttpObjectDecoder()
Creates a new instance with the defaultmaxInitialLineLength (4096),maxHeaderSize (8192), andmaxChunkSize (8192).
-
HttpObjectDecoder
@Deprecated protected HttpObjectDecoder(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean chunkedSupported)Deprecated.UseHttpObjectDecoder(HttpDecoderConfig)instead.Creates a new instance with the specified parameters.
-
HttpObjectDecoder
@Deprecated protected HttpObjectDecoder(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean chunkedSupported, boolean validateHeaders)Deprecated.UseHttpObjectDecoder(HttpDecoderConfig)instead.Creates a new instance with the specified parameters.
-
HttpObjectDecoder
@Deprecated protected HttpObjectDecoder(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean chunkedSupported, boolean validateHeaders, int initialBufferSize)Deprecated.UseHttpObjectDecoder(HttpDecoderConfig)instead.Creates a new instance with the specified parameters.
-
HttpObjectDecoder
@Deprecated protected HttpObjectDecoder(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean chunkedSupported, boolean validateHeaders, int initialBufferSize, boolean allowDuplicateContentLengths)Deprecated.UseHttpObjectDecoder(HttpDecoderConfig)instead.Creates a new instance with the specified parameters.
-
HttpObjectDecoder
@Deprecated protected HttpObjectDecoder(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean chunkedSupported, boolean validateHeaders, int initialBufferSize, boolean allowDuplicateContentLengths, boolean allowPartialChunks)Deprecated.UseHttpObjectDecoder(HttpDecoderConfig)instead.Creates a new instance with the specified parameters.
-
HttpObjectDecoder
protected HttpObjectDecoder(HttpDecoderConfig config)
Creates a new instance with the specified configuration.
-
-
Method Detail
-
handlerRemoved0
protected void handlerRemoved0(ChannelHandlerContext ctx) throws java.lang.Exception
Description copied from class:ByteToMessageDecoderGets called after theByteToMessageDecoderwas removed from the actual context and it doesn't handle events anymore.- Overrides:
handlerRemoved0in classByteToMessageDecoder- Throws:
java.lang.Exception
-
isValidating
protected boolean isValidating(HttpHeadersFactory headersFactory)
-
decode
protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, java.util.List<java.lang.Object> out) throws java.lang.Exception
Description copied from class:ByteToMessageDecoderDecode the from oneByteBufto an other. This method will be called till either the inputByteBufhas nothing to read when return from this method or till nothing was read from the inputByteBuf.- Specified by:
decodein classByteToMessageDecoder- Parameters:
ctx- theChannelHandlerContextwhich thisByteToMessageDecoderbelongs tobuffer- theByteBuffrom which to read dataout- theListto which decoded messages should be added- Throws:
java.lang.Exception- is thrown if an error occurs
-
decodeLast
protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, java.util.List<java.lang.Object> out) throws java.lang.Exception
Description copied from class:ByteToMessageDecoderIs called one last time when theChannelHandlerContextgoes in-active. Which means theByteToMessageDecoder.channelInactive(ChannelHandlerContext)was triggered. By default, this will just callByteToMessageDecoder.decode(ChannelHandlerContext, ByteBuf, List)but sub-classes may override this for some special cleanup operation.- Overrides:
decodeLastin classByteToMessageDecoder- Throws:
java.lang.Exception
-
userEventTriggered
public void userEventTriggered(ChannelHandlerContext ctx, java.lang.Object evt) throws java.lang.Exception
Description copied from class:ChannelInboundHandlerAdapterCallsChannelHandlerContext.fireUserEventTriggered(Object)to forward to the nextChannelInboundHandlerin theChannelPipeline. Sub-classes may override this method to change behavior.- Specified by:
userEventTriggeredin interfaceChannelInboundHandler- Overrides:
userEventTriggeredin classByteToMessageDecoder- Throws:
java.lang.Exception
-
addCurrentMessage
private void addCurrentMessage(java.util.List<java.lang.Object> out)
-
isContentAlwaysEmpty
protected boolean isContentAlwaysEmpty(HttpMessage msg)
-
isSwitchingToNonHttp1Protocol
protected boolean isSwitchingToNonHttp1Protocol(HttpResponse msg)
Returns true if the server switched to a different protocol than HTTP/1.0 or HTTP/1.1, e.g. HTTP/2 or Websocket. Returns false if the upgrade happened in a different layer, e.g. upgrade from HTTP/1.1 to HTTP/1.1 over TLS.
-
reset
public void reset()
Resets the state of the decoder so that it is ready to decode a new message. This method is useful for handling a rejected request withExpect: 100-continueheader.
-
resetNow
private void resetNow()
-
invalidMessage
private HttpMessage invalidMessage(HttpMessage current, ByteBuf in, java.lang.Exception cause)
-
checkChunkExtensions
private static void checkChunkExtensions(ByteBuf line)
-
invalidChunk
private HttpContent invalidChunk(ByteBuf in, java.lang.Exception cause)
-
readHeaders
private HttpObjectDecoder.State readHeaders(ByteBuf buffer)
-
handleTransferEncodingChunkedWithContentLength
protected void handleTransferEncodingChunkedWithContentLength(HttpMessage message)
Invoked when a message with both a "Transfer-Encoding: chunked" and a "Content-Length" header field is detected. The default behavior is to remove the Content-Length field, but this method could be overridden to change the behavior (to, e.g., throw an exception and produce an invalid message).See: https://tools.ietf.org/html/rfc7230#section-3.3.3
If a message is received with both a Transfer-Encoding and a Content-Length header field, the Transfer-Encoding overrides the Content-Length. Such a message might indicate an attempt to perform request smuggling (Section 9.5) or response splitting (Section 9.4) and ought to be handled as an error. A sender MUST remove the received Content-Length field prior to forwarding such a message downstream.Also see: https://github.com/apache/tomcat/blob/b693d7c1981fa7f51e58bc8c8e72e3fe80b7b773/ java/org/apache/coyote/http11/Http11Processor.java#L747-L755 https://github.com/nginx/nginx/blob/0ad4393e30c119d250415cb769e3d8bc8dce5186/ src/http/ngx_http_request.c#L1946-L1953
-
readTrailingHeaders
private LastHttpContent readTrailingHeaders(ByteBuf buffer)
-
isPermittedTrailingHeader
private static boolean isPermittedTrailingHeader(AsciiString name)
Checks whether the given trailer field name is permitted per RFC 9110 section 6.5
-
isDecodingRequest
protected abstract boolean isDecodingRequest()
-
createMessage
protected abstract HttpMessage createMessage(java.lang.String[] initialLine) throws java.lang.Exception
- Throws:
java.lang.Exception
-
createInvalidMessage
protected abstract HttpMessage createInvalidMessage()
-
skipWhiteSpaces
private static int skipWhiteSpaces(byte[] hex, int start, int length)It skips any whitespace char and return the number of skipped bytes.
-
getChunkSize
private static int getChunkSize(byte[] hex, int start, int length)
-
splitInitialLine
private java.lang.String[] splitInitialLine(ByteBuf asciiBuffer)
-
splitFirstWordInitialLine
protected java.lang.String splitFirstWordInitialLine(byte[] asciiContent, int start, int length)
-
splitSecondWordInitialLine
protected java.lang.String splitSecondWordInitialLine(byte[] asciiContent, int start, int length)
-
splitThirdWordInitialLine
protected java.lang.String splitThirdWordInitialLine(byte[] asciiContent, int start, int length)
-
langAsciiString
private static java.lang.String langAsciiString(byte[] asciiContent, int start, int length)This method shouldn't exist: look at https://bugs.openjdk.org/browse/JDK-8295496 for more context
-
splitHeader
private void splitHeader(byte[] line, int start, int length)
-
splitHeaderName
protected AsciiString splitHeaderName(byte[] sb, int start, int length)
-
findNonSPLenient
private static int findNonSPLenient(byte[] sb, int offset, int end)
-
findSPLenient
private static int findSPLenient(byte[] sb, int offset, int end)
-
isSPLenient
private static boolean isSPLenient(byte c)
-
isWhitespace
private static boolean isWhitespace(byte b)
-
findNonWhitespace
private static int findNonWhitespace(byte[] sb, int offset, int end)
-
findEndOfString
private static int findEndOfString(byte[] sb, int start, int end)
-
isOWS
private static boolean isOWS(byte ch)
-
isControlOrWhitespaceAsciiChar
private static boolean isControlOrWhitespaceAsciiChar(byte b)
-
-