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();
}
}
}
}
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"));
}
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));
}
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));
}
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));
}
Aggregations