/*
 * ====================================================================
 * 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
 * <http://www.apache.org/>.
 *
 */

package org.apache.hc.client5.http.impl.routing;

import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;

import org.apache.hc.client5.http.SchemePortResolver;
import org.apache.hc.core5.annotation.Immutable;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.protocol.HttpContext;

/**
 * {@link org.apache.hc.client5.http.routing.HttpRoutePlanner} implementation
 * based on {@link ProxySelector}. By default, this class will pick up
 * the proxy settings of the JVM, either from system properties
 * or from the browser running the application.
 *
 * @since 4.3
 */
@Immutable
public class SystemDefaultRoutePlanner extends DefaultRoutePlanner {

    private final ProxySelector proxySelector;

    public SystemDefaultRoutePlanner(
            final SchemePortResolver schemePortResolver,
            final ProxySelector proxySelector) {
        super(schemePortResolver);
        this.proxySelector = proxySelector != null ? proxySelector : ProxySelector.getDefault();
    }

    public SystemDefaultRoutePlanner(final ProxySelector proxySelector) {
        this(null, proxySelector);
    }

    @Override
    protected HttpHost determineProxy(
            final HttpHost    target,
            final HttpRequest request,
            final HttpContext context) throws HttpException {
        final URI targetURI;
        try {
            targetURI = new URI(target.toURI());
        } catch (final URISyntaxException ex) {
            throw new HttpException("Cannot convert host to URI: " + target, ex);
        }
        final List<Proxy> proxies = this.proxySelector.select(targetURI);
        final Proxy p = chooseProxy(proxies);
        HttpHost result = null;
        if (p.type() == Proxy.Type.HTTP) {
            // convert the socket address to an HttpHost
            if (!(p.address() instanceof InetSocketAddress)) {
                throw new HttpException("Unable to handle non-Inet proxy address: " + p.address());
            }
            final InetSocketAddress isa = (InetSocketAddress) p.address();
            // assume default scheme (http)
            result = new HttpHost(isa.getAddress(), isa.getHostString(), isa.getPort(), null);
        }

        return result;
    }

    private Proxy chooseProxy(final List<Proxy> proxies) {
        Proxy result = null;
        // check the list for one we can use
        for (int i=0; (result == null) && (i < proxies.size()); i++) {
            final Proxy p = proxies.get(i);
            switch (p.type()) {

            case DIRECT:
            case HTTP:
                result = p;
                break;

            case SOCKS:
                // SOCKS hosts are not handled on the route level.
                // The socket may make use of the SOCKS host though.
                break;
            }
        }
        if (result == null) {
            //@@@ log as warning or info that only a socks proxy is available?
            // result can only be null if all proxies are socks proxies
            // socks proxies are not handled on the route planning level
            result = Proxy.NO_PROXY;
        }
        return result;
    }

}

@@ -2105,16 +2105,119 @@
 ector;%0A%0A
+    /**%0A     * @param proxySelector the proxy selector, or %7B@code null%7D for the system default%0A     */%0A
     publ
@@ -2433,18 +2433,38 @@
 ctor
- != null ?
+;%0A    %7D%0A%0A    /**%0A     * @param
  pro
@@ -2478,44 +2478,74 @@
 tor 
-: P
+the p
 roxy
-S
+ s
 elector
-.getD
+, or %7B@code null%7D for the system d
 efault
-();
 %0A    
-%7D%0A
+ */
 %0A   
@@ -3073,32 +3073,394 @@
  ex);%0A        %7D%0A
+        ProxySelector proxySelectorInstance = this.proxySelector;%0A        if (proxySelectorInstance == null) %7B%0A            proxySelectorInstance = ProxySelector.getDefault();%0A        %7D%0A        if (proxySelectorInstance == null) %7B%0A            //The proxy selector can be %22unset%22, so we must be able to deal with a null selector%0A            return null;%0A        %7D%0A
         final Li
@@ -3479,21 +3479,16 @@
 oxies = 
-this.
 proxySel
@@ -3492,16 +3492,24 @@
 Selector
+Instance
 .select(

Support changing system default ProxySelector Contributed by Robin Stevens <stevensro at gmail.com>