2013-05-03 34 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」