/*
* Copyright (C) 2012-2016 Julien Bonjean <julien@bonjean.info>
*
* This file is part of Beluga Player.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package info.bonjean.beluga.connection;
import info.bonjean.beluga.configuration.BelugaConfiguration;
import info.bonjean.beluga.configuration.DNSProxy;
import info.bonjean.beluga.exception.CommunicationException;
import java.io.IOException;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpInetConnection;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
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.HttpClientBuilder;
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.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author Julien Bonjean <julien@bonjean.info>
*
*/
public class BelugaHTTPClient
{
private static final Logger log = LoggerFactory.getLogger(BelugaHTTPClient.class);
private static final int TIMEOUT = 4000;
private static BelugaHTTPClient instance;
private HttpClient httpClient;
private PoolingHttpClientConnectionManager connectionManager;
private BelugaHTTPClient()
{
BelugaConfiguration configuration = BelugaConfiguration.getInstance();
HttpClientBuilder clientBuilder = HttpClients.custom();
// timeout
RequestConfig config = RequestConfig.custom().setConnectTimeout(TIMEOUT)
.setSocketTimeout(TIMEOUT).setConnectionRequestTimeout(TIMEOUT).build();
clientBuilder.setDefaultRequestConfig(config);
switch (configuration.getConnectionType())
{
case DIRECT:
connectionManager = new PoolingHttpClientConnectionManager();
break;
case HTTP_PROXY:
HttpHost proxy = new HttpHost(configuration.getProxyHost(),
configuration.getProxyPort(), "http");
clientBuilder.setProxy(proxy);
default:
Registry<ConnectionSocketFactory> registry = RegistryBuilder
.<ConnectionSocketFactory> create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory()).build();
BelugaDNSResolver dnsOverrider = new BelugaDNSResolver(DNSProxy.get(configuration
.getConnectionType().getId()));
connectionManager = new PoolingHttpClientConnectionManager(registry, dnsOverrider);
break;
}
// limit the pool size
connectionManager.setDefaultMaxPerRoute(2);
// add interceptor, currently for debugging only
clientBuilder.addInterceptorFirst(new HttpResponseInterceptor()
{
@Override
public void process(HttpResponse response, HttpContext context) throws HttpException,
IOException
{
HttpInetConnection connection = (HttpInetConnection) context
.getAttribute(HttpCoreContext.HTTP_CONNECTION);
log.debug("Remote address: " + connection.getRemoteAddress());
// TODO: reimplement blacklisting for DNS proxy by maintaining a
// map [DNS IP,RESOLVED IP] in the DNS resolver for reverse
// lookup
}
});
// finally create the HTTP client
clientBuilder.setConnectionManager(connectionManager);
httpClient = clientBuilder.build();
}
public static BelugaHTTPClient getInstance()
{
if (instance == null)
instance = new BelugaHTTPClient();
return instance;
}
public static void reset()
{
if (instance != null)
{
instance.connectionManager.close();
instance = null;
}
}
public String requestPost(HttpPost post) throws CommunicationException,
ClientProtocolException, IOException
{
HttpResponse httpResponse = httpClient.execute(post);
String result = IOUtils.toString(httpResponse.getEntity().getContent());
EntityUtils.consume(httpResponse.getEntity());
return result;
}
public byte[] requestGet(HttpGet get) throws ClientProtocolException, IOException
{
HttpResponse httpResponse = httpClient.execute(get);
byte[] result = IOUtils.toByteArray(httpResponse.getEntity().getContent());
EntityUtils.consume(httpResponse.getEntity());
return result;
}
public HttpResponse requestGetStream(HttpGet get) throws ClientProtocolException, IOException
{
// TODO: use a different client with BasicHttpClientConnectionManager
// for the streaming connection
// no socket timeout, this may improve pause support
// TODO: check if it is working as expected (also depends of the server)
RequestConfig config = RequestConfig.custom().setConnectTimeout(TIMEOUT)
.setConnectionRequestTimeout(TIMEOUT).build();
get.setConfig(config);
HttpResponse httpResponse = httpClient.execute(get);
if (httpResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK)
throw new IOException("Server reply: " + httpResponse.getStatusLine().getReasonPhrase());
return httpResponse;
}
}