diff --git a/src/com/machinepublishers/jbrowserdriver/StreamConnection.java b/src/com/machinepublishers/jbrowserdriver/StreamConnection.java index bdbf3cf0..e71d7593 100644 --- a/src/com/machinepublishers/jbrowserdriver/StreamConnection.java +++ b/src/com/machinepublishers/jbrowserdriver/StreamConnection.java @@ -45,7 +45,9 @@ import java.nio.file.Paths; import java.security.KeyStore; import java.security.Permission; +import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Base64; import java.util.Collection; @@ -68,6 +70,7 @@ import org.apache.http.Header; import org.apache.http.HttpEntity; +import org.apache.http.HttpHost; import org.apache.http.client.config.CookieSpecs; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; @@ -86,12 +89,14 @@ import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.impl.DefaultConnectionReuseStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.client.cache.CachingHttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.protocol.HttpContext; import org.apache.http.ssl.SSLContexts; +import org.apache.http.ssl.TrustStrategy; class StreamConnection extends HttpURLConnection implements Closeable { private static final Pattern invalidUrlChar = Pattern.compile("[^-A-Za-z0-9._~:/?#\\[\\]@!$&'()*+,;=]"); @@ -121,6 +126,7 @@ class StreamConnection extends HttpURLConnection implements Closeable { .setMaxConnPerRoute(ROUTE_CONNECTIONS) .setMaxConnTotal(CONNECTIONS) .setDefaultCredentialsProvider(ProxyAuth.instance()) + .setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE) .build(); private static final CloseableHttpClient cachingClient = CachingHttpClients.custom() .disableRedirectHandling() @@ -129,6 +135,7 @@ class StreamConnection extends HttpURLConnection implements Closeable { .setMaxConnPerRoute(ROUTE_CONNECTIONS) .setMaxConnTotal(CONNECTIONS) .setDefaultCredentialsProvider(ProxyAuth.instance()) + .setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE) .build(); private static boolean cacheByDefault; @@ -171,56 +178,70 @@ class StreamConnection extends HttpURLConnection implements Closeable { } private static SSLContext sslContext() { - //a good pem source: https://raw.githubusercontent.com/bagder/ca-bundle/master/ca-bundle.crt if (System.getProperty("jbd.pemfile") != null) { - try { - String location = System.getProperty("jbd.pemfile"); - File cachedPemFile = new File("./pemfile_cached"); - boolean remote = location.startsWith("https://") || location.startsWith("http://"); - if (remote && cachedPemFile.exists() - && (System.currentTimeMillis() - cachedPemFile.lastModified() < 48 * 60 * 60 * 1000)) { - location = cachedPemFile.getAbsolutePath(); - remote = false; - } - String pemBlocks = null; - if (remote) { - HttpURLConnection remotePemFile = (HttpURLConnection) new URL(location).openConnection(); - remotePemFile.setRequestMethod("GET"); - remotePemFile.connect(); - pemBlocks = Util.toString(remotePemFile.getInputStream(), - Util.charset(remotePemFile)); - cachedPemFile.delete(); - Files.write(Paths.get(cachedPemFile.getAbsolutePath()), pemBlocks.getBytes("utf-8")); - } else { - pemBlocks = new String(Files.readAllBytes( - Paths.get(new File(location).getAbsolutePath())), "utf-8"); + if ("trustanything".equals(System.getProperty("jbd.pemfile"))) { + try { + return SSLContexts.custom().loadTrustMaterial(KeyStore.getInstance(KeyStore.getDefaultType()), + new TrustStrategy() { + public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { + return true; + } + }).build(); + } catch (Throwable t) { + Logs.logsFor(1l).exception(t); } - KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); - keyStore.load(null); - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - Matcher matcher = pemBlock.matcher(pemBlocks); - boolean found = false; - while (matcher.find()) { - String pemBlock = matcher.group(1).replaceAll("[\\n\\r]+", ""); - ByteArrayInputStream byteStream = new ByteArrayInputStream(Base64.getDecoder().decode(pemBlock)); - java.security.cert.X509Certificate cert = (java.security.cert.X509Certificate) cf.generateCertificate(byteStream); - String alias = cert.getSubjectX500Principal().getName("RFC2253"); - if (alias != null && !keyStore.containsAlias(alias)) { - found = true; - keyStore.setCertificateEntry(alias, cert); + } else { + try { + String location = System.getProperty("jbd.pemfile"); + location = location.equals("compatible") + ? "https://raw.githubusercontent.com/bagder/ca-bundle/master/ca-bundle.crt" : location; + File cachedPemFile = new File("./pemfile_cached"); + boolean remote = location.startsWith("https://") || location.startsWith("http://"); + if (remote && cachedPemFile.exists() + && (System.currentTimeMillis() - cachedPemFile.lastModified() < 48 * 60 * 60 * 1000)) { + location = cachedPemFile.getAbsolutePath(); + remote = false; } + String pemBlocks = null; + if (remote) { + HttpURLConnection remotePemFile = (HttpURLConnection) new URL(location).openConnection(); + remotePemFile.setRequestMethod("GET"); + remotePemFile.connect(); + pemBlocks = Util.toString(remotePemFile.getInputStream(), + Util.charset(remotePemFile)); + cachedPemFile.delete(); + Files.write(Paths.get(cachedPemFile.getAbsolutePath()), pemBlocks.getBytes("utf-8")); + } else { + pemBlocks = new String(Files.readAllBytes( + Paths.get(new File(location).getAbsolutePath())), "utf-8"); + } + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(null); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + Matcher matcher = pemBlock.matcher(pemBlocks); + boolean found = false; + while (matcher.find()) { + String pemBlock = matcher.group(1).replaceAll("[\\n\\r]+", ""); + ByteArrayInputStream byteStream = new ByteArrayInputStream(Base64.getDecoder().decode(pemBlock)); + java.security.cert.X509Certificate cert = (java.security.cert.X509Certificate) cf.generateCertificate(byteStream); + String alias = cert.getSubjectX500Principal().getName("RFC2253"); + if (alias != null && !keyStore.containsAlias(alias)) { + found = true; + keyStore.setCertificateEntry(alias, cert); + } + } + if (found) { + KeyManagerFactory keyManager = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + keyManager.init(keyStore, null); + TrustManagerFactory trustManager = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManager.init(keyStore); + SSLContext context = SSLContext.getInstance("TLS"); + context.init(keyManager.getKeyManagers(), trustManager.getTrustManagers(), null); + return context; + } + } catch (Throwable t) { + Logs.logsFor(1l).exception(t); } - if (found) { - KeyManagerFactory keyManager = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - keyManager.init(keyStore, null); - TrustManagerFactory trustManager = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - trustManager.init(keyStore); - SSLContext context = SSLContext.getInstance("TLS"); - context.init(keyManager.getKeyManagers(), trustManager.getTrustManagers(), null); - return context; - } - } catch (Throwable t) { - Logs.logsFor(1l).exception(t); } } return SSLContexts.createSystemDefault(); @@ -244,15 +265,17 @@ public Socket createSocket(final HttpContext context) throws IOException { } } - private static Socket newSocket(final HttpContext context) { + private static Socket newSocket(final HttpContext context) throws IOException { InetSocketAddress proxySocks = (InetSocketAddress) context.getAttribute("proxy.socks.address"); - InetSocketAddress proxyHttp = (InetSocketAddress) context.getAttribute("proxy.http.address"); + Socket socket; if (proxySocks != null) { - return new Socket(new Proxy(Proxy.Type.SOCKS, proxySocks)); - } else if (proxyHttp != null) { - return new Socket(new Proxy(Proxy.Type.HTTP, proxyHttp)); + socket = new Socket(new Proxy(Proxy.Type.SOCKS, proxySocks)); + } else { + socket = new Socket(); } - return new Socket(); + socket.setTcpNoDelay(true); + socket.setKeepAlive(true); + return socket; } private boolean isBlocked(String host) { @@ -375,7 +398,7 @@ public void connect() throws IOException { if (proxy.type() == ProxyConfig.Type.SOCKS) { context.setAttribute("proxy.socks.address", proxyAddress); } else { - context.setAttribute("proxy.http.address", proxyAddress); + config.setProxy(new HttpHost(proxy.host(), proxy.port())); } } context.setCookieStore(SettingsManager.get(settingsId.get()).get().cookieStore()); @@ -480,8 +503,7 @@ public String getResponseMessage() throws IOException { @Override public int getResponseCode() throws IOException { exec(); - StatusMonitor.get(settingsId.get()).addRedirect( - urlString, getHeaderField("location")); + StatusMonitor.get(settingsId.get()).addRedirect(urlString, getHeaderField("location")); if (skip.get()) { return 204; }