Search in sources :

Example 11 with HttpMetaData

use of io.servicetalk.http.api.HttpMetaData in project servicetalk by apple.

the class AbstractH2DuplexHandlerTest method noContentLength.

private void noContentLength(Variant variant, boolean endStream) {
    variant.writeOutbound(channel);
    Http2Headers headers = variant.setHeaders(new DefaultHttp2Headers());
    channel.writeInbound(headersFrame(headers, endStream));
    HttpMetaData metaData = channel.readInbound();
    if (endStream) {
        assertThat(metaData.headers().contains(CONTENT_LENGTH), is(true));
    } else {
        assertThat(isTransferEncodingChunked(metaData.headers()), is(true));
    }
}
Also used : Http2Headers(io.netty.handler.codec.http2.Http2Headers) DefaultHttp2Headers(io.netty.handler.codec.http2.DefaultHttp2Headers) DefaultHttp2Headers(io.netty.handler.codec.http2.DefaultHttp2Headers) HttpMetaData(io.servicetalk.http.api.HttpMetaData)

Example 12 with HttpMetaData

use of io.servicetalk.http.api.HttpMetaData in project servicetalk by apple.

the class HttpObjectEncoder method write.

@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
    if (msg instanceof HttpMetaData) {
        T metaData = castMetaData(msg);
        final boolean realResponse = !isInterim(metaData);
        if (!realResponse && messageSent) {
            // Discard an "interim message" if it arrives after a "real message" has been sent.
            return;
        }
        if (state == CONTENT_LEN_CHUNKED) {
            // The user didn't write any trailers, so just send the last chunk.
            encodeAndWriteTrailers(ctx, EmptyHttpHeaders.INSTANCE, promise);
        } else if (state > 0) {
            tryTooLittleContent(ctx, msg, promise);
            return;
        } else if (state == -1) {
            unknownContentLengthNewRequest(ctx);
        }
        onMetaData(ctx, metaData);
        if (realResponse) {
            // Notify the CloseHandler only about "real" messages. We don't expose "interim messages", like 1xx
            // responses to the user, and handle them internally.
            messageSent = true;
            closeHandler.protocolPayloadBeginOutbound(ctx);
            if (shouldClose(metaData)) {
                closeHandler.protocolClosingOutbound(ctx);
            }
        }
        // We prefer a direct allocation here because it is expected the resulted encoded Buffer will be written
        // to a socket. In order to do the write to the socket the memory typically needs to be allocated in direct
        // memory and will be copied to direct memory if not. Using a direct buffer will avoid the copy.
        ByteBuf byteBuf = ctx.alloc().directBuffer((int) headersEncodedSizeAccumulator);
        try {
            Buffer stBuf = newBufferFrom(byteBuf);
            // Encode the message.
            encodeInitialLine(ctx, stBuf, metaData);
            if (isContentAlwaysEmpty(metaData)) {
                state = CONTENT_LEN_EMPTY;
                if (realResponse) {
                    signalProtocolPayloadEndOutbound(ctx, promise);
                }
            } else if (isTransferEncodingChunked(metaData.headers())) {
                state = CONTENT_LEN_CHUNKED;
            } else {
                state = getContentLength(metaData);
                assert state > CONTENT_LEN_LARGEST_VALUE;
                if (state == 0) {
                    contentLenConsumed(ctx, promise);
                }
            }
            sanitizeHeadersBeforeEncode(metaData);
            encodeHeaders(metaData.headers(), byteBuf, stBuf);
            writeShortBE(byteBuf, CRLF_SHORT);
            headersEncodedSizeAccumulator = HEADERS_WEIGHT_NEW * padSizeForAccumulation(byteBuf.readableBytes()) + HEADERS_WEIGHT_HISTORICAL * headersEncodedSizeAccumulator;
        } catch (Throwable e) {
            // Encoding of meta-data can fail or cause expansion of the initial ByteBuf capacity that can fail
            byteBuf.release();
            tryIoException(ctx, e, promise);
            return;
        }
        if (realResponse) {
            ctx.write(byteBuf, promise);
        } else {
            // All "interim messages" have to be flushed right away.
            ctx.writeAndFlush(byteBuf, promise);
        }
    } else if (msg instanceof Buffer) {
        final Buffer stBuffer = (Buffer) msg;
        final int readableBytes = stBuffer.readableBytes();
        if (readableBytes <= 0) {
            ctx.write(EMPTY_BUFFER, promise);
        } else if (state == CONTENT_LEN_CHUNKED) {
            PromiseCombiner promiseCombiner = new PromiseCombiner(ctx.executor());
            encodeChunkedContent(ctx, stBuffer, stBuffer.readableBytes(), promiseCombiner);
            promiseCombiner.finish(promise);
        } else if (state <= CONTENT_LEN_LARGEST_VALUE || state >= 0 && (state -= readableBytes) < 0) {
            // state may be <0 if there is no content-length or transfer-encoding, so let this pass through, but if
            // state would go negative (or already zeroed) then fail.
            tryTooMuchContent(ctx, readableBytes, promise);
        } else {
            if (state == 0) {
                contentLenConsumed(ctx, promise);
            }
            ctx.write(encodeAndRetain(stBuffer), promise);
        }
    } else if (msg instanceof HttpHeaders) {
        final boolean isChunked = state == CONTENT_LEN_CHUNKED;
        state = CONTENT_LEN_INIT;
        final HttpHeaders trailers = (HttpHeaders) msg;
        if (isChunked) {
            signalProtocolPayloadEndOutbound(ctx, promise);
            encodeAndWriteTrailers(ctx, trailers, promise);
        } else if (!trailers.isEmpty()) {
            tryFailNonEmptyTrailers(ctx, trailers, promise);
        } else if (state > 0) {
            tryTooLittleContent(ctx, promise);
        } else {
            // Allow trailers to be written as a marker indicating the request is done.
            if (state != CONTENT_LEN_CONSUMED) {
                signalProtocolPayloadEndOutbound(ctx, promise);
            }
            state = CONTENT_LEN_INIT;
            ctx.write(EMPTY_BUFFER, promise);
        }
    }
}
Also used : Unpooled.unreleasableBuffer(io.netty.buffer.Unpooled.unreleasableBuffer) Unpooled.wrappedBuffer(io.netty.buffer.Unpooled.wrappedBuffer) CharSequences.unwrapBuffer(io.servicetalk.buffer.api.CharSequences.unwrapBuffer) Unpooled.directBuffer(io.netty.buffer.Unpooled.directBuffer) Buffer(io.servicetalk.buffer.api.Buffer) HttpHeaders(io.servicetalk.http.api.HttpHeaders) EmptyHttpHeaders(io.servicetalk.http.api.EmptyHttpHeaders) PromiseCombiner(io.netty.util.concurrent.PromiseCombiner) ByteBuf(io.netty.buffer.ByteBuf) HttpMetaData(io.servicetalk.http.api.HttpMetaData)

Example 13 with HttpMetaData

use of io.servicetalk.http.api.HttpMetaData in project servicetalk by apple.

the class HttpObjectDecoderTest method smuggleTransferEncoding.

void smuggleTransferEncoding(boolean smuggleBeforeTransferEncoding, boolean crlf) {
    EmbeddedChannel channel = channel(crlf);
    String br = br(crlf);
    assertThrows(DecoderException.class, () -> writeMsg(startLineForContent() + br + "Host: servicetalk.io" + br + // [1] https://tools.ietf.org/html/rfc7230#section-3.3.3
    (smuggleBeforeTransferEncoding ? "Smuggled: " + startLine() + br + br + TRANSFER_ENCODING + ":" + CHUNKED + br : TRANSFER_ENCODING + ":" + CHUNKED + br + "Smuggled: " + startLine() + br + br) + "Connection: keep-alive" + br + br, channel));
    HttpMetaData metaData = assertStartLineForContent(channel);
    assertSingleHeaderValue(metaData.headers(), HOST, "servicetalk.io");
    assertSingleHeaderValue(metaData.headers(), "Smuggled", startLine());
}
Also used : EmbeddedChannel(io.netty.channel.embedded.EmbeddedChannel) Matchers.emptyString(org.hamcrest.Matchers.emptyString) Integer.toHexString(java.lang.Integer.toHexString) HttpMetaData(io.servicetalk.http.api.HttpMetaData)

Example 14 with HttpMetaData

use of io.servicetalk.http.api.HttpMetaData in project servicetalk by apple.

the class HttpObjectDecoderTest method smuggleZeroContentLength.

private void smuggleZeroContentLength(boolean smuggleBeforeContentLength, boolean crlf) {
    EmbeddedChannel channel = channel(crlf);
    String br = br(crlf);
    DecoderException e = assertThrows(DecoderException.class, () -> writeMsg(startLine() + br + "Host: servicetalk.io" + br + // [1] https://tools.ietf.org/html/rfc7230#section-3.3.3
    (smuggleBeforeContentLength ? "Smuggled: " + startLine() + br + br + "Content-Length: 0" + br : "Content-Length: 0" + br + "Smuggled: " + startLine() + br + br) + "Connection: keep-alive" + br + br, channel));
    assertThat(e.getMessage(), startsWith("Invalid start-line"));
    HttpMetaData metaData = assertStartLine(channel);
    assertSingleHeaderValue(metaData.headers(), HOST, "servicetalk.io");
    assertSingleHeaderValue(metaData.headers(), "Smuggled", startLine());
    assertEmptyTrailers(channel);
}
Also used : DecoderException(io.netty.handler.codec.DecoderException) EmbeddedChannel(io.netty.channel.embedded.EmbeddedChannel) Matchers.emptyString(org.hamcrest.Matchers.emptyString) Integer.toHexString(java.lang.Integer.toHexString) HttpMetaData(io.servicetalk.http.api.HttpMetaData)

Example 15 with HttpMetaData

use of io.servicetalk.http.api.HttpMetaData in project servicetalk by apple.

the class HttpObjectDecoderTest method validateWithContent.

final HttpMetaData validateWithContent(int expectedContentLength, boolean containsTrailers, EmbeddedChannel channel) {
    HttpMetaData metaData = assertStartLineForContent(channel);
    assertStandardHeaders(metaData.headers());
    if (expectedContentLength > 0) {
        assertSingleHeaderValue(metaData.headers(), CONTENT_LENGTH, String.valueOf(expectedContentLength));
        HttpHeaders trailers = assertPayloadSize(expectedContentLength, channel);
        assertThat("Trailers are not empty", trailers, nullValue());
    } else if (expectedContentLength == 0) {
        if (containsTrailers) {
            assertSingleHeaderValue(metaData.headers(), TRANSFER_ENCODING, CHUNKED);
            HttpHeaders trailers = channel.readInbound();
            assertSingleHeaderValue(trailers, "TrailerStatus", "good");
        } else {
            assertSingleHeaderValue(metaData.headers(), CONTENT_LENGTH, "0");
            assertEmptyTrailers(channel);
        }
    } else {
        assertThat("No 'transfer-encoding: chunked' header", isTransferEncodingChunked(metaData.headers()), is(true));
        HttpHeaders trailers = assertPayloadSize(-expectedContentLength, channel);
        if (containsTrailers) {
            assertThat(trailers, not(nullValue()));
            assertSingleHeaderValue(trailers, "TrailerStatus", "good");
        } else if (trailers != null) {
            assertThat("Trailers are not empty", trailers.isEmpty(), is(true));
        }
    }
    assertFalse(channel.finishAndReleaseAll());
    return metaData;
}
Also used : HttpHeaders(io.servicetalk.http.api.HttpHeaders) HttpMetaData(io.servicetalk.http.api.HttpMetaData)

Aggregations

HttpMetaData (io.servicetalk.http.api.HttpMetaData)22 Test (org.junit.jupiter.api.Test)8 Integer.toHexString (java.lang.Integer.toHexString)7 EmbeddedChannel (io.netty.channel.embedded.EmbeddedChannel)6 Buffer (io.servicetalk.buffer.api.Buffer)6 HttpHeaders (io.servicetalk.http.api.HttpHeaders)6 Matchers.emptyString (org.hamcrest.Matchers.emptyString)6 DefaultHttp2Headers (io.netty.handler.codec.http2.DefaultHttp2Headers)4 Http2Headers (io.netty.handler.codec.http2.Http2Headers)4 EmptyHttpHeaders (io.servicetalk.http.api.EmptyHttpHeaders)4 ParameterizedTest (org.junit.jupiter.params.ParameterizedTest)3 EnumSource (org.junit.jupiter.params.provider.EnumSource)3 DefaultHttp2DataFrame (io.netty.handler.codec.http2.DefaultHttp2DataFrame)2 DefaultHttp2HeadersFrame (io.netty.handler.codec.http2.DefaultHttp2HeadersFrame)2 Http2HeadersFrame (io.netty.handler.codec.http2.Http2HeadersFrame)2 ByteBuf (io.netty.buffer.ByteBuf)1 Unpooled.directBuffer (io.netty.buffer.Unpooled.directBuffer)1 Unpooled.unreleasableBuffer (io.netty.buffer.Unpooled.unreleasableBuffer)1 Unpooled.wrappedBuffer (io.netty.buffer.Unpooled.wrappedBuffer)1 DecoderException (io.netty.handler.codec.DecoderException)1