Search in sources :

Example 1 with AppendableCharSequence

use of io.netty.util.internal.AppendableCharSequence in project netty by netty.

the class HttpObjectDecoder method decode.

@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception {
    if (resetRequested) {
        resetNow();
    }
    switch(currentState) {
        case SKIP_CONTROL_CHARS:
        // Fall-through
        case READ_INITIAL:
            try {
                AppendableCharSequence line = lineParser.parse(buffer);
                if (line == null) {
                    return;
                }
                String[] initialLine = splitInitialLine(line);
                if (initialLine.length < 3) {
                    // Invalid initial line - ignore.
                    currentState = State.SKIP_CONTROL_CHARS;
                    return;
                }
                message = createMessage(initialLine);
                currentState = State.READ_HEADER;
            // fall-through
            } catch (Exception e) {
                out.add(invalidMessage(buffer, e));
                return;
            }
        case READ_HEADER:
            try {
                State nextState = readHeaders(buffer);
                if (nextState == null) {
                    return;
                }
                currentState = nextState;
                switch(nextState) {
                    case SKIP_CONTROL_CHARS:
                        // fast-path
                        // No content is expected.
                        out.add(message);
                        out.add(LastHttpContent.EMPTY_LAST_CONTENT);
                        resetNow();
                        return;
                    case READ_CHUNK_SIZE:
                        if (!chunkedSupported) {
                            throw new IllegalArgumentException("Chunked messages not supported");
                        }
                        // Chunked encoding - generate HttpMessage first.  HttpChunks will follow.
                        out.add(message);
                        return;
                    default:
                        /**
                         * <a href="https://tools.ietf.org/html/rfc7230#section-3.3.3">RFC 7230, 3.3.3</a> states that if a
                         * request does not have either a transfer-encoding or a content-length header then the message body
                         * length is 0. However for a response the body length is the number of octets received prior to the
                         * server closing the connection. So we treat this as variable length chunked encoding.
                         */
                        long contentLength = contentLength();
                        if (contentLength == 0 || contentLength == -1 && isDecodingRequest()) {
                            out.add(message);
                            out.add(LastHttpContent.EMPTY_LAST_CONTENT);
                            resetNow();
                            return;
                        }
                        assert nextState == State.READ_FIXED_LENGTH_CONTENT || nextState == State.READ_VARIABLE_LENGTH_CONTENT;
                        out.add(message);
                        if (nextState == State.READ_FIXED_LENGTH_CONTENT) {
                            // chunkSize will be decreased as the READ_FIXED_LENGTH_CONTENT state reads data chunk by chunk.
                            chunkSize = contentLength;
                        }
                        // We return here, this forces decode to be called again where we will decode the content
                        return;
                }
            } catch (Exception e) {
                out.add(invalidMessage(buffer, e));
                return;
            }
        case READ_VARIABLE_LENGTH_CONTENT:
            {
                // Keep reading data as a chunk until the end of connection is reached.
                int toRead = Math.min(buffer.readableBytes(), maxChunkSize);
                if (toRead > 0) {
                    ByteBuf content = buffer.readRetainedSlice(toRead);
                    out.add(new DefaultHttpContent(content));
                }
                return;
            }
        case READ_FIXED_LENGTH_CONTENT:
            {
                int readLimit = buffer.readableBytes();
                // See https://github.com/netty/netty/issues/433
                if (readLimit == 0) {
                    return;
                }
                int toRead = Math.min(readLimit, maxChunkSize);
                if (toRead > chunkSize) {
                    toRead = (int) chunkSize;
                }
                ByteBuf content = buffer.readRetainedSlice(toRead);
                chunkSize -= toRead;
                if (chunkSize == 0) {
                    // Read all content.
                    out.add(new DefaultLastHttpContent(content, validateHeaders));
                    resetNow();
                } else {
                    out.add(new DefaultHttpContent(content));
                }
                return;
            }
        /**
         * everything else after this point takes care of reading chunked content. basically, read chunk size,
         * read chunk, read and ignore the CRLF and repeat until 0
         */
        case READ_CHUNK_SIZE:
            try {
                AppendableCharSequence line = lineParser.parse(buffer);
                if (line == null) {
                    return;
                }
                int chunkSize = getChunkSize(line.toString());
                this.chunkSize = chunkSize;
                if (chunkSize == 0) {
                    currentState = State.READ_CHUNK_FOOTER;
                    return;
                }
                currentState = State.READ_CHUNKED_CONTENT;
            // fall-through
            } catch (Exception e) {
                out.add(invalidChunk(buffer, e));
                return;
            }
        case READ_CHUNKED_CONTENT:
            {
                assert chunkSize <= Integer.MAX_VALUE;
                int toRead = Math.min((int) chunkSize, maxChunkSize);
                if (!allowPartialChunks && buffer.readableBytes() < toRead) {
                    return;
                }
                toRead = Math.min(toRead, buffer.readableBytes());
                if (toRead == 0) {
                    return;
                }
                HttpContent chunk = new DefaultHttpContent(buffer.readRetainedSlice(toRead));
                chunkSize -= toRead;
                out.add(chunk);
                if (chunkSize != 0) {
                    return;
                }
                currentState = State.READ_CHUNK_DELIMITER;
            // fall-through
            }
        case READ_CHUNK_DELIMITER:
            {
                final int wIdx = buffer.writerIndex();
                int rIdx = buffer.readerIndex();
                while (wIdx > rIdx) {
                    byte next = buffer.getByte(rIdx++);
                    if (next == HttpConstants.LF) {
                        currentState = State.READ_CHUNK_SIZE;
                        break;
                    }
                }
                buffer.readerIndex(rIdx);
                return;
            }
        case READ_CHUNK_FOOTER:
            try {
                LastHttpContent trailer = readTrailingHeaders(buffer);
                if (trailer == null) {
                    return;
                }
                out.add(trailer);
                resetNow();
                return;
            } catch (Exception e) {
                out.add(invalidChunk(buffer, e));
                return;
            }
        case BAD_MESSAGE:
            {
                // Keep discarding until disconnection.
                buffer.skipBytes(buffer.readableBytes());
                break;
            }
        case UPGRADED:
            {
                int readableBytes = buffer.readableBytes();
                if (readableBytes > 0) {
                    // Keep on consuming as otherwise we may trigger an DecoderException,
                    // other handler will replace this codec with the upgraded protocol codec to
                    // take the traffic over at some point then.
                    // See https://github.com/netty/netty/issues/2173
                    out.add(buffer.readBytes(readableBytes));
                }
                break;
            }
        default:
            break;
    }
}
Also used : ByteBuf(io.netty.buffer.ByteBuf) PrematureChannelClosureException(io.netty.handler.codec.PrematureChannelClosureException) TooLongFrameException(io.netty.handler.codec.TooLongFrameException) AppendableCharSequence(io.netty.util.internal.AppendableCharSequence)

Example 2 with AppendableCharSequence

use of io.netty.util.internal.AppendableCharSequence in project netty by netty.

the class HttpObjectDecoder method readHeaders.

private State readHeaders(ByteBuf buffer) {
    final HttpMessage message = this.message;
    final HttpHeaders headers = message.headers();
    AppendableCharSequence line = headerParser.parse(buffer);
    if (line == null) {
        return null;
    }
    if (line.length() > 0) {
        do {
            char firstChar = line.charAtUnsafe(0);
            if (name != null && (firstChar == ' ' || firstChar == '\t')) {
                // please do not make one line from below code
                // as it breaks +XX:OptimizeStringConcat optimization
                String trimmedLine = line.toString().trim();
                String valueStr = String.valueOf(value);
                value = valueStr + ' ' + trimmedLine;
            } else {
                if (name != null) {
                    headers.add(name, value);
                }
                splitHeader(line);
            }
            line = headerParser.parse(buffer);
            if (line == null) {
                return null;
            }
        } while (line.length() > 0);
    }
    // Add the last header.
    if (name != null) {
        headers.add(name, value);
    }
    // reset name and value fields
    name = null;
    value = null;
    // Done parsing initial line and headers. Set decoder result.
    HttpMessageDecoderResult decoderResult = new HttpMessageDecoderResult(lineParser.size, headerParser.size);
    message.setDecoderResult(decoderResult);
    List<String> contentLengthFields = headers.getAll(HttpHeaderNames.CONTENT_LENGTH);
    if (!contentLengthFields.isEmpty()) {
        HttpVersion version = message.protocolVersion();
        boolean isHttp10OrEarlier = version.majorVersion() < 1 || (version.majorVersion() == 1 && version.minorVersion() == 0);
        // Guard against multiple Content-Length headers as stated in
        // https://tools.ietf.org/html/rfc7230#section-3.3.2:
        contentLength = HttpUtil.normalizeAndGetContentLength(contentLengthFields, isHttp10OrEarlier, allowDuplicateContentLengths);
        if (contentLength != -1) {
            headers.set(HttpHeaderNames.CONTENT_LENGTH, contentLength);
        }
    }
    if (isContentAlwaysEmpty(message)) {
        HttpUtil.setTransferEncodingChunked(message, false);
        return State.SKIP_CONTROL_CHARS;
    } else if (HttpUtil.isTransferEncodingChunked(message)) {
        if (!contentLengthFields.isEmpty() && message.protocolVersion() == HttpVersion.HTTP_1_1) {
            handleTransferEncodingChunkedWithContentLength(message);
        }
        return State.READ_CHUNK_SIZE;
    } else if (contentLength() >= 0) {
        return State.READ_FIXED_LENGTH_CONTENT;
    } else {
        return State.READ_VARIABLE_LENGTH_CONTENT;
    }
}
Also used : AppendableCharSequence(io.netty.util.internal.AppendableCharSequence)

Example 3 with AppendableCharSequence

use of io.netty.util.internal.AppendableCharSequence in project netty by netty.

the class HttpObjectDecoder method readTrailingHeaders.

private LastHttpContent readTrailingHeaders(ByteBuf buffer) {
    AppendableCharSequence line = headerParser.parse(buffer);
    if (line == null) {
        return null;
    }
    LastHttpContent trailer = this.trailer;
    if (line.length() == 0 && trailer == null) {
        // before. Just return an empty last content to reduce allocations.
        return LastHttpContent.EMPTY_LAST_CONTENT;
    }
    CharSequence lastHeader = null;
    if (trailer == null) {
        trailer = this.trailer = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, validateHeaders);
    }
    while (line.length() > 0) {
        char firstChar = line.charAtUnsafe(0);
        if (lastHeader != null && (firstChar == ' ' || firstChar == '\t')) {
            List<String> current = trailer.trailingHeaders().getAll(lastHeader);
            if (!current.isEmpty()) {
                int lastPos = current.size() - 1;
                // please do not make one line from below code
                // as it breaks +XX:OptimizeStringConcat optimization
                String lineTrimmed = line.toString().trim();
                String currentLastPos = current.get(lastPos);
                current.set(lastPos, currentLastPos + lineTrimmed);
            }
        } else {
            splitHeader(line);
            CharSequence headerName = name;
            if (!HttpHeaderNames.CONTENT_LENGTH.contentEqualsIgnoreCase(headerName) && !HttpHeaderNames.TRANSFER_ENCODING.contentEqualsIgnoreCase(headerName) && !HttpHeaderNames.TRAILER.contentEqualsIgnoreCase(headerName)) {
                trailer.trailingHeaders().add(headerName, value);
            }
            lastHeader = name;
            // reset name and value fields
            name = null;
            value = null;
        }
        line = headerParser.parse(buffer);
        if (line == null) {
            return null;
        }
    }
    this.trailer = null;
    return trailer;
}
Also used : AppendableCharSequence(io.netty.util.internal.AppendableCharSequence) AppendableCharSequence(io.netty.util.internal.AppendableCharSequence)

Example 4 with AppendableCharSequence

use of io.netty.util.internal.AppendableCharSequence in project netty by netty.

the class StompSubframeDecoder method readLine.

private static String readLine(ByteBuf buffer, int maxLineLength) {
    AppendableCharSequence buf = new AppendableCharSequence(128);
    int lineLength = 0;
    for (; ; ) {
        byte nextByte = buffer.readByte();
        if (nextByte == StompConstants.CR) {
            nextByte = buffer.readByte();
            if (nextByte == StompConstants.LF) {
                return buf.toString();
            }
        } else if (nextByte == StompConstants.LF) {
            return buf.toString();
        } else {
            if (lineLength >= maxLineLength) {
                throw new TooLongFrameException("An STOMP line is larger than " + maxLineLength + " bytes.");
            }
            lineLength++;
            buf.append((char) nextByte);
        }
    }
}
Also used : TooLongFrameException(io.netty.handler.codec.TooLongFrameException) AppendableCharSequence(io.netty.util.internal.AppendableCharSequence)

Aggregations

AppendableCharSequence (io.netty.util.internal.AppendableCharSequence)4 TooLongFrameException (io.netty.handler.codec.TooLongFrameException)2 ByteBuf (io.netty.buffer.ByteBuf)1 PrematureChannelClosureException (io.netty.handler.codec.PrematureChannelClosureException)1