package de.geeksfactory.opacclient.networking;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.ProtocolException;
import org.apache.http.client.CircularRedirectException;
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.HttpHead;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpRequestWrapper;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.utils.URIUtils;
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.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.impl.client.RedirectLocations;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.Args;
import org.apache.http.util.Asserts;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
/**
* Utility to create a new HTTP client.
*/
public class HttpClientFactory {
public String user_agent;
public String ssl_store_path = "../res/raw/ssl_trust_store.bks";
private KeyStore trust_store;
/**
* Initialize a new client factory.
*
* @param user_agent The User-Agent header to be sent.
*/
public HttpClientFactory(String user_agent) {
this.user_agent = user_agent;
}
/**
* Initialize a new client factory with an additional key store to trust for SSL connections.
*
* @param user_agent The User-Agent header to be sent.
* @param ssl_store_path The path to the .bks store
*/
public HttpClientFactory(String user_agent, String ssl_store_path) {
this.user_agent = user_agent;
this.ssl_store_path = ssl_store_path;
}
protected KeyStore getKeyStore()
throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException {
final KeyStore trustStore = KeyStore.getInstance("BKS");
InputStream in;
try {
in = new FileInputStream(ssl_store_path);
} catch (FileNotFoundException e) {
in = new FileInputStream("../opacapp/src/main/res/raw/ssl_trust_store.bks");
}
try {
trustStore.load(in,
"ro5eivoijeeGohsh0daequoo5Zeepaen".toCharArray());
} finally {
in.close();
}
return trustStore;
}
protected Class<?> getSocketFactoryClass(boolean tls_only) {
return null;
}
/**
* Create a new HttpClient.
*
* @param tls_only If this is true, only TLS v1 and newer will be used, SSLv3 will be disabled.
* We highly recommend to set this to true, if possible. This is currently a
* no-op on the default implementation and only used in the Android
* implementation!
*/
public HttpClient getNewApacheHttpClient(boolean customssl, boolean tls_only,
boolean disguise_app) {
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setRedirectStrategy(new CustomRedirectStrategy());
if (disguise_app) {
builder.setUserAgent("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, " +
"like Gecko) Chrome/43.0.2357.130 Safari/537.36\t");
} else {
builder.setUserAgent(user_agent);
}
if (customssl && ssl_store_path != null) {
try {
if (trust_store == null) {
trust_store = getKeyStore();
}
SSLConnectionSocketFactory sf =
AdditionalKeyStoresSSLSocketFactory.create(trust_store,
getSocketFactoryClass(tls_only));
Registry<ConnectionSocketFactory> registry =
RegistryBuilder.<ConnectionSocketFactory>create().register("http",
PlainConnectionSocketFactory.getSocketFactory()).register(
"https", sf).build();
HttpClientConnectionManager ccm = new PoolingHttpClientConnectionManager(registry);
builder.setConnectionManager(ccm);
return builder.build();
} catch (Exception e) {
e.printStackTrace();
return builder.build();
}
} else {
return builder.build();
}
}
public static class CustomRedirectStrategy extends LaxRedirectStrategy {
public CustomRedirectStrategy() {
super();
}
@Override
public HttpUriRequest getRedirect(
final HttpRequest request,
final HttpResponse response,
final HttpContext context) throws ProtocolException {
URI uri = getLocationURI(request, response, context);
final String method = request.getRequestLine().getMethod();
String original_scheme = "http";
String original_host = "";
if (request instanceof HttpRequestWrapper) {
HttpRequest original = ((HttpRequestWrapper) request).getOriginal();
if (original instanceof HttpRequestBase) {
original_scheme = ((HttpRequestBase) original).getURI().getScheme();
original_host = ((HttpRequestBase) original).getURI().getHost();
}
} else if (request instanceof HttpRequestBase) {
if (((HttpRequestBase) request).getURI().getScheme() != null) {
original_scheme = ((HttpRequestBase) request).getURI().getScheme();
original_host = ((HttpRequestBase) request).getURI().getHost();
}
}
// Strict Transport Security for redirects, required for misconfigured webservers like Erlangen
if ("https".equals(original_scheme) && uri.getScheme().equals("http") && uri.getHost().equals(original_host)) {
try {
uri = new URI(uri.toString().replace("http://", "https://"));
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
if (method.equalsIgnoreCase(HttpHead.METHOD_NAME)) {
return new HttpHead(uri);
} else if (method.equalsIgnoreCase(HttpGet.METHOD_NAME)) {
return new HttpGet(uri);
} else {
final int status = response.getStatusLine().getStatusCode();
if (status == HttpStatus.SC_TEMPORARY_REDIRECT) {
return RequestBuilder.copy(request).setUri(uri).build();
} else {
return new HttpGet(uri);
}
}
}
@Override
public URI getLocationURI(final HttpRequest request, final HttpResponse response,
final HttpContext context) throws ProtocolException {
Args.notNull(request, "HTTP request");
Args.notNull(response, "HTTP response");
Args.notNull(context, "HTTP context");
final HttpClientContext clientContext = HttpClientContext.adapt(context);
//get the location header to find out where to redirect to
final Header locationHeader = response.getFirstHeader("location");
if (locationHeader == null) {
// got a redirect response, but no location header
throw new ProtocolException(
"Received redirect response " + response.getStatusLine()
+ " but no location header");
}
final String location = locationHeader.getValue().replaceAll(" ", "%20");
final RequestConfig config = clientContext.getRequestConfig();
URI uri = createLocationURI(location);
// rfc2616 demands the location value be a complete URI
// Location = "Location" ":" absoluteURI
try {
if (!uri.isAbsolute()) {
if (!config.isRelativeRedirectsAllowed()) {
throw new ProtocolException("Relative redirect location '"
+ uri + "' not allowed");
}
// Adjust location URI
final HttpHost target = clientContext.getTargetHost();
Asserts.notNull(target, "Target host");
final URI requestURI = new URI(request.getRequestLine().getUri());
final URI absoluteRequestURI = URIUtils.rewriteURI(requestURI, target, false);
uri = URIUtils.resolve(absoluteRequestURI, uri);
}
} catch (final URISyntaxException ex) {
throw new ProtocolException(ex.getMessage(), ex);
}
RedirectLocations redirectLocations = (RedirectLocations) clientContext.getAttribute(
HttpClientContext.REDIRECT_LOCATIONS);
if (redirectLocations == null) {
redirectLocations = new RedirectLocations();
context.setAttribute(HttpClientContext.REDIRECT_LOCATIONS, redirectLocations);
}
if (!config.isCircularRedirectsAllowed()) {
if (redirectLocations.contains(uri)) {
throw new CircularRedirectException("Circular redirect to '" + uri + "'");
}
}
redirectLocations.add(uri);
return uri;
}
}
}