Search in sources :

Example 1 with WebClientService

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();
}
Also used : Config(io.helidon.config.Config) WebClientService(io.helidon.webclient.spi.WebClientService) WebClient(io.helidon.webclient.WebClient)

Example 2 with WebClientService

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);
    }
}
Also used : WebClientResponse(io.helidon.webclient.WebClientResponse) Counter(org.eclipse.microprofile.metrics.Counter) WebClientService(io.helidon.webclient.spi.WebClientService) WebClient(io.helidon.webclient.WebClient) Test(org.junit.jupiter.api.Test)

Example 3 with WebClientService

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()));
}
Also used : Counter(org.eclipse.microprofile.metrics.Counter) WebClientService(io.helidon.webclient.spi.WebClientService) WebClient(io.helidon.webclient.WebClient)

Example 4 with WebClientService

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);
}
Also used : AttributeKey(io.netty.util.AttributeKey) Arrays(java.util.Arrays) HttpHeaders(io.netty.handler.codec.http.HttpHeaders) URL(java.net.URL) URISyntaxException(java.net.URISyntaxException) DataChunk(io.helidon.common.http.DataChunk) MessageBodyReadableContent(io.helidon.media.common.MessageBodyReadableContent) AsciiString(io.netty.util.AsciiString) MessageBodyWriterContext(io.helidon.media.common.MessageBodyWriterContext) MediaType(io.helidon.common.http.MediaType) DefaultHttpRequest(io.netty.handler.codec.http.DefaultHttpRequest) Flow(java.util.concurrent.Flow) Duration(java.time.Duration) Map(java.util.Map) Parameters(io.helidon.common.http.Parameters) URI(java.net.URI) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Set(java.util.Set) Logger(java.util.logging.Logger) NioEventLoopGroup(io.netty.channel.nio.NioEventLoopGroup) Objects(java.util.Objects) MessageBodyReaderContext(io.helidon.media.common.MessageBodyReaderContext) List(java.util.List) CompletionStage(java.util.concurrent.CompletionStage) DefaultHttpHeaders(io.netty.handler.codec.http.DefaultHttpHeaders) Optional(java.util.Optional) Headers(io.helidon.common.http.Headers) HttpRequest(io.helidon.common.http.HttpRequest) NioSocketChannel(io.netty.channel.socket.nio.NioSocketChannel) HttpVersion(io.netty.handler.codec.http.HttpVersion) ChannelOption(io.netty.channel.ChannelOption) Context(io.helidon.common.context.Context) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) HashMap(java.util.HashMap) CompletableFuture(java.util.concurrent.CompletableFuture) Function(java.util.function.Function) ArrayList(java.util.ArrayList) Level(java.util.logging.Level) HashSet(java.util.HashSet) StringTokenizer(java.util.StringTokenizer) ChannelFutureListener(io.netty.channel.ChannelFutureListener) Single(io.helidon.common.reactive.Single) Http(io.helidon.common.http.Http) WebClientService(io.helidon.webclient.spi.WebClientService) HttpHeaderValues(io.netty.handler.codec.http.HttpHeaderValues) HttpMethod(io.netty.handler.codec.http.HttpMethod) IOException(java.io.IOException) GenericType(io.helidon.common.GenericType) Contexts(io.helidon.common.context.Contexts) ChannelFuture(io.netty.channel.ChannelFuture) TimeUnit(java.util.concurrent.TimeUnit) Channel(io.netty.channel.Channel) AtomicLong(java.util.concurrent.atomic.AtomicLong) Bootstrap(io.netty.bootstrap.Bootstrap) HttpHeaderNames(io.netty.handler.codec.http.HttpHeaderNames) Collections(java.util.Collections) HttpUtil(io.netty.handler.codec.http.HttpUtil) ChannelFuture(io.netty.channel.ChannelFuture) HttpHeaders(io.netty.handler.codec.http.HttpHeaders) DefaultHttpHeaders(io.netty.handler.codec.http.DefaultHttpHeaders) WebClientService(io.helidon.webclient.spi.WebClientService) URI(java.net.URI) ChannelFutureListener(io.netty.channel.ChannelFutureListener) CompletableFuture(java.util.concurrent.CompletableFuture) DefaultHttpRequest(io.netty.handler.codec.http.DefaultHttpRequest) Bootstrap(io.netty.bootstrap.Bootstrap)

Example 5 with WebClientService

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();
        }
    }
}
Also used : BufferedEmittingPublisher(io.helidon.common.reactive.BufferedEmittingPublisher) AttributeKey(io.netty.util.AttributeKey) HttpHeaders(io.netty.handler.codec.http.HttpHeaders) COMPLETED(io.helidon.webclient.WebClientRequestBuilderImpl.COMPLETED) RECEIVED(io.helidon.webclient.WebClientRequestBuilderImpl.RECEIVED) DataChunk(io.helidon.common.http.DataChunk) RESULT(io.helidon.webclient.WebClientRequestBuilderImpl.RESULT) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) CompletableFuture(java.util.concurrent.CompletableFuture) ReentrantReadWriteLock(java.util.concurrent.locks.ReentrantReadWriteLock) HttpObject(io.netty.handler.codec.http.HttpObject) ArrayList(java.util.ArrayList) Level(java.util.logging.Level) LastHttpContent(io.netty.handler.codec.http.LastHttpContent) ChannelHandlerContext(io.netty.channel.ChannelHandlerContext) RESPONSE(io.helidon.webclient.WebClientRequestBuilderImpl.RESPONSE) ByteBuf(io.netty.buffer.ByteBuf) Single(io.helidon.common.reactive.Single) Http(io.helidon.common.http.Http) REQUEST(io.helidon.webclient.WebClientRequestBuilderImpl.REQUEST) HttpContent(io.netty.handler.codec.http.HttpContent) WILL_CLOSE(io.helidon.webclient.WebClientRequestBuilderImpl.WILL_CLOSE) WebClientService(io.helidon.webclient.spi.WebClientService) HttpHeaderValues(io.netty.handler.codec.http.HttpHeaderValues) REQUEST_ID(io.helidon.webclient.WebClientRequestBuilderImpl.REQUEST_ID) IOException(java.io.IOException) HttpResponseStatus(io.netty.handler.codec.http.HttpResponseStatus) Logger(java.util.logging.Logger) Channel(io.netty.channel.Channel) IN_USE(io.helidon.webclient.WebClientRequestBuilderImpl.IN_USE) List(java.util.List) CompletionStage(java.util.concurrent.CompletionStage) SimpleChannelInboundHandler(io.netty.channel.SimpleChannelInboundHandler) HttpResponse(io.netty.handler.codec.http.HttpResponse) RETURN(io.helidon.webclient.WebClientRequestBuilderImpl.RETURN) RESPONSE_RECEIVED(io.helidon.webclient.WebClientRequestBuilderImpl.RESPONSE_RECEIVED) HttpHeaders(io.netty.handler.codec.http.HttpHeaders) Channel(io.netty.channel.Channel) HttpResponse(io.netty.handler.codec.http.HttpResponse) WebClientService(io.helidon.webclient.spi.WebClientService) LastHttpContent(io.netty.handler.codec.http.LastHttpContent) LastHttpContent(io.netty.handler.codec.http.LastHttpContent) HttpContent(io.netty.handler.codec.http.HttpContent)

Aggregations

WebClientService (io.helidon.webclient.spi.WebClientService)8 WebClient (io.helidon.webclient.WebClient)6 WebClientResponse (io.helidon.webclient.WebClientResponse)4 Counter (org.eclipse.microprofile.metrics.Counter)4 Test (org.junit.jupiter.api.Test)4 Http (io.helidon.common.http.Http)3 Single (io.helidon.common.reactive.Single)3 DataChunk (io.helidon.common.http.DataChunk)2 Channel (io.netty.channel.Channel)2 HttpHeaderValues (io.netty.handler.codec.http.HttpHeaderValues)2 HttpHeaders (io.netty.handler.codec.http.HttpHeaders)2 AttributeKey (io.netty.util.AttributeKey)2 IOException (java.io.IOException)2 ArrayList (java.util.ArrayList)2 List (java.util.List)2 CompletableFuture (java.util.concurrent.CompletableFuture)2 CompletionStage (java.util.concurrent.CompletionStage)2 AtomicBoolean (java.util.concurrent.atomic.AtomicBoolean)2 Level (java.util.logging.Level)2 Logger (java.util.logging.Logger)2