Search in sources :

Example 1 with Buffer

use of com.hotels.styx.api.Buffer in project styx by ExpediaGroup.

the class NettyToStyxResponsePropagator method channelRead0.

@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
    FlowControllingHttpContentProducer producer = getContentProducer(ctx);
    if (msg instanceof io.netty.handler.codec.http.HttpResponse) {
        io.netty.handler.codec.http.HttpResponse nettyResponse = (io.netty.handler.codec.http.HttpResponse) msg;
        if (!responseReceived.compareAndSet(false, true)) {
            LOGGER.warn("Unexpected additional response received: " + nettyResponse);
            ctx.channel().close();
            return;
        }
        if (nettyResponse.getDecoderResult().isFailure()) {
            emitResponseError(new BadHttpResponseException(origin, nettyResponse.getDecoderResult().cause()));
            return;
        }
        ctx.channel().config().setAutoRead(false);
        ctx.channel().read();
        // Can be started with flow controlling disabled
        EventLoop eventLoop = ctx.channel().eventLoop();
        Publisher<Buffer> contentPublisher = new ContentPublisher(eventLoop, producer);
        if ("close".equalsIgnoreCase(nettyResponse.headers().get(CONNECTION))) {
            toBeClosed = true;
        }
        LiveHttpResponse response = toStyxResponse(nettyResponse, contentPublisher, origin);
        this.sink.next(response);
    }
    if (msg instanceof HttpContent) {
        ByteBuf content = ((ByteBufHolder) msg).content();
        if (content.isReadable()) {
            producer.newChunk(retain(content));
        }
        if (msg instanceof LastHttpContent) {
            // Note: Netty may send a LastHttpContent as a response to TCP connection close.
            // In this case channelReadComplete event will _not_ follow the LastHttpContent.
            producer.lastHttpContent();
            if (toBeClosed) {
                ctx.channel().close();
            }
        }
    }
}
Also used : Buffer(com.hotels.styx.api.Buffer) LiveHttpResponse(com.hotels.styx.api.LiveHttpResponse) LiveHttpResponse(com.hotels.styx.api.LiveHttpResponse) ByteBuf(io.netty.buffer.ByteBuf) LastHttpContent(io.netty.handler.codec.http.LastHttpContent) EventLoop(io.netty.channel.EventLoop) ByteBufHolder(io.netty.buffer.ByteBufHolder) BadHttpResponseException(com.hotels.styx.client.BadHttpResponseException) LastHttpContent(io.netty.handler.codec.http.LastHttpContent) HttpContent(io.netty.handler.codec.http.HttpContent)

Example 2 with Buffer

use of com.hotels.styx.api.Buffer in project styx by ExpediaGroup.

the class StyxHostHttpClientTest method terminatesConnectionDueToUnsubscribedBody.

@Test
public void terminatesConnectionDueToUnsubscribedBody() {
    TestPublisher<Buffer> testPublisher = TestPublisher.create();
    Connection connection = mockConnection(just(LiveHttpResponse.response(OK).body(new ByteStream(testPublisher)).build()));
    ConnectionPool pool = mockPool(connection);
    Context context = mockContext();
    AtomicReference<LiveHttpResponse> receivedResponse = new AtomicReference<>();
    StyxHostHttpClient hostClient = new StyxHostHttpClient(pool);
    StepVerifier.create(hostClient.sendRequest(request, context)).consumeNextWith(receivedResponse::set).expectComplete().verify();
    StepVerifier.create(receivedResponse.get().body()).thenCancel().verify();
    verify(pool).closeConnection(any(Connection.class));
    verify(context).add(ORIGINID_CONTEXT_KEY, Id.id("mockorigin"));
}
Also used : Buffer(com.hotels.styx.api.Buffer) ConnectionPool(com.hotels.styx.client.connectionpool.ConnectionPool) HttpInterceptorContext(com.hotels.styx.server.HttpInterceptorContext) Support.requestContext(com.hotels.styx.support.Support.requestContext) Context(com.hotels.styx.api.HttpInterceptor.Context) ByteStream(com.hotels.styx.api.ByteStream) AtomicReference(java.util.concurrent.atomic.AtomicReference) LiveHttpResponse(com.hotels.styx.api.LiveHttpResponse) Test(org.junit.jupiter.api.Test)

Example 3 with Buffer

use of com.hotels.styx.api.Buffer in project styx by ExpediaGroup.

the class HttpResponseWriterTest method releasesContentChunksWhenFailsToConvertToNettyHeaders.

@Test
public void releasesContentChunksWhenFailsToConvertToNettyHeaders() throws Exception {
    CaptureHttpResponseWriteEventsHandler writeEventsCollector = new CaptureHttpResponseWriteEventsHandler();
    Buffer chunk1 = new Buffer("aaa", UTF_8);
    Buffer chunk2 = new Buffer("aaa", UTF_8);
    AtomicBoolean unsubscribed = new AtomicBoolean(false);
    EmbeddedChannel ch = new EmbeddedChannel(new CaptureChannelArgumentsHandler(channelArgs), writeEventsCollector, new SimpleChannelInboundHandler<LiveHttpResponse>() {

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, LiveHttpResponse response) throws Exception {
            HttpResponseWriter writer = new HttpResponseWriter(ctx, httpResponse -> {
                throw new RuntimeException();
            });
            CompletableFuture<Void> future = writer.write(response);
            contentObservable.onNext(chunk1);
            contentObservable.onNext(chunk2);
            contentObservable.onComplete();
            assertThat(future.isDone(), is(true));
            assertThat(toByteBuf(chunk1).refCnt(), is(0));
            assertThat(toByteBuf(chunk2).refCnt(), is(0));
            channelRead.set(true);
        }
    });
    LiveHttpResponse.Builder response = response(OK).cookies(responseCookie(",,,,", ",,,,").build());
    ch.writeInbound(response.body(new ByteStream(contentObservable.doOnCancel(() -> unsubscribed.set(true)))).build());
    assertThat(channelRead.get(), is(true));
}
Also used : Buffer(com.hotels.styx.api.Buffer) Assertions.assertThrows(org.junit.jupiter.api.Assertions.assertThrows) BeforeEach(org.junit.jupiter.api.BeforeEach) LiveHttpResponse.response(com.hotels.styx.api.LiveHttpResponse.response) LoggingHandler(io.netty.handler.logging.LoggingHandler) Buffers.toByteBuf(com.hotels.styx.api.Buffers.toByteBuf) LoggingTestSupport(com.hotels.styx.support.matchers.LoggingTestSupport) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) Origin.newOriginBuilder(com.hotels.styx.api.extension.Origin.newOriginBuilder) CompletableFuture(java.util.concurrent.CompletableFuture) Disabled(org.junit.jupiter.api.Disabled) ArrayList(java.util.ArrayList) ChannelHandlerContext(io.netty.channel.ChannelHandlerContext) LoggingEventMatcher.loggingEvent(com.hotels.styx.support.matchers.LoggingEventMatcher.loggingEvent) ChannelPromise(io.netty.channel.ChannelPromise) EmitterProcessor(reactor.core.publisher.EmitterProcessor) Is.is(org.hamcrest.core.Is.is) MatcherAssert.assertThat(org.hamcrest.MatcherAssert.assertThat) InetAddress.getLoopbackAddress(java.net.InetAddress.getLoopbackAddress) ChannelOutboundHandlerAdapter(io.netty.channel.ChannelOutboundHandlerAdapter) UTF_8(java.nio.charset.StandardCharsets.UTF_8) EmbeddedChannel(io.netty.channel.embedded.EmbeddedChannel) TransportLostException(com.hotels.styx.api.exceptions.TransportLostException) MILLISECONDS(java.util.concurrent.TimeUnit.MILLISECONDS) EMPTY_LAST_CONTENT(io.netty.handler.codec.http.LastHttpContent.EMPTY_LAST_CONTENT) InetSocketAddress(java.net.InetSocketAddress) Buffer(com.hotels.styx.api.Buffer) Test(org.junit.jupiter.api.Test) ExecutionException(java.util.concurrent.ExecutionException) Matchers.instanceOf(org.hamcrest.Matchers.instanceOf) Level(ch.qos.logback.classic.Level) AfterEach(org.junit.jupiter.api.AfterEach) List(java.util.List) LiveHttpResponse(com.hotels.styx.api.LiveHttpResponse) ByteStream(com.hotels.styx.api.ByteStream) SimpleChannelInboundHandler(io.netty.channel.SimpleChannelInboundHandler) DefaultHttpResponse(io.netty.handler.codec.http.DefaultHttpResponse) OK(com.hotels.styx.api.HttpResponseStatus.OK) ResponseCookie.responseCookie(com.hotels.styx.api.ResponseCookie.responseCookie) Queue(java.util.Queue) ArrayDeque(java.util.ArrayDeque) EmbeddedChannel(io.netty.channel.embedded.EmbeddedChannel) ChannelHandlerContext(io.netty.channel.ChannelHandlerContext) LiveHttpResponse(com.hotels.styx.api.LiveHttpResponse) TransportLostException(com.hotels.styx.api.exceptions.TransportLostException) ExecutionException(java.util.concurrent.ExecutionException) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) CompletableFuture(java.util.concurrent.CompletableFuture) ByteStream(com.hotels.styx.api.ByteStream) Test(org.junit.jupiter.api.Test)

Example 4 with Buffer

use of com.hotels.styx.api.Buffer in project styx by ExpediaGroup.

the class HttpResponseWriterTest method ignoresLastEmptyHttpContentWriteOutcome.

@Test
public void ignoresLastEmptyHttpContentWriteOutcome() throws Exception {
    /*
         * It is necessary to ignore outcome of LastEmptyHttpContent.
         * This is because the full response would have been already sent,
         * and the remote end may have closed the connection before LastEmptyHttpContent
         * would have been written. This would result in an unnecessary
         * rejection of response writer future, even when the response in
         * fact was correctly sent. The following diagram illustrates the
         * scenario:
         *
         * 1. Styx HTTP Response writer writes the headers.
         *
         * 2. Styx HTTP Response writer writes the remaining content.
         *
         * 3. Remote receives the full response and closes the connection.
         *
         * 4. Styx HTTP Response Writer attempts to write the last empty HTTP
         *    content chunk. This will now fail because the TCP connection has
         *    closed.
         *
         * 5. HttpResponseWriter future completes unsuccessfully.
         *
         */
    EmbeddedChannel ch = new EmbeddedChannel(new CaptureChannelArgumentsHandler(channelArgs), new LoggingHandler(), new SimpleChannelInboundHandler<LiveHttpResponse>() {

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, LiveHttpResponse response) throws Exception {
            HttpResponseWriter writer = new HttpResponseWriter(ctx);
            CompletableFuture<Void> future = writer.write(response);
            // For response headers
            writeAck(channelArgs);
            assertThat(future.isDone(), is(false));
            contentObservable.onNext(new Buffer("aaa", UTF_8));
            // For content chunk
            writeAck(channelArgs);
            assertThat(future.isDone(), is(false));
            contentObservable.onComplete();
            // For EMPTY_LAST_CHUNK
            writeError(channelArgs);
            assertThat(future.isDone(), is(true));
            assertThat(future.isCompletedExceptionally(), is(false));
            channelRead.set(true);
        }
    });
    ch.writeInbound(response(OK).body(new ByteStream(contentObservable)).build());
    assertThat(channelRead.get(), is(true));
}
Also used : Buffer(com.hotels.styx.api.Buffer) LoggingHandler(io.netty.handler.logging.LoggingHandler) CompletableFuture(java.util.concurrent.CompletableFuture) ByteStream(com.hotels.styx.api.ByteStream) EmbeddedChannel(io.netty.channel.embedded.EmbeddedChannel) ChannelHandlerContext(io.netty.channel.ChannelHandlerContext) LiveHttpResponse(com.hotels.styx.api.LiveHttpResponse) TransportLostException(com.hotels.styx.api.exceptions.TransportLostException) ExecutionException(java.util.concurrent.ExecutionException) Test(org.junit.jupiter.api.Test)

Example 5 with Buffer

use of com.hotels.styx.api.Buffer in project styx by ExpediaGroup.

the class HttpResponseWriterTest method completesFutureOnlyAfterContentObservableIsCompleted.

@Test
public void completesFutureOnlyAfterContentObservableIsCompleted() throws Exception {
    EmbeddedChannel ch = new EmbeddedChannel(new SimpleChannelInboundHandler<LiveHttpResponse>() {

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, LiveHttpResponse response) throws Exception {
            HttpResponseWriter writer = new HttpResponseWriter(ctx);
            CompletableFuture<Void> future = writer.write(response);
            assertThat(future.isDone(), is(false));
            contentObservable.onNext(new Buffer("aaa", UTF_8));
            assertThat(future.isDone(), is(false));
            contentObservable.onComplete();
            assertThat(future.isDone(), is(true));
            channelRead.set(true);
        }
    });
    ch.writeInbound(response(OK).body(new ByteStream(contentObservable)).build());
    assertThat(channelRead.get(), is(true));
}
Also used : Buffer(com.hotels.styx.api.Buffer) CompletableFuture(java.util.concurrent.CompletableFuture) ByteStream(com.hotels.styx.api.ByteStream) EmbeddedChannel(io.netty.channel.embedded.EmbeddedChannel) ChannelHandlerContext(io.netty.channel.ChannelHandlerContext) LiveHttpResponse(com.hotels.styx.api.LiveHttpResponse) TransportLostException(com.hotels.styx.api.exceptions.TransportLostException) ExecutionException(java.util.concurrent.ExecutionException) Test(org.junit.jupiter.api.Test)

Aggregations

Buffer (com.hotels.styx.api.Buffer)10 LiveHttpResponse (com.hotels.styx.api.LiveHttpResponse)10 ByteStream (com.hotels.styx.api.ByteStream)8 ChannelHandlerContext (io.netty.channel.ChannelHandlerContext)7 CompletableFuture (java.util.concurrent.CompletableFuture)7 Test (org.junit.jupiter.api.Test)7 TransportLostException (com.hotels.styx.api.exceptions.TransportLostException)6 EmbeddedChannel (io.netty.channel.embedded.EmbeddedChannel)6 ExecutionException (java.util.concurrent.ExecutionException)6 LoggingHandler (io.netty.handler.logging.LoggingHandler)3 ByteBuf (io.netty.buffer.ByteBuf)2 EMPTY_LAST_CONTENT (io.netty.handler.codec.http.LastHttpContent.EMPTY_LAST_CONTENT)2 InetSocketAddress (java.net.InetSocketAddress)2 Level (ch.qos.logback.classic.Level)1 Buffers (com.hotels.styx.api.Buffers)1 Buffers.toByteBuf (com.hotels.styx.api.Buffers.toByteBuf)1 Context (com.hotels.styx.api.HttpInterceptor.Context)1 HttpResponseStatus (com.hotels.styx.api.HttpResponseStatus)1 OK (com.hotels.styx.api.HttpResponseStatus.OK)1 LiveHttpResponse.response (com.hotels.styx.api.LiveHttpResponse.response)1