use of io.helidon.webclient.spi.WebClientService in project helidon by oracle.
the class ClientMainTest method createWebClient.
private void createWebClient(int port, WebClientService... services) {
Config config = Config.create();
WebClient.Builder builder = WebClient.builder().baseUri("http://localhost:" + port + "/greet").config(config.get("client")).addMediaSupport(JsonpSupport.create());
for (WebClientService service : services) {
builder.addService(service);
}
webClient = builder.build();
}
use of io.helidon.webclient.spi.WebClientService in project helidon by oracle.
the class MetricsTest method testErrorHandling.
@Test
public void testErrorHandling() {
WebClientService errorAll = WebClientMetrics.counter().success(false).nameFormat("counter.all.errors.%2$s").build();
WebClientService errorGet = WebClientMetrics.counter().methods(Http.Method.GET).success(false).nameFormat("counter.errors.%1$s.%2$s").build();
WebClientService errorPut = WebClientMetrics.counter().methods(Http.Method.PUT).success(false).nameFormat("counter.errors.%1$s.%2$s").build();
WebClient webClient = createNewClient(errorAll, errorGet, errorPut);
Counter counterAll = FACTORY.counter("counter.all.errors.localhost");
Counter counterGet = FACTORY.counter("counter.errors.GET.localhost");
Counter counterPut = FACTORY.counter("counter.errors.PUT.localhost");
assertThat(counterAll.getCount(), is(0L));
assertThat(counterGet.getCount(), is(0L));
assertThat(counterPut.getCount(), is(0L));
try {
webClient.get().path("/invalid").request().thenCompose(WebClientResponse::close).toCompletableFuture().get();
assertThat(counterAll.getCount(), is(1L));
assertThat(counterGet.getCount(), is(1L));
assertThat(counterPut.getCount(), is(0L));
webClient.put().path("/invalid").submit().thenCompose(WebClientResponse::close).toCompletableFuture().get();
assertThat(counterAll.getCount(), is(2L));
assertThat(counterGet.getCount(), is(1L));
assertThat(counterPut.getCount(), is(1L));
} catch (Exception e) {
fail(e);
}
}
use of io.helidon.webclient.spi.WebClientService in project helidon by oracle.
the class ClientMain method clientMetricsExample.
static Single<String> clientMetricsExample(String url, Config config) {
// This part here is only for verification purposes, it is not needed to be done for actual usage.
String counterName = "example.metric.GET.localhost";
Counter counter = METRIC_REGISTRY.counter(counterName);
System.out.println(counterName + ": " + counter.getCount());
// Creates new metric which will count all GET requests and has format of example.metric.GET.<host-name>
WebClientService clientService = WebClientMetrics.counter().methods(Http.Method.GET).nameFormat("example.metric.%1$s.%2$s").build();
// This newly created metric now needs to be registered to WebClient.
WebClient webClient = WebClient.builder().baseUri(url).config(config).addService(clientService).build();
// Perform any GET request using this newly created WebClient instance.
return performGetMethod(webClient).peek(s -> System.out.println(counterName + ": " + counter.getCount()));
}
use of io.helidon.webclient.spi.WebClientService in project helidon by oracle.
the class WebClientRequestBuilderImpl method invoke.
private Single<WebClientResponse> invoke(Flow.Publisher<DataChunk> requestEntity) {
finalUri = prepareFinalURI();
if (requestId == null) {
requestId = REQUEST_NUMBER.incrementAndGet();
}
// LOGGER.finest(() -> "(client reqID: " + requestId + ") Request final URI: " + uri);
CompletableFuture<WebClientServiceRequest> sent = new CompletableFuture<>();
CompletableFuture<WebClientServiceResponse> responseReceived = new CompletableFuture<>();
CompletableFuture<WebClientServiceResponse> complete = new CompletableFuture<>();
WebClientServiceRequest completedRequest = new WebClientServiceRequestImpl(this, sent, responseReceived, complete);
CompletionStage<WebClientServiceRequest> rcs = CompletableFuture.completedFuture(completedRequest);
for (WebClientService service : services) {
rcs = rcs.thenCompose(service::request).thenApply(servReq -> {
finalUri = recreateURI(servReq);
return servReq;
});
}
Single<WebClientResponse> single = Single.create(rcs.thenCompose(serviceRequest -> {
URI requestUri = relativizeNoProxy(finalUri, proxy, configuration.relativeUris());
requestId = serviceRequest.requestId();
HttpHeaders headers = toNettyHttpHeaders();
DefaultHttpRequest request = new DefaultHttpRequest(toNettyHttpVersion(httpVersion), toNettyMethod(method), requestUri.toASCIIString(), headers);
boolean keepAlive = HttpUtil.isKeepAlive(request);
requestConfiguration = RequestConfiguration.builder(finalUri).update(configuration).followRedirects(followRedirects).clientServiceRequest(serviceRequest).readerContext(readerContext).writerContext(writerContext).connectTimeout(connectTimeout).readTimeout(readTimeout).services(services).context(context).proxy(proxy).keepAlive(keepAlive).requestId(requestId).build();
WebClientRequestImpl clientRequest = new WebClientRequestImpl(this);
CompletableFuture<WebClientResponse> result = new CompletableFuture<>();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventGroup).channel(NioSocketChannel.class).handler(new NettyClientInitializer(requestConfiguration)).option(ChannelOption.SO_KEEPALIVE, keepAlive).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, (int) connectTimeout.toMillis());
ChannelFuture channelFuture = keepAlive ? obtainChannelFuture(requestConfiguration, bootstrap) : bootstrap.connect(finalUri.getHost(), finalUri.getPort());
channelFuture.addListener((ChannelFutureListener) future -> {
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.finest(() -> "(client reqID: " + requestId + ") " + "Channel hashcode -> " + channelFuture.channel().hashCode());
}
channelFuture.channel().attr(REQUEST).set(clientRequest);
channelFuture.channel().attr(RESPONSE_RECEIVED).set(false);
channelFuture.channel().attr(RECEIVED).set(responseReceived);
channelFuture.channel().attr(COMPLETED).set(complete);
channelFuture.channel().attr(WILL_CLOSE).set(!keepAlive);
channelFuture.channel().attr(RESULT).set(result);
channelFuture.channel().attr(REQUEST_ID).set(requestId);
Throwable cause = future.cause();
if (null == cause) {
RequestContentSubscriber requestContentSubscriber = new RequestContentSubscriber(request, channelFuture.channel(), result, sent, allowChunkedEncoding);
requestEntity.subscribe(requestContentSubscriber);
} else {
sent.completeExceptionally(cause);
responseReceived.completeExceptionally(cause);
complete.completeExceptionally(cause);
result.completeExceptionally(new WebClientException(finalUri.toString(), cause));
}
});
return result;
}));
return wrapWithContext(single);
}
use of io.helidon.webclient.spi.WebClientService in project helidon by oracle.
the class NettyClientHandler method channelRead0.
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws IOException {
Channel channel = ctx.channel();
if (msg instanceof HttpResponse) {
channel.config().setAutoRead(false);
HttpResponse response = (HttpResponse) msg;
this.requestId = channel.attr(REQUEST_ID).get();
channel.attr(RESPONSE_RECEIVED).set(true);
WebClientRequestImpl clientRequest = channel.attr(REQUEST).get();
RequestConfiguration requestConfiguration = clientRequest.configuration();
LOGGER.finest(() -> "(client reqID: " + requestId + ") Initial http response message received");
this.publisher = new HttpResponsePublisher(ctx);
channel.attr(PUBLISHER).set(this.publisher);
this.responseCloser = new ResponseCloser(ctx);
WebClientResponseImpl.Builder responseBuilder = WebClientResponseImpl.builder();
responseBuilder.contentPublisher(publisher).readerContext(requestConfiguration.readerContext()).status(helidonStatus(response.status())).httpVersion(Http.Version.create(response.protocolVersion().toString())).responseCloser(responseCloser).lastEndpointURI(requestConfiguration.requestURI());
HttpHeaders nettyHeaders = response.headers();
for (String name : nettyHeaders.names()) {
List<String> values = nettyHeaders.getAll(name);
responseBuilder.addHeader(name, values);
}
String connection = nettyHeaders.get(Http.Header.CONNECTION, HttpHeaderValues.CLOSE.toString());
if (connection.equals(HttpHeaderValues.CLOSE.toString())) {
ctx.channel().attr(WILL_CLOSE).set(true);
}
// we got a response, we can safely complete the future
// all errors are now fed only to the publisher
WebClientResponse clientResponse = responseBuilder.build();
channel.attr(RESPONSE).set(clientResponse);
for (HttpInterceptor interceptor : HTTP_INTERCEPTORS) {
if (interceptor.shouldIntercept(response.status(), requestConfiguration)) {
boolean continueAfter = !interceptor.continueAfterInterception();
if (continueAfter) {
responseCloser.close().thenAccept(future -> LOGGER.finest(() -> "Response closed due to redirection"));
}
interceptor.handleInterception(response, clientRequest, channel.attr(RESULT).get());
if (continueAfter) {
return;
}
}
}
requestConfiguration.cookieManager().put(requestConfiguration.requestURI(), clientResponse.headers().toMap());
WebClientServiceResponse clientServiceResponse = new WebClientServiceResponseImpl(requestConfiguration.context().get(), clientResponse.headers(), clientResponse.status());
channel.attr(SERVICE_RESPONSE).set(clientServiceResponse);
List<WebClientService> services = requestConfiguration.services();
CompletionStage<WebClientServiceResponse> csr = CompletableFuture.completedFuture(clientServiceResponse);
for (WebClientService service : services) {
csr = csr.thenCompose(clientSerResponse -> service.response(clientRequest, clientSerResponse));
}
CompletableFuture<WebClientServiceResponse> responseReceived = channel.attr(RECEIVED).get();
CompletableFuture<WebClientResponse> responseFuture = channel.attr(RESULT).get();
csr.whenComplete((clientSerResponse, throwable) -> {
if (throwable != null) {
responseReceived.completeExceptionally(throwable);
responseFuture.completeExceptionally(throwable);
responseCloser.close();
} else {
responseReceived.complete(clientServiceResponse);
responseReceived.thenRun(() -> {
if (shouldResponseAutomaticallyClose(clientResponse)) {
responseCloser.close().thenAccept(aVoid -> {
LOGGER.finest(() -> "Response automatically closed. No entity expected");
});
}
responseFuture.complete(clientResponse);
});
}
});
}
if (responseCloser.isClosed()) {
if (!channel.attr(WILL_CLOSE).get() && channel.hasAttr(RETURN)) {
if (msg instanceof LastHttpContent) {
LOGGER.finest(() -> "(client reqID: " + requestId + ") Draining finished");
if (channel.isActive()) {
channel.attr(RETURN).get().set(true);
}
} else {
LOGGER.finest(() -> "(client reqID: " + requestId + ") Draining not finished, requesting new chunk");
}
channel.read();
}
return;
}
// never "else-if" - msg may be an instance of more than one type, we must process all of them
if (msg instanceof HttpContent) {
HttpContent content = (HttpContent) msg;
publisher.emit(content.content());
}
if (msg instanceof LastHttpContent) {
LOGGER.finest(() -> "(client reqID: " + requestId + ") Last http content received");
if (channel.hasAttr(RETURN)) {
channel.attr(RETURN).get().set(true);
responseCloser.close();
channel.read();
} else {
responseCloser.close();
}
}
}
Aggregations