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

io.github.bonigarcia.wdm.WdmHttpClient Maven / Gradle / Ivy

/*
 * (C) Copyright 2015 Boni Garcia (http://bonigarcia.github.io/)
 *
 * Licensed 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.
 *
 */
package io.github.bonigarcia.wdm;

import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.NTCredentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpOptions;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.ssl.TrustStrategy;
import org.slf4j.Logger;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.StringTokenizer;

import static io.github.bonigarcia.wdm.WdmConfig.isNullOrEmpty;
import static java.lang.System.getenv;
import static java.lang.invoke.MethodHandles.lookup;
import static java.net.URLDecoder.decode;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.http.HttpStatus.SC_BAD_REQUEST;
import static org.apache.http.HttpStatus.SC_UNAUTHORIZED;
import static org.apache.http.auth.AuthScope.ANY_REALM;
import static org.apache.http.client.config.AuthSchemes.NTLM;
import static org.apache.http.client.config.RequestConfig.custom;
import static org.slf4j.LoggerFactory.getLogger;

/**
 * The Http Client for WebDriverManager.
 *
 * @author Kazuki Shimizu
 * @since 1.6.2
 */
public class WdmHttpClient implements Closeable {
    final Logger log = getLogger(lookup().lookupClass());

    private final CloseableHttpClient httpClient;

    private WdmHttpClient(String proxyUrl, String proxyUser, String proxyPass) {
        HttpHost proxyHost = createProxyHttpHost(proxyUrl);
        HttpClientBuilder builder = HttpClientBuilder.create()
                .setConnectionManagerShared(true);
        if (proxyHost != null) {
            builder.setProxy(proxyHost);
            BasicCredentialsProvider credentialsProvider = createBasicCredentialsProvider(
                    proxyUrl, proxyUser, proxyPass, proxyHost);
            builder.setDefaultCredentialsProvider(credentialsProvider);
        }

        try {
            HostnameVerifier allHostsValid = new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            };
            SSLContext sslContext = SSLContexts.custom()
                    .loadTrustMaterial(null, new TrustStrategy() {
                        @Override
                        public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                            return true;
                        }
                    }).build();
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                    sslContext, allHostsValid);
            Registry socketFactoryRegistry = RegistryBuilder
                    .create().register("https", sslsf)
                    .register("http", new PlainConnectionSocketFactory())
                    .build();
            PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(
                    socketFactoryRegistry);

            builder.setConnectionManager(cm);
        } catch (Exception e) {
            throw new WebDriverManagerException(e);
        }

        httpClient = builder.useSystemProperties().build();
    }

    public Proxy createProxy(String proxyUrl) {
        URL url = determineProxyUrl(proxyUrl);
        if (url == null) {
            return null;
        }
        String proxyHost = url.getHost();
        int proxyPort = url.getPort() == -1 ? 80 : url.getPort();
        return new Proxy(Proxy.Type.HTTP,
                new InetSocketAddress(proxyHost, proxyPort));
    }

    public Response execute(Method method) throws IOException {
        HttpResponse response = httpClient.execute(method.toHttpUriRequest());
        if (response.getStatusLine().getStatusCode() >= SC_BAD_REQUEST) {
            String errorMessage = "A response error is detected: "
                    + response.getStatusLine();
            log.error(errorMessage);
            throw new WebDriverManagerException(errorMessage);
        }
        return new Response(response);
    }

    public boolean isValid(URL url) throws IOException {
        HttpResponse response = httpClient
                .execute(new WdmHttpClient.Options(url).toHttpUriRequest());
        if (response.getStatusLine().getStatusCode() > SC_UNAUTHORIZED) {
            log.debug("A response error is detected. {}",
                    response.getStatusLine());
            return false;
        }
        return true;
    }

    private URL determineProxyUrl(String proxy) {
        String proxyInput = isNullOrEmpty(proxy) ? getenv("HTTPS_PROXY")
                : proxy;
        if (isNullOrEmpty(proxyInput)) {
            return null;
        }
        try {
            return new URL(proxyInput.matches("^http[s]?://.*$") ? proxyInput
                    : "http://" + proxyInput);
        } catch (MalformedURLException e) {
            log.error("Invalid proxy url {}", proxyInput, e);
            return null;
        }
    }

    private final HttpHost createProxyHttpHost(String proxyUrl) {
        Proxy proxy = createProxy(proxyUrl);
        if (proxy == null || proxy.address() == null) {
            return null;
        }
        if (!(proxy.address() instanceof InetSocketAddress)) {
            String errorMessage = "Detect an unsupported subclass of SocketAddress. "
                    + "Please use the InetSocketAddress or subclass. Actual:"
                    + proxy.address().getClass();
            log.error(errorMessage);
            throw new WebDriverManagerException(errorMessage);
        }
        InetSocketAddress proxyAddress = (InetSocketAddress) proxy.address();
        return new HttpHost(proxyAddress.getHostName(), proxyAddress.getPort());
    }

    private final BasicCredentialsProvider createBasicCredentialsProvider(
            String proxy, String proxyUser, String proxyPass,
            HttpHost proxyHost) {
        URL proxyUrl = determineProxyUrl(proxy);
        if (proxyUrl == null) {
            return null;
        }
        try {
            String username = null;
            String password = null;

            // apply env value
            String userInfo = proxyUrl.getUserInfo();
            if (userInfo != null) {
                StringTokenizer st = new StringTokenizer(userInfo, ":");
                username = st.hasMoreTokens()
                        ? decode(st.nextToken(), UTF_8.name())
                        : null;
                password = st.hasMoreTokens()
                        ? decode(st.nextToken(), UTF_8.name())
                        : null;
            }
            String envProxyUser = getenv("HTTPS_PROXY_USER");
            String envProxyPass = getenv("HTTPS_PROXY_PASS");
            username = (envProxyUser != null) ? envProxyUser : username;
            password = (envProxyPass != null) ? envProxyPass : password;

            // apply option value
            username = (proxyUser != null) ? proxyUser : username;
            password = (proxyPass != null) ? proxyPass : password;

            if (username == null) {
                return null;
            }

            String ntlmUsername = username;
            String ntlmDomain = null;

            int index = username.indexOf('\\');
            if (index > 0) {
                ntlmDomain = username.substring(0, index);
                ntlmUsername = username.substring(index + 1);
            }

            BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
            AuthScope authScope = new AuthScope(proxyHost.getHostName(),
                    proxyHost.getPort(), ANY_REALM, NTLM);
            Credentials creds = new NTCredentials(ntlmUsername, password,
                    getWorkstation(), ntlmDomain);
            credentialsProvider.setCredentials(authScope, creds);

            authScope = new AuthScope(proxyHost.getHostName(),
                    proxyHost.getPort());
            creds = new UsernamePasswordCredentials(username, password);
            credentialsProvider.setCredentials(authScope, creds);

            return credentialsProvider;
        } catch (UnsupportedEncodingException e) {
            throw new WebDriverManagerException(e);
        }
    }

    private String getWorkstation() {
        Map env = getenv();

        if (env.containsKey("COMPUTERNAME")) {
            // Windows
            return env.get("COMPUTERNAME");
        } else if (env.containsKey("HOSTNAME")) {
            // Unix/Linux/MacOS
            return env.get("HOSTNAME");
        } else {
            // From DNS
            try {
                return InetAddress.getLocalHost().getHostName();
            } catch (UnknownHostException ex) {
                return null;
            }
        }
    }

    @Override
    public void close() throws IOException {
        httpClient.close();
    }

    public static class Builder {
        private String proxy;
        private String proxyUser;
        private String proxyPass;

        public Builder proxy(String proxy) {
            this.proxy = proxy;
            return this;
        }

        public Builder proxyUser(String proxyUser) {
            this.proxyUser = proxyUser;
            return this;
        }

        public Builder proxyPass(String proxyPass) {
            this.proxyPass = proxyPass;
            return this;
        }

        public WdmHttpClient build() {
            return new WdmHttpClient(proxy, proxyUser, proxyPass);
        }
    }

    private interface Method {
        HttpUriRequest toHttpUriRequest();
    }

    public static final class Get implements Method {
        private final HttpGet httpGet;
        private final RequestConfig requestConfig;

        public Get(URL url) {
            httpGet = new HttpGet(url.toString());
            requestConfig = null;
        }

        public Get(String url, int socketTimeout) {
            httpGet = new HttpGet(url);
            requestConfig = custom().setSocketTimeout(socketTimeout).build();
        }

        public Get addHeader(String name, String value) {
            httpGet.addHeader(name, value);
            return this;
        }

        @Override
        public HttpUriRequest toHttpUriRequest() {
            if (requestConfig != null) {
                httpGet.setConfig(requestConfig);
            }
            return httpGet;
        }
    }

    public static final class Options implements Method {
        private final HttpOptions httpOptions;

        public Options(URL url) {
            httpOptions = new HttpOptions(url.toString());
        }

        @Override
        public HttpUriRequest toHttpUriRequest() {
            return httpOptions;
        }
    }

    public static final class Response {
        private final HttpResponse httpResponse;

        public Response(HttpResponse response) {
            this.httpResponse = response;
        }

        public InputStream getContent() throws IOException {
            return httpResponse.getEntity().getContent();
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy