2013-05-03 121 views
11

我在制作依赖于设置http.proxyPorthttp.proxyHost的Java应用程序。有两个过程:一个是正常程序,另一个是代理。我有一个简单的套接字侦听器,运行在http.proxyPort(我控制)上。它是那样简单Java https代理(使用https.proxyPort和https.proxyHost)

while (true) { 
    try { 
    Socket connection = server.accept(); 

    Handler handler = new Handler(connection); 
    handler.start(); 
    } catch (Exception ex) { 
     ex.printStackTrace(); 
    } 
} 

所以每当 “处理1” 发出HTTP请求 - 像

URL yahoo = new URL("http://www.google.ca/"); 
URLConnection yc = yahoo.openConnection(); 
System.out.println(yc.getClass().getName()); 
BufferedReader in = new BufferedReader(new InputStreamReader(yc.getInputStream())); 

它去通过代理。现在如果客户端使用HTTPS协议呢?喜欢改用https://google.ca?有一个属性https.proxyPorthttps.proxyHost,但我真的一直在努力几个月(开,关,这不是太重要),没有运气。我读过一堆线程(我会在最后列出一些,所以你知道我已经做了一些事情)。

到目前为止我最亲密的尝试: 服务器

try { 
    System.setProperty("javax.net.ssl.keyStore", "test.jks"); 
    System.setProperty("javax.net.ssl.keyStorePassword", "2520xe"); 

    SSLServerSocketFactory sslserversocketfactory = 
      (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); 
    SSLServerSocket sslserversocket = 
      (SSLServerSocket) sslserversocketfactory.createServerSocket(9999); 
    System.out.println("Ready"); 
    SSLSocket sslsocket = (SSLSocket) sslserversocket.accept(); 

    InputStream inputstream = sslsocket.getInputStream(); 
    InputStreamReader inputstreamreader = new InputStreamReader(inputstream); 
    BufferedReader bufferedreader = new BufferedReader(inputstreamreader); 

    OutputStream toClient = sslsocket.getOutputStream(); 
    toClient.write(("HTTP/1.0 200 Connection established\n" + 
      "Content-Length: " + "Shut down!".getBytes().length 
            + "\r\n").getBytes("utf-8")); 
    toClient.write("Shut down!".getBytes("utf-8")); 
    toClient.close(); 
} catch (Exception exception) { 
    exception.printStackTrace(); 
} 

客户

try { 
    System.setProperty("https.proxyHost", "127.0.0.1"); 
    System.setProperty("https.proxyPort", "9999"); 
    URL yahoo = new URL("https://www.google.ca/"); 
    URLConnection yc = yahoo.openConnection(); 
    System.out.println(yc.getClass().getName()); 
    BufferedReader in = new BufferedReader(
       new InputStreamReader(
       yc.getInputStream())); 
    String inputLine; 

    while ((inputLine = in.readLine()) != null) 
    System.out.println(inputLine); 
    in.close(); 
} catch (Exception ex) { 
    ex.printStackTrace(); 
} 

而且我得到这个错误javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?我GOOGLE了,但与一些邮件的东西,而不是走了过来。

基本上,我需要创建一个Java代理服务器,这是由https.proxyPorthttps.proxyHost标志设置到客户端,并且可以将数据发送回客户端应用程序,这可能不以任何方式被修改(它只是用URL connection = new URL("https://...")

几个我试过的网站...

+0

目前我还设置了系统属性https.proxyHost挣扎。通过将调试器附加到VM进程,似乎虚拟机根本没有拿起https.proxyHost属性。 – 2014-04-30 10:51:04

+0

好的,只需使用proxyHost和proxyPort而不使用http。或https。前缀现在适合我。它也似乎是(再一次)从Java 6到Java 7的行为。从appleis到Java 7的上述语句。 – 2014-04-30 12:57:21

+0

当通过代理请求https URL时,客户端必须请求'CONNECT www.google.ca:443 HTTP/1.0'和代理服务器响应'HTTP/1.0 200连接建立',两者都是纯文本格式。然后客户端开始SSL握手。 – auntyellow 2014-11-26 13:27:15

回答

8

由于auntyellow评论说:你不需要做任何SSL-摆弄你自己。基本上HTT代理是关于在双方之间转发二进制数据的。

draft-luotonen-web-proxy-tunneling-01.txt

CLIENT -> SERVER      SERVER -> CLIENT 
-------------------------------------- ----------------------------------- 
CONNECT home.netscape.com:443 HTTP/1.0 
User-agent: Mozilla/4.0 
<<< empty line >>> 
             HTTP/1.0 200 Connection established 
             Proxy-agent: Netscape-Proxy/1.1 
             <<< empty line >>> 
       <<< data tunneling to both directions begins >>> 

所以基本上你需要确保你信任你的客户,足以从代理防火墙的位置连接到指定的主机和端口。由于这种常见做法是将允许的端口限制为443,因此拒绝与本地主机以及来自“不受信任”的各方的连接。

这是一个“简单”的服务器,如果你不相信喷气机是可用作在Java中https.proxy:对于HTTPS协议

import java.io.*; 
import java.net.ServerSocket; 
import java.net.Socket; 
import java.util.regex.Matcher; 
import java.util.regex.Pattern; 

/** 
* Created for http://stackoverflow.com/q/16351413/1266906. 
*/ 
public class Server extends Thread { 

    public static void main(String[] args) { 
     (new Server()).run(); 
    } 

    public Server() { 
     super("Server Thread"); 
    } 

    @Override 
    public void run() { 
     try (ServerSocket serverSocket = new ServerSocket(9999)) { 
      Socket socket; 
      try { 
       while ((socket = serverSocket.accept()) != null) { 
        (new Handler(socket)).start(); 
       } 
      } catch (IOException e) { 
       e.printStackTrace(); // TODO: implement catch 
      } 
     } catch (IOException e) { 
      e.printStackTrace(); // TODO: implement catch 
      return; 
     } 
    } 

    public static class Handler extends Thread { 
     public static final Pattern CONNECT_PATTERN = Pattern.compile("CONNECT (.+):(.+) HTTP/(1\\.[01])", 
                     Pattern.CASE_INSENSITIVE); 
     private final Socket clientSocket; 
     private boolean previousWasR = false; 

     public Handler(Socket clientSocket) { 
      this.clientSocket = clientSocket; 
     } 

     @Override 
     public void run() { 
      try { 
       String request = readLine(clientSocket); 
       System.out.println(request); 
       Matcher matcher = CONNECT_PATTERN.matcher(request); 
       if (matcher.matches()) { 
        String header; 
        do { 
         header = readLine(clientSocket); 
        } while (!"".equals(header)); 
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(clientSocket.getOutputStream(), 
                        "ISO-8859-1"); 

        final Socket forwardSocket; 
        try { 
         forwardSocket = new Socket(matcher.group(1), Integer.parseInt(matcher.group(2))); 
         System.out.println(forwardSocket); 
        } catch (IOException | NumberFormatException e) { 
         e.printStackTrace(); // TODO: implement catch 
         outputStreamWriter.write("HTTP/" + matcher.group(3) + " 502 Bad Gateway\r\n"); 
         outputStreamWriter.write("Proxy-agent: Simple/0.1\r\n"); 
         outputStreamWriter.write("\r\n"); 
         outputStreamWriter.flush(); 
         return; 
        } 
        try { 
         outputStreamWriter.write("HTTP/" + matcher.group(3) + " 200 Connection established\r\n"); 
         outputStreamWriter.write("Proxy-agent: Simple/0.1\r\n"); 
         outputStreamWriter.write("\r\n"); 
         outputStreamWriter.flush(); 

         Thread remoteToClient = new Thread() { 
          @Override 
          public void run() { 
           forwardData(forwardSocket, clientSocket); 
          } 
         }; 
         remoteToClient.start(); 
         try { 
          if (previousWasR) { 
           int read = clientSocket.getInputStream().read(); 
           if (read != -1) { 
            if (read != '\n') { 
             forwardSocket.getOutputStream().write(read); 
            } 
            forwardData(clientSocket, forwardSocket); 
           } else { 
            if (!forwardSocket.isOutputShutdown()) { 
             forwardSocket.shutdownOutput(); 
            } 
            if (!clientSocket.isInputShutdown()) { 
             clientSocket.shutdownInput(); 
            } 
           } 
          } else { 
           forwardData(clientSocket, forwardSocket); 
          } 
         } finally { 
          try { 
           remoteToClient.join(); 
          } catch (InterruptedException e) { 
           e.printStackTrace(); // TODO: implement catch 
          } 
         } 
        } finally { 
         forwardSocket.close(); 
        } 
       } 
      } catch (IOException e) { 
       e.printStackTrace(); // TODO: implement catch 
      } finally { 
       try { 
        clientSocket.close(); 
       } catch (IOException e) { 
        e.printStackTrace(); // TODO: implement catch 
       } 
      } 
     } 

     private static void forwardData(Socket inputSocket, Socket outputSocket) { 
      try { 
       InputStream inputStream = inputSocket.getInputStream(); 
       try { 
        OutputStream outputStream = outputSocket.getOutputStream(); 
        try { 
         byte[] buffer = new byte[4096]; 
         int read; 
         do { 
          read = inputStream.read(buffer); 
          if (read > 0) { 
           outputStream.write(buffer, 0, read); 
           if (inputStream.available() < 1) { 
            outputStream.flush(); 
           } 
          } 
         } while (read >= 0); 
        } finally { 
         if (!outputSocket.isOutputShutdown()) { 
          outputSocket.shutdownOutput(); 
         } 
        } 
       } finally { 
        if (!inputSocket.isInputShutdown()) { 
         inputSocket.shutdownInput(); 
        } 
       } 
      } catch (IOException e) { 
       e.printStackTrace(); // TODO: implement catch 
      } 
     } 

     private String readLine(Socket socket) throws IOException { 
      ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 
      int next; 
      readerLoop: 
      while ((next = socket.getInputStream().read()) != -1) { 
       if (previousWasR && next == '\n') { 
        previousWasR = false; 
        continue; 
       } 
       previousWasR = false; 
       switch (next) { 
        case '\r': 
         previousWasR = true; 
         break readerLoop; 
        case '\n': 
         break readerLoop; 
        default: 
         byteArrayOutputStream.write(next); 
         break; 
       } 
      } 
      return byteArrayOutputStream.toString("ISO-8859-1"); 
     } 
    } 
} 
+0

不错。有一个皱纹。当代理从一个套接字读取EOS时,它应该关闭另一个套接字进行输出并退出该线程。当它关闭了两个插座时,它可以关闭它们并退出第二个线程。这有助于HTTP保持活动状态。 – EJP 2014-11-27 23:19:04

+0

@EJP感谢您的反馈!我切换到[调用shutdown-methods](http://stackoverflow.com/a/3626450/1266906),并添加了一个更接近'forwardSocket'的地方。我错过了别的东西吗? – TheConstructor 2014-11-28 06:15:22

1

默认的Java SE7实现的URLConnection的使用参数
https.proxyHosthttps.proxyPort

添加到Tomcat:

-Dhttps.proxyHost = “192.168.121.31” -Dhttps.proxyPort = “3128”