org.aludratest.service.gui.web.selenium.httpproxy.AuthenticatingHttpProxy.java Source code

Java tutorial

Introduction

Here is the source code for org.aludratest.service.gui.web.selenium.httpproxy.AuthenticatingHttpProxy.java

Source

/*
 * Copyright (C) 2010-2014 Hamburg Sud and the contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.aludratest.service.gui.web.selenium.httpproxy;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;

import org.aludratest.exception.AutomationException;
import org.aludratest.util.DataUtil;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.impl.DefaultBHttpClientConnection;
import org.apache.http.impl.DefaultBHttpServerConnection;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpProcessor;
import org.apache.http.protocol.HttpRequestExecutor;
import org.apache.http.protocol.HttpService;
import org.apache.http.protocol.ImmutableHttpProcessor;
import org.apache.http.protocol.RequestConnControl;
import org.apache.http.protocol.RequestExpectContinue;
import org.apache.http.protocol.ResponseConnControl;
import org.apache.http.protocol.ResponseContent;
import org.apache.http.protocol.ResponseDate;
import org.apache.http.protocol.UriHttpRequestHandlerMapper;
import org.databene.commons.Base64Codec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Opens a server socket at the {@link #localPort}, receives HTTP requests,
 * injects custom HTTP request headers 
 * and forwards the request to the server {@link #realHost}.   
 * @author Volker Bergmann
 */
public class AuthenticatingHttpProxy {

    /** The logger of the class */
    private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticatingHttpProxy.class);

    /** The buffer size for the socket */
    private static final int BUFSIZE = 8 * 1024;

    /** The String-formatted IP address of the host on which the process is running. */
    private String localHost;

    /** The local port on which to listen for incoming connections. */
    private int localPort;

    /** The host to which to forward the incoming requests. */
    private HttpHost realHost;

    /** The custom HTTP header information to inject. */
    private Map<String, String> customHeaders;

    /** A boolean flag for controlling the server's main listener loop. */
    private boolean running;

    private ServerThread serverThread;

    /** Constructor with the central configuration settings. */
    public AuthenticatingHttpProxy(int localPort, String targetHost, int targetPort) {
        try {
            this.localHost = InetAddress.getLocalHost().getHostAddress();
            this.localPort = localPort;
            this.realHost = new HttpHost(targetHost, targetPort);
            this.customHeaders = new HashMap<String, String>();
            this.running = false;
            this.serverThread = null;
        } catch (UnknownHostException e) {
            throw new AutomationException("Error initializing " + getClass(), e);
        }
    }

    public boolean isRunning() {
        return running;
    }

    /** sets an 'Authorization' HTTP request header for BASIC authentication. */
    public void setBasicAuthentication(String user, String password) {
        if (user != null || password != null) {
            String code = Base64Codec.encode((user + ":" + password).getBytes(DataUtil.UTF_8));
            setCustomRequestHeader("Authorization", "Basic " + code);
        }
    }

    /** Sets the name-value pair of a custom HTTP request header to add to forwarded calls.
     *  For each request header to be set, all incoming request headers of the same key 
     *  are deleted, then the new value is set. */
    public void setCustomRequestHeader(String key, String value) {
        customHeaders.put(key, value);
    }

    public String mapTargetToProxyUrl(String requestedUrlString) {
        try {
            URL requestedUrl = new URL(requestedUrlString);
            int requestedPort = requestedUrl.getPort();
            if (requestedPort == -1) {
                requestedPort = 80; // NOSONAR
            }
            if (requestedUrl.getHost().equals(realHost.getHostName()) && requestedPort == realHost.getPort()) {
                URL proxyUrl = new URL(requestedUrl.getProtocol(), localHost, localPort, requestedUrl.getFile());
                return proxyUrl.toString();
            } else {
                return requestedUrlString;
            }
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        }
    }

    public void start() throws IOException {
        serverThread = new ServerThread(localPort, realHost);
        this.running = true;
        serverThread.start();
    }

    public void stop() {
        this.running = false;
        try {
            if (this.serverThread != null) { // this only happens when start() has failed
                this.serverThread.serversocket.close();
                this.serverThread = null;
            }
        } catch (IOException e) {
            LOGGER.error("Error closing server socket. ", e);
        }
    }

    class ServerThread extends Thread {

        private final HttpHost target;
        private final HttpService httpService;
        private final ServerSocket serversocket;

        public ServerThread(int port, HttpHost target) throws IOException {
            this.target = target;
            this.serversocket = new ServerSocket(port);
            this.setDaemon(true);

            HttpRequestInterceptor authenticator = new HttpRequestInterceptor() {
                @Override
                public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
                    for (Map.Entry<String, String> customHeader : customHeaders.entrySet()) {
                        String key = customHeader.getKey();
                        String value = customHeader.getValue();
                        request.removeHeaders(key);
                        if (value != null) {
                            request.addHeader(key, value);
                        }
                    }
                }
            };

            // Set up HTTP protocol processor for incoming connections
            HttpProcessor inhttpproc = new ImmutableHttpProcessor(new HttpRequestInterceptor[] {
                    //new RequestContent(),
                    authenticator, new RequestHeaderOverwriter(HTTP.TARGET_HOST, target.toHostString()),
                    new RequestConnControl(), new RequestExpectContinue(true) });

            // Set up HTTP protocol processor for outgoing connections
            HttpProcessor outhttpproc = new ImmutableHttpProcessor(new HttpResponseInterceptor[] {
                    new ResponseDate(), new ResponseContent(), new ResponseConnControl() });

            // Set up outgoing request executor
            HttpRequestExecutor httpexecutor = new HttpRequestExecutor();

            // Set up incoming request handler
            UriHttpRequestHandlerMapper reqistry = new UriHttpRequestHandlerMapper();
            reqistry.register("*", new ProxyHandler(this.target, outhttpproc, httpexecutor));

            // Set up the HTTP service
            this.httpService = new HttpService(inhttpproc, reqistry);
        }

        @Override
        public void run() {
            LOGGER.info("Listening on " + localHost + ":" + localPort);
            while (running && !Thread.interrupted()) {
                try {
                    int bufsize = BUFSIZE;
                    // Set up incoming HTTP connection
                    Socket insocket = this.serversocket.accept();
                    DefaultBHttpServerConnection inconn = new DefaultBHttpServerConnection(bufsize);
                    LOGGER.debug("Incoming connection from " + insocket.getInetAddress());
                    inconn.bind(insocket);

                    // Set up outgoing HTTP connection
                    Socket outsocket = new Socket(this.target.getHostName(), this.target.getPort());
                    DefaultBHttpClientConnection outconn = new DefaultBHttpClientConnection(bufsize);
                    outconn.bind(outsocket);
                    LOGGER.debug("Outgoing connection to " + outsocket.getInetAddress());

                    // Start worker thread
                    Thread t = new RequestProcessorThread(AuthenticatingHttpProxy.this, this.httpService, inconn,
                            outconn);
                    t.setDaemon(true);
                    t.start();
                } catch (InterruptedIOException e) {
                    break;
                } catch (SocketException e) {
                    if (running) {
                        LOGGER.error("Socket error: " + e.getMessage(), e);
                    }
                    break;
                } catch (IOException e) {
                    LOGGER.error("I/O error initialising connection thread: " + e.getMessage(), e);
                    break;
                }
            }
            if (this.serversocket != null) {
                try {
                    this.serversocket.close();
                } catch (IOException e) {
                    LOGGER.error("Error closing server socket. ", e);
                }
            }
        }
    }

}