io.github.bonigarcia.wdm.WdmHttpClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of webdrivermanager Show documentation
Show all versions of webdrivermanager Show documentation
Automatic Selenium WebDriver binaries management in runtime for Java
/*
* (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();
}
}
}