【问题标题】:Trying to handle a HTTP CONNECT request from browser in java试图在java中处理来自浏览器的HTTP CONNECT请求
【发布时间】:2011-12-30 00:40:27
【问题描述】:

我真的想更好地了解如何处理 CONNECT HTTP 请求过程。我在我正在构建的 HttpServer 中被困在这一点上,并希望其他人可以帮助我鼓励我应该如何应对这些下一个挑战。到目前为止,关于我的代码的一些信息。我有一个类 HTTPServer 在端口 8080 上的套接字上侦听(最初它是一个非 SSL 套接字)。我有一个名为 DefaultHttpRequestHandler 的类,它包含一个 HTTPClient 实例,该实例处理需要由服务器发出的所有请求,并且 HttpServer 内的工作线程处理将浏览器发送到端口 8080 的所有请求分派。

我的问题如下:

  1. 当 CONNECT 请求进来并被发送到 DefaultHttpRequestHandler 时,它被传递给 handle(HttpRequest request, HttpResponse response,HttpContext context) 方法。在这一点上,我查看了请求,如果我看到它是一个 CONNECT 下一步怎么办?我在想我然后在普通套接字之前的端口 8080 上建立 SSL 套接字连接?还是我总是持有两个插座,一个作为标准插座,一个作为 ssl,而不是切换到 ssl 一个。这部分真的让我很沮丧,我很困惑如何编码这个傻瓜!

DefaultHttpServer.java - 服务器

 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 - 从我的代理服务器向服务器发送请求的客户端

 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 - 这是我的 httpserver 中处理调度请求的运行方法

 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) {
            }
        }
    }

}

【问题讨论】:

    标签: java sockets ssl


    【解决方案1】:

    接收CONNECT 请求(并接受它)的代理不会进行任何 SSL/TLS 初始化或处理(如果这样做,它将成为潜在的 MITM 攻击者)。它只是来回中继目标 HTTPS 主机和初始客户端之间的所有流量。

    这些答案可能更详细:

    您需要的是能够获取底层套接字(或输入/输出流)并写入您在另一端读取的每个字节。

    【讨论】:

    • 该信息确实对您有所帮助。但是,它并没有回答我关于您如何处理 SSL 部分的主要问题。从您提供的那些链接看来,代理服务器似乎可以使用标准套接字来侦听来自浏览器的传入 http 请求。如果是这种情况,为什么会存在 serversocket 和 sslserversocket。我假设代理服务器将是默认套接字连接,而不是在您看到 CONNECT 切换到 sslsocket 之后,但这不起作用,因为浏览器通过的任何纯文本都会引发异常。为什么会如此令人困惑!
    • 不,您可能误解了,但代理绝对不会做任何与 SSL 相关的事情。它根本不会切换到 SSLSocket。它只是打开到目标 HTTPS 服务器的 TCP 连接,将它从初始客户端读取的内容写入它,然后将它从客户端读取的内容写回客户端。代理服务器根本不使用 SSLServerSocket(除非浏览器和代理之间的连接本身是通过 SSL 完成的,但这很不寻常)。
    • 您可能遇到的困难是我不确定您是否可以从您在处理程序中获得的HttpRequest 实例访问初始客户端套接字。您需要它的 I/O 流能够将它们通过隧道传送到目标 HTTPS 服务器。因为一旦你使用了CONNECT,你就不再真正使用HTTP(在浏览器和代理之间),准备一个HttpResponse实例并没有什么意义。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多