@Override
    public ClassicHttpResponse execute(
            final ClassicHttpRequest request,
            final ExecChain.Scope scope,
            final ExecChain chain) throws IOException, HttpException {
        Args.notNull(request, "HTTP request");
        Args.notNull(scope, "Scope");

        final String exchangeId = scope.exchangeId;
        final HttpRoute route = scope.route;
        final HttpClientContext context = scope.clientContext;
        final ExecRuntime execRuntime = scope.execRuntime;

        if (!execRuntime.isEndpointAcquired()) {
            final Object userToken = context.getUserToken();
            if (LOG.isDebugEnabled()) {
                LOG.debug("{} acquiring connection with route {}", exchangeId, route);
            }
            execRuntime.acquireEndpoint(exchangeId, route, userToken, context);
        }
        try {
            if (!execRuntime.isEndpointConnected()) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("{} opening connection {}", exchangeId, route);
                }

                final RouteTracker tracker = new RouteTracker(route);
                int step;
                do {
                    final HttpRoute fact = tracker.toRoute();
                    step = this.routeDirector.nextStep(route, fact);

                    switch (step) {

                        case HttpRouteDirector.CONNECT_TARGET:
                            execRuntime.connectEndpoint(context);
                            tracker.connectTarget(route.isSecure());
                            break;
                        case HttpRouteDirector.CONNECT_PROXY:
                            execRuntime.connectEndpoint(context);
                            final HttpHost proxy = route.getProxyHost();
                            tracker.connectProxy(proxy, route.isSecure() && !route.isTunnelled());
                            break;
                        case HttpRouteDirector.TUNNEL_TARGET: {
                            final ClassicHttpResponse finalResponse = createTunnelToTarget(
                                    exchangeId, route, request, execRuntime, context);
                            if (finalResponse != null) {
                                return finalResponse;
                            }
                            if (LOG.isDebugEnabled()) {
                                LOG.debug("{} tunnel to target created.", exchangeId);
                            }
                            tracker.tunnelTarget(false);
                        }
                        break;

                        case HttpRouteDirector.TUNNEL_PROXY: {
                            // The most simple example for this case is a proxy chain
                            // of two proxies, where P1 must be tunnelled to P2.
                            // route: Source -> P1 -> P2 -> Target (3 hops)
                            // fact:  Source -> P1 -> Target       (2 hops)
                            final int hop = fact.getHopCount() - 1; // the hop to establish
                            final boolean secure = createTunnelToProxy(route, hop, context);
                            if (LOG.isDebugEnabled()) {
                                LOG.debug("{} tunnel to proxy created.", exchangeId);
                            }
                            tracker.tunnelProxy(route.getHopTarget(hop), secure);
                        }
                        break;

                        case HttpRouteDirector.LAYER_PROTOCOL:
                            execRuntime.upgradeTls(context);
                            tracker.layerProtocol(route.isSecure());
                            break;

                        case HttpRouteDirector.UNREACHABLE:
                            throw new HttpException("Unable to establish route: " +
                                    "planned = " + route + "; current = " + fact);
                        case HttpRouteDirector.COMPLETE:
                            break;
                        default:
                            throw new IllegalStateException("Unknown step indicator "
                                    + step + " from RouteDirector.");
                    }

                } while (step > HttpRouteDirector.COMPLETE);
            }
            final EndpointInfo endpointInfo = execRuntime.getEndpointInfo();
            if (endpointInfo != null) {
                context.setSSLSession(endpointInfo.getSslSession());
            }
            return chain.proceed(request, scope);

        } catch (final IOException | HttpException | RuntimeException ex) {
            execRuntime.discardEndpoint();
            throw ex;
        }
    }
Request execution handler in the classic request execution chain
that is responsible for establishing connection to the target
origin server as specified by the current connection route.

@since 5.0
httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ConnectExec.java