package com.camptocamp.owsproxy; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.MalformedURLException; import java.security.UnrecoverableKeyException; import java.util.logging.Level; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.httpclient.Credentials; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.UsernamePasswordCredentials; import org.apache.commons.httpclient.auth.AuthScope; import org.apache.commons.httpclient.contrib.ssl.AuthSSLInitializationError; import org.apache.commons.httpclient.contrib.ssl.AuthSSLProtocolSocketFactory; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.protocol.Protocol; import com.camptocamp.owsproxy.ConnectionEvent.ConnectionStatus; import com.camptocamp.owsproxy.logging.OWSLogger; import com.camptocamp.owsproxy.parameters.ConnectionParameters; public class OWSProxyServlet extends HttpServlet { private static final long serialVersionUID = 1L; private ErrorReporter reporter; private boolean firstTime = true; private String listenURL; private ConnectionParameters connectionParams; public OWSProxyServlet(ErrorReporter reporter, ConnectionParameters connectionParams) { this.reporter = reporter; this.connectionParams = connectionParams; } public void setListenURL(String URL) { this.listenURL = URL; } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { OWSLogger.DEV.fine("server handling: " + Thread.currentThread().getName()); //$NON-NLS-1$ String serviceEndPoint = connectionParams.server; String queryString = request.getQueryString(); try { configureSSH(); HttpClient client = new HttpClient(); configureProxy(client); if (queryString != null) { // TODO: only send for GetCapabilities if (this.listenURL != null) { queryString += "&" + "VENDOR_ONLINE_RESOURCE=" + this.listenURL; //$NON-NLS-1$ //$NON-NLS-2$ } serviceEndPoint += "?" + queryString; //$NON-NLS-1$ } OWSLogger.DEV.info("Request: " + serviceEndPoint); //$NON-NLS-1$ HttpMethod method = new GetMethod(serviceEndPoint); // Authentication if (connectionParams.username != null && connectionParams.password != null) { client.getParams().setAuthenticationPreemptive(true); Credentials defaultcreds = new UsernamePasswordCredentials(connectionParams.username, connectionParams.password); client.getState().setCredentials(AuthScope.ANY, defaultcreds); } int statusCode = client.executeMethod(method); String header = "Content-Type"; //$NON-NLS-1$ Header contentTypeHeader = method.getResponseHeader(header); if (contentTypeHeader != null) { response.setHeader(header, contentTypeHeader.getValue()); } Header[] headers = method.getResponseHeaders(); for (Header h : headers) { // XXX override some headers? if("Transfer-Encoding".equalsIgnoreCase(h.getName()) && "chunked".equalsIgnoreCase(h.getValue())) continue; if (!ieBugCausingHeader(h)) { response.setHeader(h.getName(), h.getValue()); } } efficientWrite(response, method); if (statusCode != HttpStatus.SC_OK) { handleError(method, statusCode, method.getResponseBody()); return; } if (firstTime) { firstTime = false; reporter.connected(); } } catch (NoKeystoreException e) { reporter.reportError(ConnectionStatus.NO_KEYSTORE, e.getMessage()); } catch (AuthSSLInitializationError e) { if( e.getCause() instanceof UnrecoverableKeyException) { reporter.reportError(ConnectionStatus.KEYSTORE_PASSWORD, e.getLocalizedMessage()); }else { reporter.reportError(ConnectionStatus.ERROR, e.getLocalizedMessage()); } }catch (Throwable e) { reporter.reportError(ConnectionStatus.ERROR, e.getLocalizedMessage()); } } private void efficientWrite(HttpServletResponse response, HttpMethod method) throws IOException { byte[] cache = new byte[response.getBufferSize()]; InputStream in = method.getResponseBodyAsStream(); ServletOutputStream out = response.getOutputStream(); try { for (int read = in.read(cache); read > 0; read = in.read(cache)) { out.write(cache, 0, read); } } finally { try { in.close(); }finally { out.close(); } } } private void configureSSH() throws MalformedURLException { File keystore = new File(connectionParams.keystore); if( connectionParams.readonlyKeystore && !keystore.exists() ) { throw new NoKeystoreException("Keystore: "+keystore+" does not exist"); //$NON-NLS-1$ //$NON-NLS-2$ } if (!keystore.exists() && !keystore.getAbsolutePath().equals(OWSClient.DEFAULT_SECURITY_SETTINGS.keystore)) { reporter.keystoreMissing(keystore); } AuthSSLProtocolSocketFactory socketFactory; socketFactory = new AuthSSLProtocolSocketFactory(keystore, connectionParams.keystorePass, connectionParams.readonlyKeystore, reporter, connectionParams.sessionCertificates ); Protocol.registerProtocol("https", new Protocol("https", socketFactory, 443)); //$NON-NLS-1$ //$NON-NLS-2$ } private boolean ieBugCausingHeader(Header h) { if (h.getName().equals("Cache-Control")) { //$NON-NLS-1$ return h.getValue().equalsIgnoreCase("no-store") || h.getValue().equalsIgnoreCase("no-cache"); //$NON-NLS-1$ //$NON-NLS-2$ } return false; } private void handleError(HttpMethod method, int statusCode, byte[] responseBody) throws IOException { System.err.println("Method failed: " + method.getStatusLine()); //$NON-NLS-1$ write(responseBody); switch (statusCode) { case HttpStatus.SC_UNAUTHORIZED: reporter.reportError(ConnectionStatus.UNAUTHORIZED, "Unauthorized: " + method.getStatusLine()); //$NON-NLS-1$ break; case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED: reporter.reportError(ConnectionStatus.PROXY_AUTH_REQUIRED, "Proxy Authentication Failed: " //$NON-NLS-1$ + method.getStatusLine()); break; default: reporter.reportError(ConnectionStatus.ERROR, "Method failed: " + method.getStatusLine()); //$NON-NLS-1$ break; } } private void write(byte[] responseBody) throws IOException { if (OWSLogger.DEV.isLoggable(Level.FINEST)) { InputStreamReader inputStreamReader = new InputStreamReader(new ByteArrayInputStream(responseBody)); int c = inputStreamReader.read(); StringBuilder builder = new StringBuilder(); while (c != -1) { builder.append(c); c = inputStreamReader.read(); } OWSLogger.DEV.finest("Response from server for the error is: \n\n" + builder); //$NON-NLS-1$ } } private void configureProxy(HttpClient client) { if (connectionParams.proxyHost != null) { client.getHostConfiguration().setProxy(connectionParams.proxyHost, connectionParams.proxyPort); Credentials defaultcreds = new UsernamePasswordCredentials(connectionParams.proxyUsername, connectionParams.proxyPassword); client.getState().setProxyCredentials(AuthScope.ANY, defaultcreds); } } }