Apache httpclient makes lots of connections - java

I'm using apache httpclient 4.0 to connect to a video stream (motion jpeg) over http. Here's my code:
DefaultHttpClient client;
HttpParams params = new BasicHttpParams();
List<String> authpref = new ArrayList<String>();
authpref.add(AuthPolicy.DIGEST);
authpref.add(AuthPolicy.BASIC);
params.setParameter("http.auth.proxy-scheme-pref", authpref);
params.setParameter("http.protocol.handle-authentication", Boolean.TRUE);
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(
new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
ClientConnectionManager connectionManager =
new ThreadSafeClientConnManager(params, schemeRegistry);
client = new DefaultHttpClient(connectionManager, params);
client.getCredentialsProvider().setCredentials(
new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT),
new UsernamePasswordCredentials(username, password));
HttpResponse videoResponse = client.execute(new HttpGet(url));
The problem is that the client.execute() line seems to make hundreds of connections to the video stream. I can see this by logging onto the server and doing a netstat: there's an enourmous number of connections to port 80 and they're all stuck in the TIME_WAIT state.
Am I doing something wrong here? What's going on?
Thanks for the help.

That's what happens if you do not release connections back to the pool and / or create a new connection pool for each and every request.
http://hc.apache.org/httpcomponents-client-4.0.1/tutorial/html/connmgmt.html

Related

REST webservice call for HTTPS link - 400 Bad Request error

Using SOAP UI, I am able to successfully invoke the REST GET call with basic authentication
GET https://app.test.com/testing/rest/authentication-point/authentication HTTP/1.1
Accept-Encoding: gzip,deflate
Authorization: Basic aPOjYVclOmIzABFhZjVpJES=
Host: app.test.com
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
I received response code as 200.
Similar request, When I tried to invoke via java client, it is giving 400 status code.
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("User", "Password"));
final HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
HttpClient client = HttpClientBuilder.create().build();
HttpResponse response = null;
response = client.execute(
new HttpGet("https://app.test.com/testing/rest/authentication-point/authentication"),
context);
int statusCode = response.getStatusLine().getStatusCode();
This code worked properly when the host was HTTP. Recently a VIP is added and made as HTTPS, after which it is not working. Please suggest a fix for this.
You need to use ssl with http client:
TrustStrategy acceptingTrustStrategy = (cert, authType) -> true;
SSLSocketFactory sf = new SSLSocketFactory(
acceptingTrustStrategy, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("https", 8443, sf));
ClientConnectionManager ccm = new PoolingClientConnectionManager(registry);
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("User", "Password"));
final HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
DefaultHttpClient httpClient = new DefaultHttpClient(ccm);
HttpGet getMethod = new HttpGet(new HttpGet("https://app.test.com/testing/rest/authentication-point/authentication"),context);
HttpResponse response = httpClient.execute(getMethod);
int statusCode = response.getStatusLine().getStatusCode();
After trying out so many options, below code worked for me.
HttpHost targetHost = new HttpHost("app.test.com", 443, "https");
AuthCache authCache = new BasicAuthCache();
authCache.put(targetHost, new BasicScheme());
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("User", "Password"));
final HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
context.setAuthCache(authCache);
HttpClient client = HttpClientBuilder.create().build();
HttpResponse response = null;
response = client.execute(
new HttpGet("https://app.test.com/testing/rest/authentication-point/authentication"),
context);
After defining a HTTP host with scheme as https, and setting them to context using AuthCache, the call went through successfully.

http-post through proxy not working

I can get to https://pushover.net/ using chrome browser, but when i try to get to the same website using java to connect to the api and send data it fails.
this is my code
HttpClient httpClient = (HttpClient) HttpClientBuilder.create()/*.setProxy(proxy)*/.build();
HttpPost post = new HttpPost("https://api.pushover.net/1/messages.json");
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
nvps.add(new BasicNameValuePair("token", apiToken));
nvps.add(new BasicNameValuePair("user", userKey));
nvps.add(new BasicNameValuePair("message", message));
post.setEntity(new UrlEncodedFormEntity(nvps, Charset.defaultCharset()));
//point A
HttpResponse deviceResponse = httpClient.execute(post);
//point B
it gets to point A, but then takes ages to get to point B and it gives an exception when it does.
The Exception is
org.apache.http.conn.HttpHostConnectException: Connect to api.pushover.net:443 (api.pushover.net/108.59.13.232] failed: Connection timer out: connect.
I have tried using a proxy with the below code above the rest
HttpHost proxy = new HttpHost("url",9090,"https");
httpClient = (HttpClient) HttpClientBuilder.create().setProxy(proxy).build();
This gives me a
javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
I then also tried adding
Authenticator.setDefault(
new Authenticator() {
#Override
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(
authUser, authPassword.toCharArray());
}
}
);
System.setProperty("http.proxyUser", authUser);
System.setProperty("http.proxyPassword", authPassword);
but it gives the same SSLHandshakeException.
I have seen things on creating keystores, but that will only work on my machine, I want something that will work in code and allow me to deploy this app to 1000's of machines, without having to do extra manual config to each.
Is there anything Better i should be doing?
I have fixed it with this code in place of the httpClient at the beginning.
HttpHost proxy = new HttpHost(PROXY_URL, 8080);
DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);
AuthCache authCache = new BasicAuthCache();
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope(PROXY_URL, 8080, AuthScope.ANY_HOST, "ntlm"), new NTCredentials(authUser, authPassword, "",PROXY_DOMAIN));
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
context.setAuthCache(authCache);
httpClient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).setRoutePlanner(routePlanner).build();

Securing a connection checking SSL Certificate?

I'm doing an internships.
Here they give me a code of an Android application that have been revisione by an agency for the code security and told me to change some points present in a document.
Now they fear for the information leakage because the application doesn't check for the SSL certificates when connecting to the bank server, risking a "Man In The Middle" attack.
Is there any class that i can use to check expirency date of the cert. or if it is trusted?
An example of a http connection in the app:
trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
SSLSocketFactory sf = new CustomSSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
HttpParams bhttpparams = new BasicHttpParams();
HttpProtocolParams.setVersion(bhttpparams, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(bhttpparams, "utf-8");
bhttpparams.setBooleanParameter("http.protocol.expect-continue", false);
HttpConnectionParams.setConnectionTimeout(bhttpparams, 20000);
HttpConnectionParams.setSoTimeout(bhttpparams, 200000);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", sf, 443));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(bhttpparams, registry);
client = new DefaultHttpClient(ccm, bhttpparams);
client.getCredentialsProvider().setCredentials(new AuthScope(null, -1), new UsernamePasswordCredentials("", ""));
HttpResponse response = client.execute(urlws);
in = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8"), 8);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
The vulnerability is here. Remove it.
But I would question both the competence and the sanity of anyone who gave this task to an uninformed intern without a proper briefing, and you can tell them I said so. I would also be asking some severe questions about how this line of code ever got there in the first peace, and about the inadequacy of the testing that allowed it to remain.

Apache HTTPClient does not make more than 2 connections

I've been trying to implement connection pooling for my application using Apache HTTPClient (v4.1). The problem is that the client always makes only two connections when run, though there are enough threads running parallel. I have been trying to modify the code for a while now, but nothing has helped yet.
I'm using ThreadSafeClientConnManager for connection pooling and set the MaxTotal and DefaulMaxPerRoute to values I want.
Is there anything that comes to your mind first that I might want to check?
Here's that code segment that I use to create the client.
DefaultHttpClient createClient() {
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, new Integer(60000));
params.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, new Integer(60000));
params.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("https", sf, 6443));
registry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(params, registry);
cm.setMaxTotal(2 * maxConnections);
cm.setDefaultMaxPerRoute(maxConnections);
HttpHost localhost = new HttpHost("localhost");
cm.setMaxForRoute(new HttpRoute(localhost), maxConnections);
HttpHost sdpTargetHost = new HttpHost("webserviceIP", webservicePort, "https");
cm.setMaxForRoute(new HttpRoute(sdpTargetHost, null, true), maxConnections);
return new DefaultHttpClient(cm, params);
}
The client returned by this function is used in Runnables managed by a ThreadPoolExecutor. The Runnables use the client, and has these lines:
HttpResponse response = httpClient.execute(httpPost, context);
HttpEntity entity = response.getEntity();
....
EntityUtils.consume(entity);
From what I know, the EntityUtils.consume(entity) will notify the connection manager that the connection is no longer used, and thus will release the connection to be used by other threads. So I'm guessing the connection management is alright.
I guess I've provided enough info, please tell me if I'm to add anything more.
Thanks
OK. I've found the solution, thanks to oleg for pointing out the logging, and to google and all the forums. All I had to do was define the class with only the connection manager, and then set HttpParams using HttpClient.setParams(). So the code will look something like this:
DefaultHttpClient createClient() {
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("https", sf, 6443));
ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(registry);
cm.setMaxTotal(maxConnections);
cm.setDefaultMaxPerRoute(maxConnections);
HttpHost targetHost = new HttpHost("webserviceIP", webservicePort, "https");
cm.setMaxForRoute(new HttpRoute(targetHost, null, true), maxConnections);
return new DefaultHttpClient(cm);
}
And right before using the client,
DefaultHttpClient httpClient = createClient();
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, new Integer(60000));
params.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, new Integer(60000));
params.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true);
httpClient.setParams(params);
There apparently is no difference in the code logically, but this fixed my problem. I presume this probably is some sorta bug in the HttpClient 4.1 API.
I was not able to set this option in cm
cm.setDefaultMaxPerRoute(maxConnections);
It was necessary to do so:
ConnPerRoute perRoute = new ConnPerRouteBean(100);
ConnManagerParams.setMaxConnectionsPerRoute(params, perRoute);
ConnManagerParams.setMaxTotalConnections(params, 100);
ConnManagerParams.setTimeout(params, 15000);

Http authentication with apache httpcomponents

I am trying to develop a java http client with apache httpcomponents 4.0.1. This client calls the page "https://myHost/myPage". This page is protected on the server by a JNDIRealm with a login form authentication, so when I try to get https://myHost/myPage I get a login page. I tried to bypass it unsuccessfully with the following code :
//I set my proxy
HttpHost proxy = new HttpHost("myProxyHost", myProxyPort);
//I add supported schemes
SchemeRegistry supportedSchemes = new SchemeRegistry();
supportedSchemes.register(new Scheme("http", PlainSocketFactory
.getSocketFactory(), 80));
supportedSchemes.register(new Scheme("https", SSLSocketFactory
.getSocketFactory(), 443));
// prepare parameters
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, "UTF-8");
HttpProtocolParams.setUseExpectContinue(params, true);
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params,
supportedSchemes);
DefaultHttpClient httpclient = new DefaultHttpClient(ccm, params);
httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,
proxy);
//I add my authentication information
httpclient.getCredentialsProvider().setCredentials(
new AuthScope("myHost/myPage", 443),
new UsernamePasswordCredentials("username", "password"));
HttpHost host = new HttpHost("myHost", 443, "https");
HttpGet req = new HttpGet("/myPage");
//show the page
ResponseHandler<String> responseHandler = new BasicResponseHandler();
String rsp = httpClient.execute(host, req, responseHandler);
System.out.println(rsp);
When I run this code, I always get the login page, not myPage. How can I apply my credential parameters to avoid this login form?
Any help would be fantastic
HttpClient doesn't support form login. What you are trying to do is Basic Auth, which does't work with form login.
You can simply trace the form post for login page and send the POST request from HttpClient.

Categories