Merge pull request #451 from AzZureman/proxy-feature

Socks proxy feature
This commit is contained in:
Ruben Bermudez 2018-07-16 13:28:44 +03:00 committed by GitHub
commit dd5d14df6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 272 additions and 67 deletions

View File

@ -31,7 +31,7 @@ public class MyBot extends AbilityBot {
Now you are able to set up your proxy
#### without authentication
#### Without authentication
```java
public class Main {
@ -51,13 +51,12 @@ public class Main {
TelegramBotsApi botsApi = new TelegramBotsApi();
// Set up Http proxy
DefaultBotOptions botOptions = ApiContext.getInstance(DefaultBotOptions.class);
DefaultBotOptions botOptions = ApiContext.getInstance(DefaultBotOptions.class);
HttpHost httpHost = new HttpHost(PROXY_HOST, PROXY_PORT);
RequestConfig requestConfig = RequestConfig.custom().setProxy(httpHost).setAuthenticationEnabled(false).build();
botOptions.setRequestConfig(requestConfig);
botOptions.setHttpProxy(httpHost);
botOptions.setProxyHost(PROXY_HOST);
botOptions.setProxyPort(PROXY_PORT);
// Select proxy type: [HTTP|SOCKS4|SOCKS5] (default: NO_PROXY)
botOptions.setProxyType(DefaultBotOptions.ProxyType.SOCKS5);
// Register your newly created AbilityBot
MyBot bot = new MyBot(BOT_TOKEN, BOT_NAME, botOptions);
@ -89,25 +88,26 @@ public class Main {
public static void main(String[] args) {
try {
// Create the Authenticator that will return auth's parameters for proxy authentication
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(PROXY_USER, PROXY_PASSWORD.toCharArray());
}
});
ApiContextInitializer.init();
// Create the TelegramBotsApi object to register your bots
TelegramBotsApi botsApi = new TelegramBotsApi();
// Set up Http proxy
DefaultBotOptions botOptions = ApiContext.getInstance(DefaultBotOptions.class);
DefaultBotOptions botOptions = ApiContext.getInstance(DefaultBotOptions.class);
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope(PROXY_HOST, PROXY_PORT),
new UsernamePasswordCredentials(PROXY_USER, PROXY_PASSWORD));
HttpHost httpHost = new HttpHost(PROXY_HOST, PROXY_PORT);
RequestConfig requestConfig = RequestConfig.custom().setProxy(httpHost).setAuthenticationEnabled(true).build();
botOptions.setRequestConfig(requestConfig);
botOptions.setCredentialsProvider(credsProvider);
botOptions.setHttpProxy(httpHost);
botOptions.setProxyHost(PROXY_HOST);
botOptions.setProxyPort(PROXY_PORT);
// Select proxy type: [HTTP|SOCKS4|SOCKS5] (default: NO_PROXY)
botOptions.setProxyType(DefaultBotOptions.ProxyType.SOCKS5);
// Register your newly created AbilityBot
MyBot bot = new MyBot(BOT_TOKEN, BOT_NAME, botOptions);
@ -119,4 +119,6 @@ public class Main {
}
}
}
```
```
If you need something more complex than one proxy, then you can create more complex Authenticator that will check host and other parameters of proxy and return auth values based on them (for more information see code of java.net.Authenticator class)

View File

@ -7,7 +7,7 @@
<groupId>org.telegram</groupId>
<artifactId>Bots</artifactId>
<packaging>pom</packaging>
<version>3.6.1</version>
<version>3.6.2</version>
<modules>
<module>telegrambots</module>

View File

@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.telegram</groupId>
<artifactId>telegrambots-abilities</artifactId>
<version>3.6.1</version>
<version>3.6.2</version>
<packaging>jar</packaging>
<name>Telegram Ability Bot</name>

View File

@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.telegram</groupId>
<artifactId>telegrambotsextensions</artifactId>
<version>3.6.1</version>
<version>3.6.2</version>
<packaging>jar</packaging>
<name>Telegram Bots Extensions</name>

View File

@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.telegram</groupId>
<artifactId>telegrambots-meta</artifactId>
<version>3.6.1</version>
<version>3.6.2</version>
<packaging>jar</packaging>
<name>Telegram Bots Meta</name>

View File

@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.telegram</groupId>
<artifactId>telegrambots-spring-boot-starter</artifactId>
<version>3.6.1</version>
<version>3.6.2</version>
<packaging>jar</packaging>
<name>Telegram Bots Spring Boot Starter</name>

View File

@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId>
<version>3.6.1</version>
<version>3.6.2</version>
<packaging>jar</packaging>
<name>Telegram Bots</name>

View File

@ -6,14 +6,11 @@ import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.entity.BufferedHttpEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
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.util.EntityUtils;
import org.telegram.telegrambots.api.methods.BotApiMethod;
import org.telegram.telegrambots.api.methods.groupadministration.SetChatPhoto;
@ -33,13 +30,13 @@ 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;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static org.telegram.telegrambots.Constants.SOCKET_TIMEOUT;
@ -56,7 +53,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 +61,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 +73,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 +747,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);

View File

@ -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,17 +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<String> allowedUpdates;
private ProxyType proxyType;
private String proxyHost;
private int proxyPort;
private CredentialsProvider credentialsProvider;
private HttpHost httpProxy;
public enum ProxyType {
NO_PROXY,
HTTP,
SOCKS4,
SOCKS5
}
public DefaultBotOptions() {
maxThreads = 1;
baseUrl = ApiConstants.BASE_URL;
httpContext = HttpClientContext.create();
proxyType = ProxyType.NO_PROXY;
}
@Override
@ -56,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;
}
@ -88,19 +106,27 @@ public class DefaultBotOptions implements BotOptions {
this.exponentialBackOff = exponentialBackOff;
}
public CredentialsProvider getCredentialsProvider() {
return credentialsProvider;
public ProxyType getProxyType() {
return proxyType;
}
public void setCredentialsProvider(CredentialsProvider credentialsProvider) {
this.credentialsProvider = credentialsProvider;
public void setProxyType(ProxyType proxyType) {
this.proxyType = proxyType;
}
public HttpHost getHttpProxy() {
return httpProxy;
public String getProxyHost() {
return proxyHost;
}
public void setHttpProxy(HttpHost httpProxy) {
this.httpProxy = httpProxy;
public void setProxyHost(String proxyHost) {
this.proxyHost = proxyHost;
}
public int getProxyPort() {
return proxyPort;
}
public void setProxyPort(int proxyPort) {
this.proxyPort = proxyPort;
}
}

View File

@ -3,12 +3,10 @@ package org.telegram.telegrambots.bots;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.entity.BufferedHttpEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONException;

View File

@ -1,10 +1,19 @@
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,22 +25,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);
if (options.getHttpProxy() != null) {
httpClientBuilder.setProxy(options.getHttpProxy());
if (options.getCredentialsProvider() != null) {
httpClientBuilder
.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy())
.setDefaultCredentialsProvider(options.getCredentialsProvider());
}
}
return httpClientBuilder.build();
}
private static HttpClientConnectionManager createConnectionManager(DefaultBotOptions options) {
Registry<ConnectionSocketFactory> registry;
switch (options.getProxyType()) {
case NO_PROXY:
return null;
case HTTP:
registry = RegistryBuilder.<ConnectionSocketFactory> create()
.register("http", new HttpConnectionSocketFactory())
.register("https", new HttpSSLConnectionSocketFactory(SSLContexts.createSystemDefault())).build();
return new PoolingHttpClientConnectionManager(registry);
case SOCKS4:
case SOCKS5:
registry = RegistryBuilder.<ConnectionSocketFactory> create()
.register("http", new SocksConnectionSocketFactory())
.register("https", new SocksSSLConnectionSocketFactory(SSLContexts.createSystemDefault())).build();
return new PoolingHttpClientConnectionManager(registry);
}
return null;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -6,13 +6,10 @@ import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.entity.BufferedHttpEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
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.util.EntityUtils;
import org.json.JSONException;
import org.telegram.telegrambots.ApiConstants;
@ -32,7 +29,6 @@ import java.nio.charset.StandardCharsets;
import java.security.InvalidParameterException;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.TimeUnit;
import static org.telegram.telegrambots.Constants.SOCKET_TIMEOUT;
@ -245,7 +241,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);