Опитвам се да създам приложение, което изпраща HTTP
заявки чрез Apache HC 4 през SOCKS5 прокси. Не мога да използвам глобален прокси на приложението, защото приложението е многонишково (имам нужда от различно прокси за всяко HttpClient
копие). Не намерих примери за използване на SOCKS5 с HC4. Как мога да го използвам?
Как да използвам Socks 5 прокси с Apache HTTP Client 4?
Отговори (4)
SOCK е прокси протокол на ниво TCP/IP, а не HTTP. Не се поддържа от HttpClient веднага.
Човек може да персонализира HttpClient за установяване на връзки чрез SOCKS прокси чрез използване на персонализирана фабрика за гнезда за връзка
РЕДАКТИРАНЕ: се променя на SSL вместо обикновени сокети
Registry<ConnectionSocketFactory> reg = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", new MyConnectionSocketFactory(SSLContexts.createSystemDefault()))
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(reg);
CloseableHttpClient httpclient = HttpClients.custom()
.setConnectionManager(cm)
.build();
try {
InetSocketAddress socksaddr = new InetSocketAddress("mysockshost", 1234);
HttpClientContext context = HttpClientContext.create();
context.setAttribute("socks.address", socksaddr);
HttpHost target = new HttpHost("localhost", 80, "http");
HttpGet request = new HttpGet("/");
System.out.println("Executing request " + request + " to " + target + " via SOCKS proxy " + socksaddr);
CloseableHttpResponse response = httpclient.execute(target, request, context);
try {
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
EntityUtils.consume(response.getEntity());
} finally {
response.close();
}
} finally {
httpclient.close();
}
static class MyConnectionSocketFactory extends SSLConnectionSocketFactory {
public MyConnectionSocketFactory(final SSLContext sslContext) {
super(sslContext);
}
@Override
public Socket createSocket(final HttpContext context) throws IOException {
InetSocketAddress socksaddr = (InetSocketAddress) context.getAttribute("socks.address");
Proxy proxy = new Proxy(Proxy.Type.SOCKS, socksaddr);
return new Socket(proxy);
}
}
HttpClientContext context = HttpClientContext.create();
не е използван
- person soulmachine; 03.09.2015
HttpHost target = new HttpHost("localhost", 80, "http");
- person Robinho; 09.03.2016
PlainConnectionSocketFactory
на http
и тогава работи. Публикувайте го тук, в случай че някой друг има същия проблем.
- person qqibrow; 05.10.2016
CredentialsProvider
, но без успех. Изглежда, че httpclient игнорира предоставените идентификационни данни и вместо това използва моето локално потребителско име.
- person Emptyfruit; 10.07.2018
Отговорът по-горе работи доста добре, освен ако вашата страна също не отрови DNS записите. Много е трудно да се каже на Java „не използва моите DNS сървъри, докато се свързва чрез прокси“, както е адресирано в тези два въпроса:
java runtime 6 със socks v5 прокси - Възможно ли е?
Как да получа URL връзка чрез прокси в java?
Също така е трудно за Apache HttpClient, тъй като той също се опитва да разрешава имена на хостове локално. Чрез известна модификация на кода по-горе това може да се реши:
static class FakeDnsResolver implements DnsResolver {
@Override
public InetAddress[] resolve(String host) throws UnknownHostException {
// Return some fake DNS record for every request, we won't be using it
return new InetAddress[] { InetAddress.getByAddress(new byte[] { 1, 1, 1, 1 }) };
}
}
static class MyConnectionSocketFactory extends PlainConnectionSocketFactory {
@Override
public Socket createSocket(final HttpContext context) throws IOException {
InetSocketAddress socksaddr = (InetSocketAddress) context.getAttribute("socks.address");
Proxy proxy = new Proxy(Proxy.Type.SOCKS, socksaddr);
return new Socket(proxy);
}
@Override
public Socket connectSocket(int connectTimeout, Socket socket, HttpHost host, InetSocketAddress remoteAddress,
InetSocketAddress localAddress, HttpContext context) throws IOException {
// Convert address to unresolved
InetSocketAddress unresolvedRemote = InetSocketAddress
.createUnresolved(host.getHostName(), remoteAddress.getPort());
return super.connectSocket(connectTimeout, socket, host, unresolvedRemote, localAddress, context);
}
}
static class MySSLConnectionSocketFactory extends SSLConnectionSocketFactory {
public MySSLConnectionSocketFactory(final SSLContext sslContext) {
// You may need this verifier if target site's certificate is not secure
super(sslContext, ALLOW_ALL_HOSTNAME_VERIFIER);
}
@Override
public Socket createSocket(final HttpContext context) throws IOException {
InetSocketAddress socksaddr = (InetSocketAddress) context.getAttribute("socks.address");
Proxy proxy = new Proxy(Proxy.Type.SOCKS, socksaddr);
return new Socket(proxy);
}
@Override
public Socket connectSocket(int connectTimeout, Socket socket, HttpHost host, InetSocketAddress remoteAddress,
InetSocketAddress localAddress, HttpContext context) throws IOException {
// Convert address to unresolved
InetSocketAddress unresolvedRemote = InetSocketAddress
.createUnresolved(host.getHostName(), remoteAddress.getPort());
return super.connectSocket(connectTimeout, socket, host, unresolvedRemote, localAddress, context);
}
}
public static void main(String[] args) throws Exception {
Registry<ConnectionSocketFactory> reg = RegistryBuilder.<ConnectionSocketFactory> create()
.register("http", new MyConnectionSocketFactory())
.register("https", new MySSLConnectionSocketFactory(SSLContexts.createSystemDefault())).build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(reg, new FakeDnsResolver());
CloseableHttpClient httpclient = HttpClients.custom().setConnectionManager(cm).build();
try {
InetSocketAddress socksaddr = new InetSocketAddress("mysockshost", 1234);
HttpClientContext context = HttpClientContext.create();
context.setAttribute("socks.address", socksaddr);
HttpGet request = new HttpGet("https://www.funnyordie.com");
System.out.println("Executing request " + request + " via SOCKS proxy " + socksaddr);
CloseableHttpResponse response = httpclient.execute(request, context);
try {
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
int i = -1;
InputStream stream = response.getEntity().getContent();
while ((i = stream.read()) != -1) {
System.out.print((char) i);
}
EntityUtils.consume(response.getEntity());
} finally {
response.close();
}
} finally {
httpclient.close();
}
}
Вдъхновен от отговора на @oleg. Можете да направите помощна програма, която ви дава правилно конфигуриран CloseableHttpClient без специални ограничения за това как да го извикате.
Можете да използвате ProxySelector в ConnectionSocketFactory, за да изберете проксито.
Клас помощник за конструиране на екземпляри на CloseableHttpClient:
import org.apache.http.HttpHost;
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.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpCoreContext;
import org.apache.http.ssl.SSLContexts;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.net.*;
public final class HttpHelper {
public static CloseableHttpClient createClient()
{
Registry<ConnectionSocketFactory> reg = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", ProxySelectorPlainConnectionSocketFactory.INSTANCE)
.register("https", new ProxySelectorSSLConnectionSocketFactory(SSLContexts.createSystemDefault()))
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(reg);
return HttpClients.custom()
.setConnectionManager(cm)
.build();
}
private enum ProxySelectorPlainConnectionSocketFactory implements ConnectionSocketFactory {
INSTANCE;
@Override
public Socket createSocket(HttpContext context) {
return HttpHelper.createSocket(context);
}
@Override
public Socket connectSocket(int connectTimeout, Socket sock, HttpHost host, InetSocketAddress remoteAddress, InetSocketAddress localAddress, HttpContext context) throws IOException {
return PlainConnectionSocketFactory.INSTANCE.connectSocket(connectTimeout, sock, host, remoteAddress, localAddress, context);
}
}
private static final class ProxySelectorSSLConnectionSocketFactory extends SSLConnectionSocketFactory {
ProxySelectorSSLConnectionSocketFactory(SSLContext sslContext) {
super(sslContext);
}
@Override
public Socket createSocket(HttpContext context) {
return HttpHelper.createSocket(context);
}
}
private static Socket createSocket(HttpContext context) {
HttpHost httpTargetHost = (HttpHost) context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST);
URI uri = URI.create(httpTargetHost.toURI());
Proxy proxy = ProxySelector.getDefault().select(uri).iterator().next();
return new Socket(proxy);
}
}
Клиентски код, използващ това:
import com.okta.tools.helpers.HttpHelper;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.net.URI;
public class Main {
public static void main(String[] args) throws IOException {
URI uri = URI.create("http://example.com/");
HttpGet request = new HttpGet(uri);
try (CloseableHttpClient closeableHttpClient = HttpHelper.createClient()) {
try (CloseableHttpResponse response = closeableHttpClient.execute(request)) {
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
System.out.println(EntityUtils.toString(response.getEntity()));
}
}
}
}
Ако знаете кои URI адреси трябва да отидат до прокси, можете също да използвате ProxySelector на нисък слой: https://docs.oracle.com/javase/7/docs/technotes/guides/net/proxies.html където за всяка осъществена връзка на Socket можете да решите какво трябва да се използват проксита.
Изглежда нещо подобно:
public class MyProxySelector extends ProxySelector {
...
public java.util.List<Proxy> select(URI uri) {
...
if (uri is what I need) {
return list of my Proxies
}
...
}
...
}
След това използвате вашия селектор:
public static void main(String[] args) {
MyProxySelector ps = new MyProxySelector(ProxySelector.getDefault());
ProxySelector.setDefault(ps);
// rest of the application
}