All Downloads are FREE. Search and download functionalities are using the official Maven repository.

keycloakjar.org.apache.hc.client5.http.impl.async.MinimalHttpAsyncClient Maven / Gradle / Ivy

There is a newer version: 7.21.1
Show newest version
/*
 * ====================================================================
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * .
 *
 */
package org.apache.hc.client5.http.impl.async;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.SchemePortResolver;
import org.apache.hc.client5.http.config.Configurable;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.impl.ConnPoolSupport;
import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
import org.apache.hc.client5.http.impl.ExecSupport;
import org.apache.hc.client5.http.impl.classic.RequestFailedException;
import org.apache.hc.client5.http.nio.AsyncClientConnectionManager;
import org.apache.hc.client5.http.nio.AsyncConnectionEndpoint;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.client5.http.routing.RoutingSupport;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.concurrent.BasicFuture;
import org.apache.hc.core5.concurrent.Cancellable;
import org.apache.hc.core5.concurrent.ComplexCancellable;
import org.apache.hc.core5.concurrent.ComplexFuture;
import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.hc.core5.http.EntityDetails;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.nio.AsyncClientEndpoint;
import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler;
import org.apache.hc.core5.http.nio.AsyncPushConsumer;
import org.apache.hc.core5.http.nio.CapacityChannel;
import org.apache.hc.core5.http.nio.DataStreamChannel;
import org.apache.hc.core5.http.nio.HandlerFactory;
import org.apache.hc.core5.http.nio.RequestChannel;
import org.apache.hc.core5.http.nio.command.ShutdownCommand;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.io.Closer;
import org.apache.hc.core5.reactor.Command;
import org.apache.hc.core5.reactor.DefaultConnectingIOReactor;
import org.apache.hc.core5.reactor.IOEventHandlerFactory;
import org.apache.hc.core5.reactor.IOReactorConfig;
import org.apache.hc.core5.util.Args;
import org.apache.hc.core5.util.Asserts;
import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.Timeout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Minimal implementation of {@link CloseableHttpAsyncClient}. This client is
 * optimized for HTTP/1.1 and HTTP/2 message transport and does not support
 * advanced HTTP protocol functionality such as request execution via a proxy,
 * state management, authentication and request redirects.
 * 

* Concurrent message exchanges executed by this client will get assigned to * separate connections leased from the connection pool. *

* * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL) public final class MinimalHttpAsyncClient extends AbstractMinimalHttpAsyncClientBase { private static final Logger LOG = LoggerFactory.getLogger(MinimalHttpAsyncClient.class); private final AsyncClientConnectionManager manager; private final SchemePortResolver schemePortResolver; private final TlsConfig tlsConfig; MinimalHttpAsyncClient( final IOEventHandlerFactory eventHandlerFactory, final AsyncPushConsumerRegistry pushConsumerRegistry, final IOReactorConfig reactorConfig, final ThreadFactory threadFactory, final ThreadFactory workerThreadFactory, final AsyncClientConnectionManager manager, final SchemePortResolver schemePortResolver, final TlsConfig tlsConfig) { super(new DefaultConnectingIOReactor( eventHandlerFactory, reactorConfig, workerThreadFactory, LoggingIOSessionDecorator.INSTANCE, LoggingExceptionCallback.INSTANCE, null, ioSession -> ioSession.enqueue(new ShutdownCommand(CloseMode.GRACEFUL), Command.Priority.NORMAL)), pushConsumerRegistry, threadFactory); this.manager = manager; this.schemePortResolver = schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE; this.tlsConfig = tlsConfig; } private Future leaseEndpoint( final HttpHost host, final Timeout connectionRequestTimeout, final Timeout connectTimeout, final HttpClientContext clientContext, final FutureCallback callback) { final HttpRoute route = new HttpRoute(RoutingSupport.normalize(host, schemePortResolver)); final ComplexFuture resultFuture = new ComplexFuture<>(callback); final String exchangeId = ExecSupport.getNextExchangeId(); clientContext.setExchangeId(exchangeId); final Future leaseFuture = manager.lease( exchangeId, route, null, connectionRequestTimeout, new FutureCallback() { @Override public void completed(final AsyncConnectionEndpoint connectionEndpoint) { if (connectionEndpoint.isConnected()) { resultFuture.completed(connectionEndpoint); } else { final Future connectFuture = manager.connect( connectionEndpoint, getConnectionInitiator(), connectTimeout, tlsConfig, clientContext, new FutureCallback() { @Override public void completed(final AsyncConnectionEndpoint result) { resultFuture.completed(result); } @Override public void failed(final Exception ex) { try { Closer.closeQuietly(connectionEndpoint); manager.release(connectionEndpoint, null, TimeValue.ZERO_MILLISECONDS); } finally { resultFuture.failed(ex); } } @Override public void cancelled() { try { Closer.closeQuietly(connectionEndpoint); manager.release(connectionEndpoint, null, TimeValue.ZERO_MILLISECONDS); } finally { resultFuture.cancel(true); } } }); resultFuture.setDependency(connectFuture); } } @Override public void failed(final Exception ex) { callback.failed(ex); } @Override public void cancelled() { callback.cancelled(); } }); resultFuture.setDependency(leaseFuture); return resultFuture; } public Future lease( final HttpHost host, final FutureCallback callback) { return lease(host, HttpClientContext.create(), callback); } public Future lease( final HttpHost host, final HttpContext context, final FutureCallback callback) { Args.notNull(host, "Host"); Args.notNull(context, "HTTP context"); final BasicFuture future = new BasicFuture<>(callback); if (!isRunning()) { future.failed(new CancellationException("Connection lease cancelled")); return future; } final HttpClientContext clientContext = HttpClientContext.adapt(context); final RequestConfig requestConfig = clientContext.getRequestConfig(); final Timeout connectionRequestTimeout = requestConfig.getConnectionRequestTimeout(); @SuppressWarnings("deprecation") final Timeout connectTimeout = requestConfig.getConnectTimeout(); leaseEndpoint( host, connectionRequestTimeout, connectTimeout, clientContext, new FutureCallback() { @Override public void completed(final AsyncConnectionEndpoint result) { future.completed(new InternalAsyncClientEndpoint(result)); } @Override public void failed(final Exception ex) { future.failed(ex); } @Override public void cancelled() { future.cancel(true); } }); return future; } @Override public Cancellable execute( final AsyncClientExchangeHandler exchangeHandler, final HandlerFactory pushHandlerFactory, final HttpContext context) { final ComplexCancellable cancellable = new ComplexCancellable(); try { if (!isRunning()) { throw new CancellationException("Request execution cancelled"); } final HttpClientContext clientContext = context != null ? HttpClientContext.adapt(context) : HttpClientContext.create(); exchangeHandler.produceRequest((request, entityDetails, context1) -> { RequestConfig requestConfig = null; if (request instanceof Configurable) { requestConfig = ((Configurable) request).getConfig(); } if (requestConfig != null) { clientContext.setRequestConfig(requestConfig); } else { requestConfig = clientContext.getRequestConfig(); } final Timeout connectionRequestTimeout = requestConfig.getConnectionRequestTimeout(); @SuppressWarnings("deprecation") final Timeout connectTimeout = requestConfig.getConnectTimeout(); final Timeout responseTimeout = requestConfig.getResponseTimeout(); final HttpHost target = new HttpHost(request.getScheme(), request.getAuthority()); final Future leaseFuture = leaseEndpoint( target, connectionRequestTimeout, connectTimeout, clientContext, new FutureCallback() { @Override public void completed(final AsyncConnectionEndpoint connectionEndpoint) { final InternalAsyncClientEndpoint endpoint = new InternalAsyncClientEndpoint(connectionEndpoint); final AtomicInteger messageCountDown = new AtomicInteger(2); final AsyncClientExchangeHandler internalExchangeHandler = new AsyncClientExchangeHandler() { @Override public void releaseResources() { try { exchangeHandler.releaseResources(); } finally { endpoint.releaseAndDiscard(); } } @Override public void failed(final Exception cause) { try { exchangeHandler.failed(cause); } finally { endpoint.releaseAndDiscard(); } } @Override public void cancel() { failed(new RequestFailedException("Request aborted")); } @Override public void produceRequest( final RequestChannel channel, final HttpContext context1) throws HttpException, IOException { channel.sendRequest(request, entityDetails, context1); if (entityDetails == null) { messageCountDown.decrementAndGet(); } } @Override public int available() { return exchangeHandler.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { exchangeHandler.produce(new DataStreamChannel() { @Override public void requestOutput() { channel.requestOutput(); } @Override public int write(final ByteBuffer src) throws IOException { return channel.write(src); } @Override public void endStream(final List trailers) throws IOException { channel.endStream(trailers); if (messageCountDown.decrementAndGet() <= 0) { endpoint.releaseAndReuse(); } } @Override public void endStream() throws IOException { channel.endStream(); if (messageCountDown.decrementAndGet() <= 0) { endpoint.releaseAndReuse(); } } }); } @Override public void consumeInformation( final HttpResponse response, final HttpContext context1) throws HttpException, IOException { exchangeHandler.consumeInformation(response, context1); } @Override public void consumeResponse( final HttpResponse response, final EntityDetails entityDetails, final HttpContext context1) throws HttpException, IOException { exchangeHandler.consumeResponse(response, entityDetails, context1); if (response.getCode() >= HttpStatus.SC_CLIENT_ERROR) { messageCountDown.decrementAndGet(); } if (entityDetails == null) { if (messageCountDown.decrementAndGet() <= 0) { endpoint.releaseAndReuse(); } } } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { exchangeHandler.updateCapacity(capacityChannel); } @Override public void consume(final ByteBuffer src) throws IOException { exchangeHandler.consume(src); } @Override public void streamEnd(final List trailers) throws HttpException, IOException { if (messageCountDown.decrementAndGet() <= 0) { endpoint.releaseAndReuse(); } exchangeHandler.streamEnd(trailers); } }; if (responseTimeout != null) { endpoint.setSocketTimeout(responseTimeout); } endpoint.execute(internalExchangeHandler, pushHandlerFactory, clientContext); } @Override public void failed(final Exception ex) { exchangeHandler.failed(ex); } @Override public void cancelled() { exchangeHandler.cancel(); } }); cancellable.setDependency(() -> leaseFuture.cancel(true)); }, context); } catch (final HttpException | IOException | IllegalStateException ex) { exchangeHandler.failed(ex); } return cancellable; } private class InternalAsyncClientEndpoint extends AsyncClientEndpoint { private final AsyncConnectionEndpoint connectionEndpoint; private final AtomicBoolean released; InternalAsyncClientEndpoint(final AsyncConnectionEndpoint connectionEndpoint) { this.connectionEndpoint = connectionEndpoint; this.released = new AtomicBoolean(false); } boolean isReleased() { return released.get(); } @Override public boolean isConnected() { return !isReleased() && connectionEndpoint.isConnected(); } @Override public void execute( final AsyncClientExchangeHandler exchangeHandler, final HandlerFactory pushHandlerFactory, final HttpContext context) { Asserts.check(!released.get(), "Endpoint has already been released"); final HttpClientContext clientContext = context != null ? HttpClientContext.adapt(context) : HttpClientContext.create(); final String exchangeId = ExecSupport.getNextExchangeId(); clientContext.setExchangeId(exchangeId); if (LOG.isDebugEnabled()) { LOG.debug("{} executing message exchange {}", exchangeId, ConnPoolSupport.getId(connectionEndpoint)); connectionEndpoint.execute( exchangeId, new LoggingAsyncClientExchangeHandler(LOG, exchangeId, exchangeHandler), pushHandlerFactory, clientContext); } else { connectionEndpoint.execute(exchangeId, exchangeHandler, clientContext); } } public void setSocketTimeout(final Timeout timeout) { connectionEndpoint.setSocketTimeout(timeout); } @Override public void releaseAndReuse() { if (released.compareAndSet(false, true)) { manager.release(connectionEndpoint, null, TimeValue.NEG_ONE_MILLISECOND); } } @Override public void releaseAndDiscard() { if (released.compareAndSet(false, true)) { Closer.closeQuietly(connectionEndpoint); manager.release(connectionEndpoint, null, TimeValue.ZERO_MILLISECONDS); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy