package net.redhillsoftware.bonza;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.BindException;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.params.CookiePolicy;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
/**
* Main class for Bonza.
*
*
*/
public final class Bonza extends AbstractHandler {
private BonzaConfig m_config;
private HttpClient m_httpClient = createHttpClient();
public Bonza(BonzaConfig bonzaConfig) throws UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
m_config = bonzaConfig;
}
private HttpClient createHttpClient() throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
HttpClient httpClient = new DefaultHttpClient(new PoolingClientConnectionManager());
HttpClientParams.setRedirecting(httpClient.getParams(), false);
HttpClientParams.setCookiePolicy(httpClient.getParams(), CookiePolicy.IGNORE_COOKIES);
//trust all ssl.
SSLSocketFactory sslsf = new SSLSocketFactory(new TrustStrategy() {
public boolean isTrusted(
final X509Certificate[] chain, String authType) throws java.security.cert.CertificateException {
return true;
}
});
httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 443, sslsf));
return httpClient;
}
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
String requestURI = getPathAndQuery(request);
String mappedRequest = m_config.mapRequest(requestURI);
BonzaConfig.Logger log = m_config.requestStart(request, requestURI, mappedRequest);
if (mappedRequest == null) {
log.logResponse(404);
return404(response);
} else {
doProxyRequest(mappedRequest, request, response);
log.logResponse(response.getStatus());
}
response.flushBuffer();
}
// Everything after the scheme + server
private String getPathAndQuery(HttpServletRequest request) {
String requestURI = request.getRequestURI();
if (request.getQueryString() != null) {
requestURI += "?" + request.getQueryString();
}
return requestURI;
}
private void doProxyRequest(String newRequestURL, HttpServletRequest originalRequest, HttpServletResponse originalResponse) throws IOException {
if (newRequestURL.startsWith("file:")) {
proxyToFile(newRequestURL, originalRequest, originalResponse);
} else {
proxyToHttp(newRequestURL, originalRequest, originalResponse);
}
}
private void proxyToHttp(String newRequestURL, HttpServletRequest originalRequest, HttpServletResponse originalResponse) throws IOException {
HttpRequestBase method = createProxyRequest(newRequestURL, originalRequest);
HttpResponse proxyResponse = m_httpClient.execute(method);
copyProxyResponse(originalRequest, originalResponse, proxyResponse);
}
private void proxyToFile(String newRequestURL, HttpServletRequest originalRequest, HttpServletResponse originalResponse)
throws IOException {
URL url = new URL(newRequestURL);
File f;
try {
f = new File(url.toURI());
} catch (URISyntaxException e) {
f = new File(url.getPath());
}
FileInputStream fis = new FileInputStream(f);
IOUtils.copy(fis, originalResponse.getOutputStream());
}
private HttpRequestBase createProxyRequest(String newRequestURL, HttpServletRequest request) throws IOException {
HttpRequestBase method;
if (request.getMethod().equalsIgnoreCase("get")) {
method = new HttpGet(newRequestURL);
} else if (request.getMethod().equalsIgnoreCase("put")) {
HttpPut putMethod = new HttpPut(newRequestURL);
putMethod.setEntity(new InputStreamEntity(request.getInputStream(), request.getContentLength()));
method = putMethod;
} else {
HttpPost postMethod = new HttpPost(newRequestURL);
postMethod.setEntity(new InputStreamEntity(request.getInputStream(), request.getContentLength()));
method = postMethod;
}
copyHeaders(request, method);
return method;
}
private void copyHeaders(HttpServletRequest request, HttpRequestBase method) {
for (String headerName : Collections.list(request.getHeaderNames())) {
for (String headerValue : Collections.list(request.getHeaders(headerName))) {
if (!"Connection".equals(headerName) && !"Content-Length".equals(headerName)) {
method.setHeader(headerName, headerValue);
}
}
}
}
private void copyProxyResponse(HttpServletRequest originalRequest,
HttpServletResponse originalResponse,
HttpResponse proxyResponse) throws IOException {
for (Header header : proxyResponse.getAllHeaders()) {
String headerName = header.getName();
String headerValue = header.getValue();
if (headerName.equals("Location")) {
headerValue = m_config.reverseMap(headerValue, originalRequest);
}
if (headerName.equals("Set-Cookie")) {
headerValue = crunchCookie(originalRequest, headerValue);
}
originalResponse.addHeader(headerName, headerValue);
}
int statusCode = proxyResponse.getStatusLine().getStatusCode();
originalResponse.setStatus(statusCode);
HttpEntity entity = proxyResponse.getEntity();
if (entity != null) {
entity.writeTo(originalResponse.getOutputStream());
}
}
private String crunchCookie(HttpServletRequest originaRequest, String headerValue) {
if (originaRequest.getRequestURL().toString().startsWith("https")) {
return headerValue;
}
List<String> parts = new ArrayList<String>();
for (String part : Arrays.asList(headerValue.split("; "))) {
if (!part.equals("Secure")) {
parts.add(part);
}
}
return StringUtils.join(parts, "; ");
}
/**
* Return a 404 if we can't map anything.
*/
private void return404(HttpServletResponse response) throws IOException {
response.setStatus(404);
response.getWriter().append("You've got buckley's chance of getting anything on this URL!");
}
public static void main(String[] args) throws Exception {
BonzaConfig config = BonzaConfig.parseOptions(args);
org.eclipse.jetty.util.log.Log.setLog(null);
Server server = new Server();
try {
SelectChannelConnector connector = new SelectChannelConnector();
connector.setReuseAddress(false);
connector.setPort(config.getPort());
server.setConnectors(new Connector[]{connector});
server.setHandler(new Bonza(config));
server.start();
out("You beauty!! Lets go.");
out("\n" + config.toString());
server.join();
} catch (BindException e) {
System.exit(1);
}
}
public static void out(String msg) {
System.out.println(msg);
}
public static void err(String emsg) {
System.err.println(emsg);
}
}