Search in sources :

Example 71 with LiveHttpResponse

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

the class StandardHttpPipelineTest method contextValuesAddedBeforeRequestCanBeRetrievedAfterward.

@Test
public void contextValuesAddedBeforeRequestCanBeRetrievedAfterward() {
    HttpInterceptor addsToContext = (request, chain) -> {
        chain.context().add("contextValue", "expected");
        return chain.proceed(request);
    };
    AtomicReference<String> foundInContext = new AtomicReference<>();
    HttpInterceptor takesFromContext = (request, chain) -> chain.proceed(request).map(response -> {
        foundInContext.set(chain.context().get("contextValue", String.class));
        return response;
    });
    StandardHttpPipeline pipeline = pipeline(addsToContext, takesFromContext);
    LiveHttpResponse response = sendRequestTo(pipeline);
    assertThat(response.status(), is(OK));
    assertThat(foundInContext.get(), is("expected"));
}
Also used : Assertions.assertThrows(org.junit.jupiter.api.Assertions.assertThrows) RequestTracker(com.hotels.styx.server.track.RequestTracker) LiveHttpResponse.response(com.hotels.styx.api.LiveHttpResponse.response) AtomicReference(java.util.concurrent.atomic.AtomicReference) ArrayList(java.util.ArrayList) Collections.singletonList(java.util.Collections.singletonList) HttpInterceptorContext(com.hotels.styx.server.HttpInterceptorContext) Support.requestContext(com.hotels.styx.support.Support.requestContext) Arrays.asList(java.util.Arrays.asList) Matchers.nullValue(org.hamcrest.Matchers.nullValue) LiveHttpRequest.get(com.hotels.styx.api.LiveHttpRequest.get) MatcherAssert.assertThat(org.hamcrest.MatcherAssert.assertThat) MethodSource(org.junit.jupiter.params.provider.MethodSource) Eventual(com.hotels.styx.api.Eventual) HttpHandler(com.hotels.styx.api.HttpHandler) Mono(reactor.core.publisher.Mono) Arguments(org.junit.jupiter.params.provider.Arguments) InetSocketAddress(java.net.InetSocketAddress) HttpInterceptor(com.hotels.styx.api.HttpInterceptor) Test(org.junit.jupiter.api.Test) Consumer(java.util.function.Consumer) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest) List(java.util.List) Stream(java.util.stream.Stream) LiveHttpResponse(com.hotels.styx.api.LiveHttpResponse) Matchers.contains(org.hamcrest.Matchers.contains) OK(com.hotels.styx.api.HttpResponseStatus.OK) Matchers.is(org.hamcrest.Matchers.is) HttpInterceptor(com.hotels.styx.api.HttpInterceptor) AtomicReference(java.util.concurrent.atomic.AtomicReference) LiveHttpResponse(com.hotels.styx.api.LiveHttpResponse) Test(org.junit.jupiter.api.Test) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest)

Example 72 with LiveHttpResponse

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

the class StandardHttpPipelineTest method interceptorsCanPassInformationThroughContextAfterRequest.

@Test
public void interceptorsCanPassInformationThroughContextAfterRequest() {
    HttpInterceptor addsToContext = (request, chain) -> chain.proceed(request).map(response -> {
        chain.context().add("contextValue", "expected");
        return response;
    });
    AtomicReference<String> foundInContext = new AtomicReference<>();
    HttpInterceptor takesFromContext = (request, chain) -> chain.proceed(request).map(response -> {
        foundInContext.set(chain.context().get("contextValue", String.class));
        return response;
    });
    // add + take happens on the way back, so order must be reserved
    StandardHttpPipeline pipeline = pipeline(takesFromContext, addsToContext);
    LiveHttpResponse response = sendRequestTo(pipeline);
    assertThat(response.status(), is(OK));
    assertThat(foundInContext.get(), is("expected"));
}
Also used : Assertions.assertThrows(org.junit.jupiter.api.Assertions.assertThrows) RequestTracker(com.hotels.styx.server.track.RequestTracker) LiveHttpResponse.response(com.hotels.styx.api.LiveHttpResponse.response) AtomicReference(java.util.concurrent.atomic.AtomicReference) ArrayList(java.util.ArrayList) Collections.singletonList(java.util.Collections.singletonList) HttpInterceptorContext(com.hotels.styx.server.HttpInterceptorContext) Support.requestContext(com.hotels.styx.support.Support.requestContext) Arrays.asList(java.util.Arrays.asList) Matchers.nullValue(org.hamcrest.Matchers.nullValue) LiveHttpRequest.get(com.hotels.styx.api.LiveHttpRequest.get) MatcherAssert.assertThat(org.hamcrest.MatcherAssert.assertThat) MethodSource(org.junit.jupiter.params.provider.MethodSource) Eventual(com.hotels.styx.api.Eventual) HttpHandler(com.hotels.styx.api.HttpHandler) Mono(reactor.core.publisher.Mono) Arguments(org.junit.jupiter.params.provider.Arguments) InetSocketAddress(java.net.InetSocketAddress) HttpInterceptor(com.hotels.styx.api.HttpInterceptor) Test(org.junit.jupiter.api.Test) Consumer(java.util.function.Consumer) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest) List(java.util.List) Stream(java.util.stream.Stream) LiveHttpResponse(com.hotels.styx.api.LiveHttpResponse) Matchers.contains(org.hamcrest.Matchers.contains) OK(com.hotels.styx.api.HttpResponseStatus.OK) Matchers.is(org.hamcrest.Matchers.is) HttpInterceptor(com.hotels.styx.api.HttpInterceptor) AtomicReference(java.util.concurrent.atomic.AtomicReference) LiveHttpResponse(com.hotels.styx.api.LiveHttpResponse) Test(org.junit.jupiter.api.Test) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest)

Example 73 with LiveHttpResponse

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

the class HttpPipelineHandler method exceptionToResponse.

private LiveHttpResponse exceptionToResponse(Throwable cause, LiveHttpRequest request, CharSequence originsHeaderName) {
    HttpResponseStatus status = status(cause instanceof PluginException ? cause.getCause() : cause);
    String message = status.code() >= 500 ? "Site temporarily unavailable." : status.description();
    LiveHttpResponse.Transformer builder = responseEnhancer.enhance(response(status).body(new ByteStream(Flux.just(new Buffer(message, UTF_8)))).build().newBuilder(), request).header(CONTENT_LENGTH, message.getBytes(UTF_8).length).header(CONNECTION, "close");
    if (originsHeaderName != null && originFromException(cause) != null) {
        return builder.header(originsHeaderName, originFromException(cause)).build();
    } else {
        return builder.build();
    }
}
Also used : Buffer(com.hotels.styx.api.Buffer) HttpResponseStatus(com.hotels.styx.api.HttpResponseStatus) PluginException(com.hotels.styx.api.plugins.spi.PluginException) ByteStream(com.hotels.styx.api.ByteStream) LiveHttpResponse(com.hotels.styx.api.LiveHttpResponse)

Example 74 with LiveHttpResponse

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

the class HttpPipelineHandler method handleChannelException.

private State handleChannelException(ChannelHandlerContext ctx, Throwable cause) {
    Throwable sslException = sslException(cause);
    if (sslException != null) {
        if (ctx.channel().isActive()) {
            ctx.channel().close();
        }
        LOGGER.info("SSL handshake failure from incoming connection " + "cause=\"{}\", " + "serverAddress={}, " + "clientAddress={}", new Object[] { sslException.getMessage(), ctx.channel().localAddress(), ctx.channel().remoteAddress() });
        return TERMINATED;
    }
    if (!isIoException(cause)) {
        LiveHttpResponse response = exceptionToResponse(cause, ongoingRequest, originsHeaderName);
        httpErrorStatusListener.proxyErrorOccurred(response.status(), cause);
        if (ctx.channel().isActive()) {
            respondAndClose(ctx, response);
        }
    }
    return TERMINATED;
}
Also used : LiveHttpResponse(com.hotels.styx.api.LiveHttpResponse)

Example 75 with LiveHttpResponse

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

the class HttpResponseWriter method write.

// CHECKSTYLE:OFF
public CompletableFuture<Void> write(LiveHttpResponse response) {
    CompletableFuture<Void> future = new CompletableFuture<>();
    try {
        writeHeaders(response).addListener((ChannelFutureListener) writeOp -> {
            if (writeOp.isSuccess()) {
                writeOpsAcked.incrementAndGet();
            } else {
                LOGGER.warn("Unable to send response headers. Written content bytes {}/{} (ackd/sent). Write events {}/{} (ackd/writes). Exception={}", new Object[] { contentBytesAcked.get(), contentBytesWritten.get(), writeOpsAcked.get(), writeOps.get(), writeOp.cause() });
                future.completeExceptionally(writeOp.cause());
            }
        });
        response.body().subscribe(new BaseSubscriber<Buffer>() {

            @Override
            public void hookOnSubscribe(Subscription subscription) {
                future.handle((ignore, cause) -> {
                    if (future.isCompletedExceptionally() && cause instanceof CancellationException) {
                        subscription.cancel();
                    }
                    return null;
                });
                subscription.request(1);
            }

            @Override
            public void hookOnComplete() {
                if (!future.isDone()) {
                    nettyWriteAndFlush(EMPTY_LAST_CONTENT).addListener((ChannelFutureListener) this::onWriteEmptyLastChunkOutcome);
                    contentCompleted.set(true);
                    completeIfAllSent(future);
                }
            }

            @Override
            public void hookOnError(Throwable cause) {
                LOGGER.warn("Content observable error. Written content bytes {}/{} (ackd/sent). Write events {}/{} (ackd/writes). Exception={}", new Object[] { contentBytesAcked.get(), contentBytesWritten.get(), writeOpsAcked.get(), writeOps.get(), cause });
                future.completeExceptionally(cause);
            }

            @Override
            public void hookOnNext(Buffer buffer) {
                ByteBuf byteBuf = Buffers.toByteBuf(buffer);
                if (future.isDone()) {
                    byteBuf.release();
                } else {
                    long bufSize = (long) byteBuf.readableBytes();
                    contentBytesWritten.addAndGet(bufSize);
                    nettyWriteAndFlush(new DefaultHttpContent(byteBuf)).addListener(it -> onWriteOutcome((ChannelFuture) it, bufSize));
                }
            }

            private void onWriteOutcome(ChannelFuture writeOp, long bufSize) {
                if (writeOp.isSuccess()) {
                    contentBytesAcked.addAndGet(bufSize);
                    writeOpsAcked.incrementAndGet();
                    request(1);
                    completeIfAllSent(future);
                } else if (!future.isDone()) {
                    // Suppress messages if future has already failed, or completed for other reason:
                    cancel();
                    LOGGER.warn("Write error. Written content bytes {}/{} (ackd/sent). Write events {}/{} (ackd/writes), Exception={}", new Object[] { contentBytesAcked.get(), contentBytesWritten.get(), writeOpsAcked.get(), writeOps.get(), response, writeOp.cause() });
                    future.completeExceptionally(writeOp.cause());
                }
            }

            private void onWriteEmptyLastChunkOutcome(ChannelFuture writeOp) {
                writeOpsAcked.incrementAndGet();
                completeIfAllSent(future);
                cancel();
            }
        });
        return future;
    } catch (Throwable cause) {
        LOGGER.warn("Failed to convert response headers. response={}, Cause={}", new Object[] { response, cause });
        toObservable(response.body()).forEach(it -> Buffers.toByteBuf(it).release());
        future.completeExceptionally(cause);
        return future;
    }
}
Also used : RxReactiveStreams.toObservable(rx.RxReactiveStreams.toObservable) Logger(org.slf4j.Logger) CancellationException(java.util.concurrent.CancellationException) LoggerFactory(org.slf4j.LoggerFactory) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) CompletableFuture(java.util.concurrent.CompletableFuture) EMPTY_LAST_CONTENT(io.netty.handler.codec.http.LastHttpContent.EMPTY_LAST_CONTENT) Buffer(com.hotels.styx.api.Buffer) ChannelFuture(io.netty.channel.ChannelFuture) BaseSubscriber(reactor.core.publisher.BaseSubscriber) DefaultHttpContent(io.netty.handler.codec.http.DefaultHttpContent) ChannelHandlerContext(io.netty.channel.ChannelHandlerContext) AtomicLong(java.util.concurrent.atomic.AtomicLong) LiveHttpResponse(com.hotels.styx.api.LiveHttpResponse) ByteBuf(io.netty.buffer.ByteBuf) Buffers(com.hotels.styx.api.Buffers) ChannelFutureListener(io.netty.channel.ChannelFutureListener) Subscription(org.reactivestreams.Subscription) Objects.requireNonNull(java.util.Objects.requireNonNull) HttpHeaders.setTransferEncodingChunked(io.netty.handler.codec.http.HttpHeaders.setTransferEncodingChunked) Buffer(com.hotels.styx.api.Buffer) ChannelFuture(io.netty.channel.ChannelFuture) DefaultHttpContent(io.netty.handler.codec.http.DefaultHttpContent) ByteBuf(io.netty.buffer.ByteBuf) CompletableFuture(java.util.concurrent.CompletableFuture) CancellationException(java.util.concurrent.CancellationException) Subscription(org.reactivestreams.Subscription)

Aggregations

LiveHttpResponse (com.hotels.styx.api.LiveHttpResponse)80 Test (org.junit.jupiter.api.Test)69 LiveHttpRequest (com.hotels.styx.api.LiveHttpRequest)25 Support.requestContext (com.hotels.styx.support.Support.requestContext)21 Eventual (com.hotels.styx.api.Eventual)15 MatcherAssert.assertThat (org.hamcrest.MatcherAssert.assertThat)15 HttpHandler (com.hotels.styx.api.HttpHandler)14 EmbeddedChannel (io.netty.channel.embedded.EmbeddedChannel)14 Mono (reactor.core.publisher.Mono)14 ByteStream (com.hotels.styx.api.ByteStream)13 Context (com.hotels.styx.api.HttpInterceptor.Context)13 OK (com.hotels.styx.api.HttpResponseStatus.OK)13 LiveHttpResponse.response (com.hotels.styx.api.LiveHttpResponse.response)13 TransportLostException (com.hotels.styx.api.exceptions.TransportLostException)12 ChannelHandlerContext (io.netty.channel.ChannelHandlerContext)12 CompletableFuture (java.util.concurrent.CompletableFuture)12 Matchers.is (org.hamcrest.Matchers.is)12 Buffer (com.hotels.styx.api.Buffer)11 LiveHttpRequest.get (com.hotels.styx.api.LiveHttpRequest.get)11 HttpInterceptorContext (com.hotels.styx.server.HttpInterceptorContext)11