diff --git a/telegrambots/src/main/java/org/telegram/telegrambots/bots/DefaultAbsSender.java b/telegrambots/src/main/java/org/telegram/telegrambots/bots/DefaultAbsSender.java index 400ab4b9..9a2b9452 100644 --- a/telegrambots/src/main/java/org/telegram/telegrambots/bots/DefaultAbsSender.java +++ b/telegrambots/src/main/java/org/telegram/telegrambots/bots/DefaultAbsSender.java @@ -33,6 +33,7 @@ import org.telegram.telegrambots.updateshandlers.SentCallback; import java.io.IOException; import java.io.Serializable; +import java.net.InetSocketAddress; import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.StandardCharsets; @@ -56,7 +57,7 @@ public abstract class DefaultAbsSender extends AbsSender { protected final ExecutorService exe; private final ObjectMapper objectMapper = new ObjectMapper(); private final DefaultBotOptions options; - private volatile CloseableHttpClient httpclient; + private volatile CloseableHttpClient httpClient; private volatile RequestConfig requestConfig; protected DefaultAbsSender(DefaultBotOptions options) { @@ -64,10 +65,10 @@ public abstract class DefaultAbsSender extends AbsSender { this.exe = Executors.newFixedThreadPool(options.getMaxThreads()); this.options = options; - httpclient = TelegramHttpClientBuilder.build(options); + httpClient = TelegramHttpClientBuilder.build(options); + configureHttpContext(); requestConfig = options.getRequestConfig(); - if (requestConfig == null) { requestConfig = RequestConfig.copy(RequestConfig.custom().build()) .setSocketTimeout(SOCKET_TIMEOUT) @@ -76,6 +77,22 @@ public abstract class DefaultAbsSender extends AbsSender { } } + private void configureHttpContext() { + + if (options.getProxyType() != DefaultBotOptions.ProxyType.NO_PROXY) { + InetSocketAddress socksaddr = new InetSocketAddress(options.getProxyHost(), options.getProxyPort()); + options.getHttpContext().setAttribute("socketAddress", socksaddr); + } + + if (options.getProxyType() == DefaultBotOptions.ProxyType.SOCKS4) { + options.getHttpContext().setAttribute("socksVersion", 4); + } + if (options.getProxyType() == DefaultBotOptions.ProxyType.SOCKS5) { + options.getHttpContext().setAttribute("socksVersion", 5); + } + + } + /** * Returns the token of the bot to be able to perform Telegram Api Requests * @return Token of the bot @@ -734,7 +751,7 @@ public abstract class DefaultAbsSender extends AbsSender { } private String sendHttpPostRequest(HttpPost httppost) throws IOException { - try (CloseableHttpResponse response = httpclient.execute(httppost)) { + try (CloseableHttpResponse response = httpClient.execute(httppost, options.getHttpContext())) { HttpEntity ht = response.getEntity(); BufferedHttpEntity buf = new BufferedHttpEntity(ht); return EntityUtils.toString(buf, StandardCharsets.UTF_8); diff --git a/telegrambots/src/main/java/org/telegram/telegrambots/bots/DefaultBotOptions.java b/telegrambots/src/main/java/org/telegram/telegrambots/bots/DefaultBotOptions.java index e2e7d25e..95ac515b 100644 --- a/telegrambots/src/main/java/org/telegram/telegrambots/bots/DefaultBotOptions.java +++ b/telegrambots/src/main/java/org/telegram/telegrambots/bots/DefaultBotOptions.java @@ -1,8 +1,8 @@ package org.telegram.telegrambots.bots; -import org.apache.http.HttpHost; -import org.apache.http.client.CredentialsProvider; import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.protocol.HttpContext; import org.telegram.telegrambots.ApiConstants; import org.telegram.telegrambots.generics.BotOptions; import org.telegram.telegrambots.updatesreceivers.ExponentialBackOff; @@ -18,14 +18,27 @@ import java.util.List; public class DefaultBotOptions implements BotOptions { private int maxThreads; ///< Max number of threads used for async methods executions (default 1) private RequestConfig requestConfig; + private volatile HttpContext httpContext; private ExponentialBackOff exponentialBackOff; private Integer maxWebhookConnections; private String baseUrl; private List allowedUpdates; + private ProxyType proxyType; + private String proxyHost; + private int proxyPort; + + public enum ProxyType { + NO_PROXY, + HTTP, + SOCKS4, + SOCKS5 + } public DefaultBotOptions() { maxThreads = 1; baseUrl = ApiConstants.BASE_URL; + httpContext = HttpClientContext.create(); + proxyType = ProxyType.NO_PROXY; } @Override @@ -53,6 +66,14 @@ public class DefaultBotOptions implements BotOptions { return maxWebhookConnections; } + public HttpContext getHttpContext() { + return httpContext; + } + + public void setHttpContext(HttpContext httpContext) { + this.httpContext = httpContext; + } + public void setMaxWebhookConnections(Integer maxWebhookConnections) { this.maxWebhookConnections = maxWebhookConnections; } @@ -85,4 +106,27 @@ public class DefaultBotOptions implements BotOptions { this.exponentialBackOff = exponentialBackOff; } + public ProxyType getProxyType() { + return proxyType; + } + + public void setProxyType(ProxyType proxyType) { + this.proxyType = proxyType; + } + + public String getProxyHost() { + return proxyHost; + } + + public void setProxyHost(String proxyHost) { + this.proxyHost = proxyHost; + } + + public int getProxyPort() { + return proxyPort; + } + + public void setProxyPort(int proxyPort) { + this.proxyPort = proxyPort; + } } diff --git a/telegrambots/src/main/java/org/telegram/telegrambots/facilities/TelegramHttpClientBuilder.java b/telegrambots/src/main/java/org/telegram/telegrambots/facilities/TelegramHttpClientBuilder.java index 57e836a3..8585460b 100644 --- a/telegrambots/src/main/java/org/telegram/telegrambots/facilities/TelegramHttpClientBuilder.java +++ b/telegrambots/src/main/java/org/telegram/telegrambots/facilities/TelegramHttpClientBuilder.java @@ -1,10 +1,20 @@ package org.telegram.telegrambots.facilities; +import org.apache.http.config.Registry; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.ProxyAuthenticationStrategy; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.ssl.SSLContexts; import org.telegram.telegrambots.bots.DefaultBotOptions; +import org.telegram.telegrambots.facilities.proxysocketfactorys.HttpConnectionSocketFactory; +import org.telegram.telegrambots.facilities.proxysocketfactorys.HttpSSLConnectionSocketFactory; +import org.telegram.telegrambots.facilities.proxysocketfactorys.SocksSSLConnectionSocketFactory; +import org.telegram.telegrambots.facilities.proxysocketfactorys.SocksConnectionSocketFactory; import java.util.concurrent.TimeUnit; @@ -16,9 +26,30 @@ public class TelegramHttpClientBuilder { public static CloseableHttpClient build(DefaultBotOptions options) { HttpClientBuilder httpClientBuilder = HttpClientBuilder.create() .setSSLHostnameVerifier(new NoopHostnameVerifier()) + .setConnectionManager(createConnectionManager(options)) .setConnectionTimeToLive(70, TimeUnit.SECONDS) .setMaxConnTotal(100); return httpClientBuilder.build(); } + private static HttpClientConnectionManager createConnectionManager(DefaultBotOptions options) { + Registry registry; + switch (options.getProxyType()) { + case NO_PROXY: + return null; + case HTTP: + registry = RegistryBuilder. create() + .register("http", new HttpConnectionSocketFactory()) + .register("https", new HttpSSLConnectionSocketFactory(SSLContexts.createSystemDefault())).build(); + return new PoolingHttpClientConnectionManager(registry); + case SOCKS4: + case SOCKS5: + registry = RegistryBuilder. create() + .register("http", new SocksConnectionSocketFactory()) + .register("https", new SocksSSLConnectionSocketFactory(SSLContexts.createSystemDefault())).build(); + return new PoolingHttpClientConnectionManager(registry); + } + return null; + } + } diff --git a/telegrambots/src/main/java/org/telegram/telegrambots/facilities/proxysocketfactorys/HttpConnectionSocketFactory.java b/telegrambots/src/main/java/org/telegram/telegrambots/facilities/proxysocketfactorys/HttpConnectionSocketFactory.java new file mode 100644 index 00000000..d06f17ce --- /dev/null +++ b/telegrambots/src/main/java/org/telegram/telegrambots/facilities/proxysocketfactorys/HttpConnectionSocketFactory.java @@ -0,0 +1,33 @@ +package org.telegram.telegrambots.facilities.proxysocketfactorys; + +import org.apache.http.HttpHost; +import org.apache.http.conn.socket.PlainConnectionSocketFactory; +import org.apache.http.protocol.HttpContext; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.Socket; + +public class HttpConnectionSocketFactory extends PlainConnectionSocketFactory { + @Override + public Socket createSocket(final HttpContext context) throws IOException { + InetSocketAddress socketAddress = (InetSocketAddress) context.getAttribute("socketAddress"); + Proxy proxy = new Proxy(Proxy.Type.HTTP, socketAddress); + return new Socket(proxy); + } + + @Override + public Socket connectSocket( + int connectTimeout, + Socket socket, + HttpHost host, + InetSocketAddress remoteAddress, + InetSocketAddress localAddress, + HttpContext context) throws IOException { + String hostName = host.getHostName(); + int port = remoteAddress.getPort(); + InetSocketAddress unresolvedRemote = InetSocketAddress.createUnresolved(hostName, port); + return super.connectSocket(connectTimeout, socket, host, unresolvedRemote, localAddress, context); + } +} diff --git a/telegrambots/src/main/java/org/telegram/telegrambots/facilities/proxysocketfactorys/HttpSSLConnectionSocketFactory.java b/telegrambots/src/main/java/org/telegram/telegrambots/facilities/proxysocketfactorys/HttpSSLConnectionSocketFactory.java new file mode 100644 index 00000000..2c97e934 --- /dev/null +++ b/telegrambots/src/main/java/org/telegram/telegrambots/facilities/proxysocketfactorys/HttpSSLConnectionSocketFactory.java @@ -0,0 +1,40 @@ +package org.telegram.telegrambots.facilities.proxysocketfactorys; + +import org.apache.http.HttpHost; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.protocol.HttpContext; + +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.Socket; + +public class HttpSSLConnectionSocketFactory extends SSLConnectionSocketFactory { + + public HttpSSLConnectionSocketFactory(final SSLContext sslContext) { + super(sslContext, new NoopHostnameVerifier()); + } + + @Override + public Socket createSocket(final HttpContext context) throws IOException { + InetSocketAddress socketAddress = (InetSocketAddress) context.getAttribute("socketAddress"); + Proxy proxy = new Proxy(Proxy.Type.HTTP, socketAddress); + return new Socket(proxy); + } + + @Override + public Socket connectSocket( + int connectTimeout, + Socket socket, + HttpHost host, + InetSocketAddress remoteAddress, + InetSocketAddress localAddress, + HttpContext context) throws IOException { + String hostName = host.getHostName(); + int port = remoteAddress.getPort(); + InetSocketAddress unresolvedRemote = InetSocketAddress.createUnresolved(hostName, port); + return super.connectSocket(connectTimeout, socket, host, unresolvedRemote, localAddress, context); + } +} diff --git a/telegrambots/src/main/java/org/telegram/telegrambots/facilities/proxysocketfactorys/SocksConnectionSocketFactory.java b/telegrambots/src/main/java/org/telegram/telegrambots/facilities/proxysocketfactorys/SocksConnectionSocketFactory.java new file mode 100644 index 00000000..d2b29a5e --- /dev/null +++ b/telegrambots/src/main/java/org/telegram/telegrambots/facilities/proxysocketfactorys/SocksConnectionSocketFactory.java @@ -0,0 +1,37 @@ +package org.telegram.telegrambots.facilities.proxysocketfactorys; + + +import org.apache.http.HttpHost; +import org.apache.http.conn.socket.PlainConnectionSocketFactory; +import org.apache.http.protocol.HttpContext; +import sun.net.SocksProxy; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.Socket; + +public class SocksConnectionSocketFactory extends PlainConnectionSocketFactory { + + @Override + public Socket createSocket(final HttpContext context) throws IOException { + InetSocketAddress socksaddr = (InetSocketAddress) context.getAttribute("socketAddress"); + int socksVersion = (Integer) context.getAttribute("socksVersion"); + Proxy proxy = SocksProxy.create(socksaddr, socksVersion); + return new Socket(proxy); + } + + @Override + public Socket connectSocket( + int connectTimeout, + Socket socket, + HttpHost host, + InetSocketAddress remoteAddress, + InetSocketAddress localAddress, + HttpContext context) throws IOException { + String hostName = host.getHostName(); + int port = remoteAddress.getPort(); + InetSocketAddress unresolvedRemote = InetSocketAddress.createUnresolved(hostName, port); + return super.connectSocket(connectTimeout, socket, host, unresolvedRemote, localAddress, context); + } +} diff --git a/telegrambots/src/main/java/org/telegram/telegrambots/facilities/proxysocketfactorys/SocksSSLConnectionSocketFactory.java b/telegrambots/src/main/java/org/telegram/telegrambots/facilities/proxysocketfactorys/SocksSSLConnectionSocketFactory.java new file mode 100644 index 00000000..cbd46b6a --- /dev/null +++ b/telegrambots/src/main/java/org/telegram/telegrambots/facilities/proxysocketfactorys/SocksSSLConnectionSocketFactory.java @@ -0,0 +1,43 @@ +package org.telegram.telegrambots.facilities.proxysocketfactorys; + +import org.apache.http.HttpHost; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.protocol.HttpContext; +import sun.net.SocksProxy; + +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.Socket; + + +public class SocksSSLConnectionSocketFactory extends SSLConnectionSocketFactory { + + public SocksSSLConnectionSocketFactory(final SSLContext sslContext) { + super(sslContext, new NoopHostnameVerifier()); + } + + @Override + public Socket createSocket(final HttpContext context) throws IOException { + InetSocketAddress socksaddr = (InetSocketAddress) context.getAttribute("socketAddress"); + int socksVersion = (Integer) context.getAttribute("socksVersion"); + Proxy proxy = SocksProxy.create(socksaddr, socksVersion); + return new Socket(proxy); + } + + @Override + public Socket connectSocket( + int connectTimeout, + Socket socket, + HttpHost host, + InetSocketAddress remoteAddress, + InetSocketAddress localAddress, + HttpContext context) throws IOException { + String hostName = host.getHostName(); + int port = remoteAddress.getPort(); + InetSocketAddress unresolvedRemote = InetSocketAddress.createUnresolved(hostName, port); + return super.connectSocket(connectTimeout, socket, host, unresolvedRemote, localAddress, context); + } +} diff --git a/telegrambots/src/main/java/org/telegram/telegrambots/updatesreceivers/DefaultBotSession.java b/telegrambots/src/main/java/org/telegram/telegrambots/updatesreceivers/DefaultBotSession.java index 6e6b56b9..37b7fbfe 100644 --- a/telegrambots/src/main/java/org/telegram/telegrambots/updatesreceivers/DefaultBotSession.java +++ b/telegrambots/src/main/java/org/telegram/telegrambots/updatesreceivers/DefaultBotSession.java @@ -245,7 +245,7 @@ public class DefaultBotSession implements BotSession { httpPost.setConfig(requestConfig); httpPost.setEntity(new StringEntity(objectMapper.writeValueAsString(request), ContentType.APPLICATION_JSON)); - try (CloseableHttpResponse response = httpclient.execute(httpPost)) { + try (CloseableHttpResponse response = httpclient.execute(httpPost, options.getHttpContext())) { HttpEntity ht = response.getEntity(); BufferedHttpEntity buf = new BufferedHttpEntity(ht); String responseContent = EntityUtils.toString(buf, StandardCharsets.UTF_8);