use of ratpack.core.http.client.ReceivedResponse in project ratpack by ratpack.
the class RequestActionSupport method addCommonResponseHandlers.
private void addCommonResponseHandlers(ChannelPipeline p, Downstream<? super T> downstream) throws Exception {
if (channelKey.ssl && p.get(SSL_HANDLER_NAME) == null) {
// this is added once because netty is not able to properly replace this handler on
// pooled channels from request to request. Because a pool is unique to a uri,
// doing this works, as subsequent requests would be passing in the same certs.
p.addLast(SSL_HANDLER_NAME, createSslHandler());
}
p.addLast(CLIENT_CODEC_HANDLER_NAME, new HttpClientCodec(4096, 8192, requestConfig.responseMaxChunkSize, false));
p.addLast(READ_TIMEOUT_HANDLER_NAME, new ReadTimeoutHandler(requestConfig.readTimeout.toNanos(), TimeUnit.NANOSECONDS));
p.addLast(REDIRECT_HANDLER_NAME, new SimpleChannelInboundHandler<HttpObject>(false) {
boolean redirected;
HttpResponse response;
@Override
public void channelInactive(ChannelHandlerContext ctx) {
ctx.fireExceptionCaught(new PrematureChannelClosureException("Server " + requestConfig.uri + " closed the connection prematurely"));
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
// received the end frame of the 100 Continue, send the body, then process the resulting response
if (msg instanceof LastHttpContent && expectContinue && receivedContinue) {
expectContinue = false;
receivedContinue = false;
sendRequestBody(downstream, ctx.channel());
return;
}
if (msg instanceof HttpResponse) {
this.response = (HttpResponse) msg;
int status = response.status().code();
if (expectContinue) {
// need to wait for a 100 Continue to come in
if (status == HttpResponseStatus.CONTINUE.code()) {
// received the continue, now wait for the end frame before sending the body
receivedContinue = true;
return;
} else if (!isRedirect(status)) {
// Received a response other than 100 Continue and not a redirect, so clear that we expect a 100
// and process this as the normal response without sending the body.
expectContinue = false;
}
}
int maxRedirects = requestConfig.maxRedirects;
String locationValue = response.headers().getAsString(HttpHeaderConstants.LOCATION);
Action<? super RequestSpec> redirectConfigurer = RequestActionSupport.this.requestConfigurer;
if (isRedirect(status) && redirectCount < maxRedirects && locationValue != null) {
final Function<? super ReceivedResponse, Action<? super RequestSpec>> onRedirect = requestConfig.onRedirect;
if (onRedirect != null) {
final Action<? super RequestSpec> onRedirectResult = ((DefaultExecution) execution).runSync(() -> onRedirect.apply(toReceivedResponse(response)));
if (onRedirectResult == null) {
redirectConfigurer = null;
} else {
redirectConfigurer = redirectConfigurer.append(onRedirectResult);
}
}
if (redirectConfigurer != null) {
Action<? super RequestSpec> redirectRequestConfig = s -> {
if (status == 301 || status == 302) {
s.get();
}
};
Action<? super RequestSpec> finalRedirectRequestConfig = redirectConfigurer.append(redirectRequestConfig);
Action<? super RequestSpec> executionBoundRedirectRequestConfig = request -> {
((DefaultExecution) execution).runSync(() -> {
finalRedirectRequestConfig.execute(request);
return null;
});
};
URI locationUri = absolutizeRedirect(requestConfig.uri, locationValue);
redirected = true;
Future<Void> dispose = dispose(ctx.pipeline(), response);
dispose.addListener(future -> {
if (future.isSuccess()) {
onRedirect(locationUri, redirectCount + 1, expectContinue, executionBoundRedirectRequestConfig).connect(downstream);
} else {
downstream.error(future.cause());
}
});
}
}
}
if (!redirected) {
ctx.fireChannelRead(msg);
}
}
});
if (requestConfig.decompressResponse) {
p.addLast(DECOMPRESS_HANDLER_NAME, new HttpContentDecompressor());
}
p.addLast(WRITABILITY_HANDLER_NAME, new ChannelInboundHandlerAdapter() {
@Override
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
onWritabilityChanged.run();
super.channelWritabilityChanged(ctx);
}
});
addResponseHandlers(p, downstream);
}
use of ratpack.core.http.client.ReceivedResponse in project ratpack by ratpack.
the class ContentAggregatingRequestAction method addResponseHandlers.
@Override
protected void addResponseHandlers(ChannelPipeline p, Downstream<? super ReceivedResponse> downstream) {
p.addLast(AGGREGATOR_HANDLER_NAME, new NoContentLengthOnNoBodyHttpObjectAggregator(requestConfig.maxContentLength));
p.addLast(RESPONSE_HANDLER_NAME, new SimpleChannelInboundHandler<FullHttpResponse>(false) {
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpResponse response) throws Exception {
response.touch();
dispose(ctx.pipeline(), response).addListener(future -> {
if (future.isSuccess()) {
ByteBuf content = new ByteBufRef(response.content());
execution.onComplete(() -> {
if (content.refCnt() > 0) {
content.release();
}
});
downstream.success(toReceivedResponse(response, content));
} else {
downstream.error(future.cause());
}
});
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
Throwable decorated = decorateException(cause);
forceDispose(ctx.pipeline()).addListener(future -> {
if (!future.isSuccess()) {
decorated.addSuppressed(future.cause());
}
downstream.error(decorated);
});
}
});
}
use of ratpack.core.http.client.ReceivedResponse in project ratpack by ratpack.
the class BlockingHttpClient method request.
public ReceivedResponse request(HttpClient httpClient, URI uri, ExecController execController, Duration timeout, Action<? super RequestSpec> action) throws Throwable {
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<ExecResult<ReceivedResponse>> result = new AtomicReference<>();
execController.fork().start(e -> httpClient.request(uri, action.prepend(s -> s.readTimeout(Duration.ofHours(1)))).map(response -> {
TypedData responseBody = response.getBody();
ByteBuf responseBuffer = responseBody.getBuffer();
ByteBuf heapResponseBodyBuffer = unreleasableBuffer(responseBuffer.isDirect() ? TestByteBufAllocators.LEAKING_UNPOOLED_HEAP.heapBuffer(responseBuffer.readableBytes()).writeBytes(responseBuffer) : responseBuffer.retain());
return new DefaultReceivedResponse(response.getStatus(), response.getHeaders(), new ByteBufBackedTypedData(heapResponseBodyBuffer, responseBody.getContentType()));
}).connect(new Downstream<ReceivedResponse>() {
@Override
public void success(ReceivedResponse value) {
result.set(ExecResult.of(Result.success(value)));
latch.countDown();
}
@Override
public void error(Throwable throwable) {
result.set(ExecResult.of(Result.error(throwable)));
latch.countDown();
}
@Override
public void complete() {
result.set(ExecResult.complete());
latch.countDown();
}
}));
try {
if (!latch.await(timeout.toNanos(), TimeUnit.NANOSECONDS)) {
TemporalUnit unit = timeout.getUnits().get(0);
throw new IllegalStateException("Request to " + uri + " took more than " + timeout.get(unit) + " " + unit.toString() + " to complete");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw Exceptions.uncheck(e);
}
return result.get().getValueOrThrow();
}
Aggregations