3

I'm really trying to better understand how a CONNECT HTTP request process is handled. I'm stuck at this point in my HttpServer that I'm building and was hoping others can help give me incite on how I should approach these next challenges. A little info on my code thus far . I have a class HTTPServer listening on a socket on port 8080 (initially its a non SSL socket). I have a a class called DefaultHttpRequestHandler that holds an instance of HTTPClient that handles all requests needing to be made by the server and a worker thread inside HttpServer handles dispatching all requests sent by browser to port 8080.

My question is the following:

  1. When the CONNECT request comes in and is sent to DefaultHttpRequestHandler it gets passed to the handle(HttpRequest request, HttpResponse response,HttpContext context) method. At this point I peek at the request and if I see it is a CONNECT what next? I was thinking I then establish the SSL socket connection on port 8080 which was before a normal socket? or do I always hold two sockets one as a standard socket and one as ssl than switch to the ssl one. This part is really frustrating me very confused how to code this sucker!

DefaultHttpServer.java - the server

 public class DefaultHttpServer {

public static void main(String[] args) throws Exception {

    Thread t = new RequestListenerThread(8080);
    t.setDaemon(false);
    t.start();

    //send a request to proxy server for testing
    testSendReqFromClient() ;
}

public static void testSendReqFromClient() throws Exception
{

    SSLContext sslCtx = SSLContext.getInstance("TLS");

    //  sslCtx.init(null,new TrustManager[] { new EasyX509TrustManager() }, null);


        sslCtx.init(null, new TrustManager[] { new X509TrustManager() {
                    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            System.out.println("getAcceptedIssuers =============");
                            return null;
                    }

                    public void checkClientTrusted(X509Certificate[] certs,
                                    String authType) {
                            System.out.println("checkClientTrusted =============");
                    }

                    public void checkServerTrusted(X509Certificate[] certs,
                                    String authType) {
                            System.out.println("checkServerTrusted =============");
                    }

                    @Override
                    public void checkClientTrusted(
                            java.security.cert.X509Certificate[] arg0,
                            String arg1) throws CertificateException {
                        // TODO Auto-generated method stub

                    }

                    @Override
                    public void checkServerTrusted(
                            java.security.cert.X509Certificate[] arg0,
                            String arg1) throws CertificateException {
                        // TODO Auto-generated method stub

                    }
        } }, new SecureRandom());

    Thread.sleep(5000);
    SSLSocketFactory sf = new SSLSocketFactory(sslCtx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
    Scheme https = new Scheme("https", 443, sf);
    SchemeRegistry schemeRegistry = new SchemeRegistry();
    schemeRegistry.register(https);
    Scheme http = new Scheme("http", 80, PlainSocketFactory.getSocketFactory());
    schemeRegistry.register(http);
    BasicHttpRequest req = new BasicHttpRequest("GET","https://www.yahoo.com");
    ThreadSafeClientConnManager tm = new ThreadSafeClientConnManager(schemeRegistry);
    HttpClient httpClient = new DefaultHttpClient(tm);
    ConnRouteParams.setDefaultProxy(req.getParams(), new HttpHost("localhost",8080,"http"));
    httpClient.execute(new RequestWrapper(req));
}

 }

DefaultRequestHandler.java - the client that sends requests to server from my proxy server

 public class DefaultHttpRequestHandler implements HttpRequestHandler {

private static String sslType = "TLS";
private HttpClient httpClient = null;
private ThreadSafeClientConnManager tm;
public DefaultHttpRequestHandler() {
    super();
    init();

}

private void init() {
    try {
        SSLContext sslCtx = SSLContext.getInstance(sslType);

    //  sslCtx.init(null,new TrustManager[] { new EasyX509TrustManager() }, null);


        sslCtx.init(null, new TrustManager[] { new X509TrustManager() {
                    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            System.out.println("getAcceptedIssuers =============");
                            return null;
                    }

                    public void checkClientTrusted(X509Certificate[] certs,
                                    String authType) {
                            System.out.println("checkClientTrusted =============");
                    }

                    public void checkServerTrusted(X509Certificate[] certs,
                                    String authType) {
                            System.out.println("checkServerTrusted =============");
                    }

                    @Override
                    public void checkClientTrusted(
                            java.security.cert.X509Certificate[] arg0,
                            String arg1) throws CertificateException {
                        // TODO Auto-generated method stub

                    }

                    @Override
                    public void checkServerTrusted(
                            java.security.cert.X509Certificate[] arg0,
                            String arg1) throws CertificateException {
                        // TODO Auto-generated method stub

                    }
        } }, new SecureRandom());

    SSLSocketFactory sf = new SSLSocketFactory(sslCtx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);


        Scheme https = new Scheme("https", 443, sf);
        SchemeRegistry schemeRegistry = new SchemeRegistry();
        schemeRegistry.register(https);

        Scheme http = new Scheme("http", 80, PlainSocketFactory.getSocketFactory());
        schemeRegistry.register(http);

        tm = new ThreadSafeClientConnManager(schemeRegistry);
        //httpClient = new  ContentEncodingHttpClient(tm);
        httpClient = new DefaultHttpClient(tm);
        httpClient.getParams().setParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, true); 
        //httpClient.getConnectionManager().getSchemeRegistry()         .register(https);

    } catch (Exception e) {
        System.err.println(e.getMessage());
        e.printStackTrace();
    }

}

public void handle(HttpRequest request, HttpResponse response,
        HttpContext context) throws HttpException, IOException {

    System.out.println(request);
    RequestLine reqLine = request.getRequestLine();
    if(reqLine.getMethod().equalsIgnoreCase("CONNECT"))
    {

        response.setEntity(new BufferedHttpEntity(new StringEntity("HTTP/1.0 200 Connection established\r\nProxy-agent: proxy client\r\n\r\n")));
        //do i switch the socket to sslsocketconnection in defaulthttpserver here?
    }
    else
    {
        try {

            HttpResponse clientResponse = null;

            HttpEntity entity = null;

            clientResponse = httpClient.execute(new RequestWrapper(request));

            entity = clientResponse.getEntity();

            if (entity != null) {
                response.setEntity(new BufferedHttpEntity(entity));
            }
        } catch (Exception e) {
            System.err.println(e.getMessage());
            e.printStackTrace();
        } 

    }

}

}

RequestListenerThread - This is run method inside my httpserver that handles dispatching requests

 class RequestListenerThread extends Thread {

    private static ServerSocket sslServersocket = null;
    private static ServerSocket serversocket = null;
     static ServerSocketFactory ssocketFactory  = null;
    private final HttpParams params;
    private final HttpService httpService;
    Selector selector ;

    public RequestListenerThread(int port) throws Exception {


        KeyStore ks = KeyStore.getInstance("JKS");  
        ks.load(new FileInputStream("privateKey2.store"), "whitehatsec123".toCharArray());

        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
        kmf.init(ks, "whitehatsec123".toCharArray());

        SSLContext context = SSLContext.getInstance("TLS");
        context.init(kmf.getKeyManagers(), null, null);


        ssocketFactory = context.getServerSocketFactory();
        //serversocket =  ssocketFactory.createServerSocket(port);
        serversocket = new ServerSocket(port);
        this.params = new SyncBasicHttpParams();
        this.params.setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, true).setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 50000)
                .setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE,
                        8 * 1024)
                .setBooleanParameter(
                        CoreConnectionPNames.STALE_CONNECTION_CHECK, false)
                .setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
                .setParameter(CoreProtocolPNames.ORIGIN_SERVER,
                        "HttpComponents/1.1");
        // Set up the HTTP protocol processor
        HttpProcessor httpproc = new ImmutableHttpProcessor(
                new HttpResponseInterceptor[] { new ResponseDate(),
                        new ResponseServer(), new ResponseContent(),
                        new ResponseConnControl() });

        // Set up request handlers
        HttpRequestHandlerRegistry reqistry = new HttpRequestHandlerRegistry();
        reqistry.register("*", new DefaultHttpRequestHandler());

        // Set up the HTTP service
        this.httpService = new HttpService(httpproc,
                new DefaultConnectionReuseStrategy(),
                new DefaultHttpResponseFactory(), reqistry, this.params);
    }


    public void run() 
    {
        System.out.println("Listening on port "
                + serversocket.getLocalPort());
        while (!Thread.interrupted()) 
        {
            try 
            {
                // Set up HTTP connection
                Socket socket = serversocket.accept();
                DefaultHttpServerConnection conn = new DefaultHttpServerConnection();
                System.out.println("Incoming connection from "
                        + socket.getInetAddress());
                conn.bind(socket, this.params);

                // Start worker thread
                Thread t = new WorkerThread(this.httpService, conn);
                t.setDaemon(true);
                t.start();
            } catch (InterruptedIOException ex) {
                break;
            } catch (IOException ex) {
                System.err
                        .println("I/O error initialising connection thread: "
                                + ex.getMessage());
                ex.printStackTrace();
                break;
            }
        }
    }


}




class WorkerThread extends Thread {

    private final HttpService httpservice;
    private final HttpServerConnection conn;

    public WorkerThread(final HttpService httpservice,
            final HttpServerConnection conn) {
        super();
        this.httpservice = httpservice;
        this.conn = conn;
    }

    public void run() {
        System.out.println("New connection thread");
        HttpContext context = new BasicHttpContext(null);

        try {
            while (!Thread.interrupted() && this.conn.isOpen()) {
                this.httpservice.handleRequest(this.conn, context);

            }
        } catch (ConnectionClosedException ex) {
            System.err.println("Client closed connection");
        } catch (IOException ex) {
            System.err.println("I/O error: " + ex.getMessage());
            ex.printStackTrace();
        } catch (HttpException ex) {
            System.err.println("Unrecoverable HTTP protocol violation: "
                    + ex.getMessage());
        } finally {
            try {
                this.conn.shutdown();
            } catch (IOException ignore) {
            }
        }
    }

}
Rsh
  • 7,214
  • 5
  • 36
  • 45
steve
  • 297
  • 1
  • 5
  • 17

1 Answers1

4

A proxy receiving a CONNECT request (and accepting it) doesn't do any SSL/TLS initialisation or processing (if it did, it would be a potential MITM attacker). It merely relays all the traffic between the target HTTPS host and the initial client back and forth.

More detailed in these answers perhaps:

What you would need is to be able to get hold of the underlying socket (or input/output streams) and write every byte you read on the other side.

Community
  • 1
  • 1
Bruno
  • 119,590
  • 31
  • 270
  • 376
  • That info does help thank you.However, it does not answer my main question regarding how you handle the SSL portion. From those links you provided it looks as if the the proxy server can use a standard socket to listen for incoming http requests from the browser. If that is the case why would there exist a serversocket and sslserversocket. I was assuming the proxy server would be a default socket connection than after you see a CONNECT switch to sslsocket but that was not working because any plain text coming through by browser was then throwing an exception. Why is this so freaking confusing! – steve Nov 18 '11 at 01:59
  • No, you may have misunderstood, but the proxy does absolutely nothing SSL-related. It doesn't switch to an SSLSocket at all. It merely opens a TCP connection to the target HTTPS server, writes to it what it reads from the initial client, and writes back to the client what it reads from it. The SSLServerSocket is not used at all by proxy servers (unless the connection between the browser and the proxy is itself done over SSL, but this is quite unusual). – Bruno Nov 18 '11 at 10:31
  • The difficulty you may have is that I'm not sure whether you can get to the initial client socket from the `HttpRequest` instance you get in your handler. You need its I/O streams to be able to tunnel them to the target HTTPS server. Since once you're using `CONNECT`, you're not longer really using HTTP (between browser and proxy), preparing an `HttpResponse` instance doesn't really make sense. – Bruno Nov 18 '11 at 10:44