How can we resolve hostname by socks proxy server in java ?
in curl we can do it with "socks5h://"
curl -x socks5h://localhost:8001 http://www.google.com/
but in java i cant find any way to do this
and i am using apache httpclient for send requests
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);
}
}
I'm trying to test my application, which sends a REST request to a server and retrieves some fileNames. I'm trying to deploy the following two classes:
#ApplicationPath("/")
public class JaxRsActivator extends Application {
}
and
#Path("/files")
#RequestScoped
public class RestEndpointMock {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getFileNames() {
return new MockResponse();
}
private class MockResponse extends Response {
#Override
public Object getEntity() {
return new MockEntity();
}
}
private class MockEntity implements HttpEntity {
#Override
public InputStream getContent() throws IOException, UnsupportedOperationException {
return ByteArrayInputStream(fileNamesAsJson.getBytes(StandardCharsets.UTF_8));
}
}
}
The aim is when the actual usecase sends a REST request, this mocked interface is hit and it returns the mocked list of file names instead.
I'm building up the request as follows (for the test call, to disable SSL certificate errors):
public static HttpClient createHttpClient() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
final SSLContext sslContext = SSLContext.getInstance("SSL");
// set up a TrustManager that trusts everything
sslContext.init(null, new TrustManager[] { new X509TrustManager() {
}, new SecureRandom());
final SSLSocketFactory sf = new SSLSocketFactory(sslContext);
final Scheme httpsScheme = new Scheme("https", 443, sf);
final SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(httpsScheme);
final ClientConnectionManager cm = new SingleClientConnManager(schemeRegistry);
final HttpClient httpClient = new DefaultHttpClient(cm);
return httpClient;
}
Then finally sending the request as:
private HttpResponse sendPostRequest(){
final HttpClient httpClient = createHttpClient();
final String postUrl = String.format(https://localhost/login);
final HttpPost postRequest = new HttpPost(postUrl);
return httpClient.execute(postRequest)
}
But I end up getting a ConnectionRefused exception when attempting this way. Unsure if I've set up the mocked endpoint incorrectly, or if something else is messed up along the way.
I can't fetch resources from SNI sites with HttpClient. URLs I am trying to fetch are: https://dabar.srce.hr/, https://www.lutrija.hr/cms/splash.
I get this error:
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
As I understand the documentation it should work like this out of the box (and it works for non sni https sites):
url = "https://dabar.srce.hr/";
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
// trust all certificates
public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
return true;
}
}).build();
SSLConnectionSocketFactory sslSF = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslSF).build();
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse response = httpclient.execute(httpGet);
System.out.println(response.getStatusLine().toString());
HttpEntity entity = response.getEntity();
System.out.println(EntityUtils.toString(entity));
I tried to explicitly enable SNIExtension:
System.setProperty("jsse.enableSNIExtension", "true");
I tried overriding SSLConnectionSocketFactory:
SSLConnectionSocketFactory sslSF = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE) {
String targetHost = "";
#Override
public Socket createLayeredSocket(Socket socket, String target, int port, HttpContext context)
throws IOException {
this.targetHost = target;
return super.createLayeredSocket(socket, target, port, context);
}
#Override
protected void prepareSocket(SSLSocket socket) throws IOException {
try {
PropertyUtils.setProperty(socket, "host", this.targetHost);
} catch (Exception ex) {
}
super.prepareSocket(socket);
}
#Override
public Socket connectSocket(int connectTimeout, Socket socket, HttpHost host, InetSocketAddress remoteAddress, InetSocketAddress localAddress, HttpContext context)
throws IOException {
if (socket instanceof SSLSocket) {
try {
PropertyUtils.setProperty(socket, "host", host.getHostName());
} catch (NoSuchMethodException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
}
}
return super.connectSocket(connectTimeout, socket, host, remoteAddress,
localAddress, context);
}
};
What am I missing?
I ran into the same issue. This custom SSLConnectionSocketFactory fixed it for me:
public class TrustAllSslSocketFactory extends SSLConnectionSocketFactory {
static final HostnameVerifier ALL_HOSTNAME_VERIFIER = (String hostname, SSLSession session) -> true;
public TrustAllSslSocketFactory(SSLContext sslContext) {
super(sslContext, ALL_HOSTNAME_VERIFIER);
}
#Override
protected void prepareSocket(SSLSocket socket) throws IOException {
String hostName = socket.getInetAddress().getHostName();
SNIHostName sniHostName = new SNIHostName(hostName);
List serverNames = new ArrayList(1);
serverNames.add(sniHostName);
SSLParameters sslParameters = socket.getSSLParameters();
sslParameters.setServerNames(serverNames);
socket.setSSLParameters(sslParameters);
}
}
See http://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#SNIExamples
What is the easiest way to give HttpClient a list of routes, host names, IP addresses that should be prevented from access?
Something like this (this code doesn't work, you can't set 0 now):
HttpHost block = new HttpHost("example.com", 80);
cm.setMaxPerRoute(new HttpRoute(block), 0);
final Set<HttpHost> sitesToBlock = new HashSet<HttpHost>();
sitesToBlock.add(new HttpHost("example.com", 80));
DefaultRoutePlanner routePlanner = new DefaultRoutePlanner(DefaultSchemePortResolver.INSTANCE) {
#Override
public HttpRoute determineRoute(
final HttpHost host, final HttpRequest request, final HttpContext context) throws HttpException {
final HttpRoute route = super.determineRoute(host, request, context);
if (sitesToBlock.contains(route.getTargetHost())) {
throw new HttpException("Connection to " + host + " is blocked");
}
return route;
}
};
CloseableHttpClient client = HttpClients.custom()
.setRoutePlanner(routePlanner)
.build();
}
Is there an easier way to setup the http client for preemptive basic authentication than what described here?
In previous version (3.x) it used to be a simple method call (eg, httpClient.getParams().setAuthenticationPreemptive(true)).
The main thing I want to avoid is adding the BasicHttpContext to each method I execute.
If you are looking to force HttpClient 4 to authenticate with a single request, the following will work:
String username = ...
String password = ...
UsernamePasswordCredentials creds = new UsernamePasswordCredentials(username, password);
HttpRequest request = ...
request.addHeader(new BasicScheme().authenticate(creds, request));
It's difficult to do this without passing a context through every time, but you can probably do it by using a request interceptor. Here is some code that we use (found from their JIRA, iirc):
// Pre-emptive authentication to speed things up
BasicHttpContext localContext = new BasicHttpContext();
BasicScheme basicAuth = new BasicScheme();
localContext.setAttribute("preemptive-auth", basicAuth);
httpClient.addRequestInterceptor(new PreemptiveAuthInterceptor(), 0);
(...)
static class PreemptiveAuthInterceptor implements HttpRequestInterceptor {
public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE);
// If no auth scheme avaialble yet, try to initialize it
// preemptively
if (authState.getAuthScheme() == null) {
AuthScheme authScheme = (AuthScheme) context.getAttribute("preemptive-auth");
CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(ClientContext.CREDS_PROVIDER);
HttpHost targetHost = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
if (authScheme != null) {
Credentials creds = credsProvider.getCredentials(new AuthScope(targetHost.getHostName(), targetHost.getPort()));
if (creds == null) {
throw new HttpException("No credentials for preemptive authentication");
}
authState.setAuthScheme(authScheme);
authState.setCredentials(creds);
}
}
}
}
This is the same solution as Mat's Mannion's, but you don't have to put localContext to each request. It's simpler, but it adds authentication to ALL requests. Useful, if you don't have control over individual requests, as in my case when using Apache Solr, which uses HttpClient internally.
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthState;
import org.apache.http.auth.Credentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpCoreContext;
httpClient.addRequestInterceptor(new PreemptiveAuthInterceptor(), 0);
(...)
static class PreemptiveAuthInterceptor implements HttpRequestInterceptor {
public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
AuthState authState = (AuthState) context.getAttribute(HttpClientContext.TARGET_AUTH_STATE);
// If no auth scheme available yet, try to initialize it
// preemptively
if (authState.getAuthScheme() == null) {
CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(HttpClientContext.CREDS_PROVIDER);
HttpHost targetHost = (HttpHost) context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST);
Credentials creds = credsProvider.getCredentials(new AuthScope(targetHost.getHostName(), targetHost.getPort()));
if (creds == null) {
throw new HttpException("No credentials for preemptive authentication");
}
authState.update(new BasicScheme(), creds);
}
}
}
Of course, you have to set the credentials provider:
httpClient.getCredentialsProvider().setCredentials(
new AuthScope(url.getHost(), url.getPort()),
new UsernamePasswordCredentials(username, password))
The AuthScope must not contain realm, as it is not known in advance.
A lot of the answers above use deprecated code. I am using Apache SOLRJ version 5.0.0.
My code consists of
private HttpSolrClient solrClient;
private void initialiseSOLRClient() {
URL solrURL = null;
try {
solrURL = new URL(urlString);
} catch (MalformedURLException e) {
LOG.error("Cannot parse the SOLR URL!!" + urlString);
throw new SystemException("Cannot parse the SOLR URL!! " + urlString, e);
}
String host = solrURL.getHost();
int port = solrURL.getPort();
AuthScope authScope = new AuthScope(host, port);
BasicTextEncryptor textEncryptor = new BasicTextEncryptor();
textEncryptor.setPassword("red bananas in the spring");
String decryptPass = textEncryptor.decrypt(pass);
UsernamePasswordCredentials creds = new UsernamePasswordCredentials(userName, decryptPass);
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
authScope,
creds);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.addInterceptorFirst(new PreemptiveAuthInterceptor());
builder.setDefaultCredentialsProvider(credsProvider);
CloseableHttpClient httpClient = builder.build();
solrClient = new HttpSolrClient(urlString, httpClient);
}
The PreemptiveAuthInterceptor is now as follows:-
static class PreemptiveAuthInterceptor implements HttpRequestInterceptor {
public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
AuthState authState = (AuthState) context.getAttribute(HttpClientContext.TARGET_AUTH_STATE);
// If no auth scheme available yet, try to initialize it
// preemptively
if (authState.getAuthScheme() == null) {
CredentialsProvider credsProvider = (CredentialsProvider)
context.getAttribute(HttpClientContext.CREDS_PROVIDER);
HttpHost targetHost = (HttpHost) context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST);
AuthScope authScope = new AuthScope(targetHost.getHostName(), targetHost.getPort());
Credentials creds = credsProvider.getCredentials(authScope);
if(creds == null){
}
authState.update(new BasicScheme(), creds);
}
}
}
A little late to the party but I came accross the thread trying to solve this for proxy pre-authorization of a post request. To add to Adam's response, I found the following worked for me:
HttpPost httppost = new HttpPost(url);
UsernamePasswordCredentials creds = new UsernamePasswordCredentials(username, password);
Header bs = new BasicScheme().authenticate(creds, httppost);
httppost.addHeader("Proxy-Authorization", bs.getValue());
Thought that might be helpful for anyone else who runs into this.
I think the best way may be to just do it manually. I added the following function
Classic Java:
import javax.xml.bind.DatatypeConverter;
...
private static void addAuthHeader(HttpRequestBase http, String username, String password) throws UnsupportedEncodingException {
String encoded = DatatypeConverter.printBase64Binary((username + ":" + password).getBytes("UTF-8"));
http.addHeader("AUTHORIZATION", "Basic " + encoded);
}
HTTPRequestBase can be an instance of HttpGet or HttpPost
Android:
import android.util.Base64;
...
private static void addAuthHeader(HttpRequestBase http, String username, String password) throws UnsupportedEncodingException {
String encoded = Base64.encodeToString((username + ":" + password).getBytes("UTF-8"), Base64.NO_WRAP);
http.addHeader("AUTHORIZATION", "Basic " + encoded);
}
I'm using this code, based on my reading of the HTTPClient 4.5 docs:
HttpClientContext ctx = HttpClientContext.create()
ctx.setCredentialsProvider(new BasicCredentialsProvider())
ctx.setAuthCache(new BasicAuthCache())
UsernamePasswordCredentials creds = new UsernamePasswordCredentials(user, pass)
AuthScope authScope = new AuthScope(host, port)
ctx.getCredentialsProvider.setCredentials(authScope, credentials)
// This part makes authentication preemptive:
HttpHost targetHost = new HttpHost(host, port, scheme)
ctx.getAuthCache.put(targetHost, new BasicScheme())
...and make sure you always pass that context to HTTPClient.execute().
I don't quite get your closing comment. It's the HttpClient that has all of that machinery for doing preemptive auth, and you only have to do that once (when you construct and configure your HttpClient). Once you've done that, you construct your method instances the same way as always. You don't "add the BasicHttpContext" to the method.
Your best bet, I'd think, is to have your own object that sets up all of the junk required for preemptive auth, and has a simple method or methods for executing requests on given HTTPMethod objects.
in android,Mat Mannion's answer can't resolve https,still send two requests,you can do like below,the trick is append authHeader with user-agent:
public static DefaultHttpClient createProxyHttpClient() {
try {
final DefaultHttpClient client = createPlaintHttpClient();
client.setRoutePlanner(new HttpRoutePlanner() {
#Override
public HttpRoute determineRoute(HttpHost target, HttpRequest request, HttpContext context) throws HttpException {
boolean isSecure = "https".equalsIgnoreCase(target.getSchemeName());
if (needProxy) {
Header header = isSecure ? ProxyUtils.createHttpsAuthHeader() : ProxyUtils.createAuthHeader();
if (isSecure) {
client.getParams().setParameter(CoreProtocolPNames.USER_AGENT, com.netease.cloudmusic.utils.HttpRequest.USER_AGENT + "\r\n" + header.getName() + ":" + header.getValue());
} else {
client.getParams().setParameter(CoreProtocolPNames.USER_AGENT, com.netease.cloudmusic.utils.HttpRequest.USER_AGENT);
if (request instanceof RequestWrapper) {
request = ((RequestWrapper) request).getOriginal();
}
request.setHeader(header);
}
String host = isSecure ? ProxyUtils.SECURE_HOST : ProxyUtils.HOST;
int port = isSecure ? ProxyUtils.SECURE_PORT : ProxyUtils.PORT;
return new HttpRoute(target, null, new HttpHost(host, port), isSecure);
} else {
client.getParams().setParameter(CoreProtocolPNames.USER_AGENT, com.netease.cloudmusic.utils.HttpRequest.USER_AGENT);
return new HttpRoute(target, null, isSecure);
}
}
});
return client;
} catch (Exception e) {
e.printStackTrace();
return new DefaultHttpClient();
}
}
public static DefaultHttpClient createPlaintHttpClient() {
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
PlainSSLSocketFactory socketFactory = new PlainSSLSocketFactory(trustStore);
socketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
BasicHttpParams params = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(params, 30000);
HttpConnectionParams.setSoTimeout(params, 30000);
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", socketFactory, 443));
ThreadSafeClientConnManager ccm = new ThreadSafeClientConnManager(params, registry);
HttpClientParams.setCookiePolicy(params, CookiePolicy.BROWSER_COMPATIBILITY);
final DefaultHttpClient client = new DefaultHttpClient(ccm, params);
client.setRoutePlanner(new HttpRoutePlanner() {
#Override
public HttpRoute determineRoute(HttpHost target, HttpRequest arg1, HttpContext arg2) throws HttpException {
client.getParams().setParameter(CoreProtocolPNames.USER_AGENT, com.netease.cloudmusic.utils.HttpRequest.USER_AGENT);
return new HttpRoute(target, null, "https".equalsIgnoreCase(target.getSchemeName()));
}
});
return client;
} catch (Exception e) {
e.printStackTrace();
return new DefaultHttpClient();
}
}
SolrConfig:
#Configuration
public class SolrConfig {
#Value("${solr.http.url}")
private String solrUrl;
#Value("${solr.http.username}")
private String solrUser;
#Value("${solr.http.password}")
private String solrPassword;
#Value("${solr.http.pool.maxTotal}")
private int poolMaxTotal;
#Value("${solr.http.pool.maxPerRoute}")
private int pollMaxPerRoute;
#Bean
public SolrClient solrClient() {
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(poolMaxTotal);
connectionManager.setDefaultMaxPerRoute(pollMaxPerRoute);
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(solrUser, solrPassword));
CloseableHttpClient httpClient = HttpClientBuilder.create()
.addInterceptorFirst(new PreemptiveAuthInterceptor())
.setConnectionManager(connectionManager)
.setDefaultCredentialsProvider(credentialsProvider)
.build();
return new HttpSolrClient.Builder(solrUrl).withHttpClient(httpClient).build();
}
}
PreemptiveAuthInterceptor:
public class PreemptiveAuthInterceptor implements HttpRequestInterceptor {
public void process(final HttpRequest request, final HttpContext context)
throws HttpException {
AuthState authState = (AuthState) context
.getAttribute(HttpClientContext.TARGET_AUTH_STATE);
// If no auth scheme available yet, try to initialize it
// preemptively
if (authState.getAuthScheme() == null) {
CredentialsProvider credentialsProvider = (CredentialsProvider) context
.getAttribute(HttpClientContext.CREDS_PROVIDER);
HttpHost targetHost = (HttpHost) context
.getAttribute(HttpCoreContext.HTTP_TARGET_HOST);
Credentials credentials = credentialsProvider.getCredentials(new AuthScope(
targetHost.getHostName(), targetHost.getPort()));
if (credentials == null) {
throw new HttpException(
"No credentials for preemptive authentication");
}
authState.update(new BasicScheme(), credentials);
}
}
}