2012-02-20 61 views
3

我已經有一個可以處理多個HTTP請求的正常工作的HTTP代理服務器。現在我的問題是如何處理https請求?創建一個接受HTTPS的Java代理服務器

這裏有一個簡單的代碼我使用:

class Daemon 
{ 
    public static void main(String[] args) 
    { 
     ServerSocket cDaemonSocket = new ServerSocket(3128); 

     while(true) 
     { 
      try 
      { 
      Socket ClientSocket = cDaemonSocket.accept(); 
      (new ClientHandler(ClientSocket)).start(); 
      }catch(Exception e) { } 
     } 
    } 

} 

和ClientHandler的

class ClientHandler extends Thread 
{ 
     private Socket socket = null; 
     private Socket remoteSocket = null; 
     private HTTPReqHeader request = null; 
     ClientHandler(Socket socket) 
     { 
      this.socket = socket; 
      request = new HTTPReqHeader(); 
      request.parse(socket); // I read and parse the HTTP request here 
     } 

     public void run() 
     { 
      if(!request.isSecure()) 
      { 
       remoteSocket = new Socket(request.url,request.port); 
      } 
      else 
      { 
       // now what should I do to established a secured socket? 
      } 

      // start connecting remoteSocket and clientSocket 
      ........... 
     } 
} 

}

我真的嘗試搜索怎麼樣,我都遇到SSL隧道,證書,握手,SSLSocket,SSLFactory,trustStore等類似的東西,但仍然無法使它工作..我只需要知道我需要什麼東西和步驟來建立一個連接到啓用了SSL的Web服務器。

+0

請閱讀[常見問題]和[問] – 2012-02-20 07:29:47

+0

檢查http://stackoverflow.com/questions/516323/https-connections-over-proxy-servers和http://stackoverflow.com/questions/ 753191 /通過代理服務器訪問https站點,以瞭解HTTPS通過「代理」如何工作。 – 2012-02-20 07:43:39

+0

@JimGarrison哦,很抱歉。下次我會盡力問好。只是迫切需要一個答案。 – 2012-02-22 04:12:06

回答

5

我終於明白了。

我只需要使用普通套接字並向客戶端發送建立連接的消息。然後進行隧道。

這裏是一個工作代碼:

private Socket socket = null; 
     private Socket remoteSocket = null; 
     private HTTPReqHeader request = null; 
     ClientHandler(Socket socket) 
     { 
      this.socket = socket; 
      request = new HTTPReqHeader(); 
      request.parse(socket); // I read and parse the HTTP request here 
     } 

     public void run() 
     { 

      remoteSocket = new Socket(request.url,request.port); 

      if(request.isSecure()) 
      { 
       // send ok message to client 
       String ConnectResponse = "HTTP/1.0 200 Connection established\n" + 
              "Proxy-agent: ProxyServer/1.0\n" + 
              "\r\n"; 
       try 
       { 
      DataOutputStream out = new DataOutputStream(socket.getOutputStream()); 
        out.writeByte(ConnectResponse); 
        out.flush(); 
       } catch(Exception e) {} 

      } 

      // start connecting remoteSocket and clientSocket 
      ........... 
     } 

這裏的服務器上如何代理處理CONNECT一個很好的解釋。 http://curl.haxx.se/rfc/draft-luotonen-web-proxy-tunneling-01.txt

+0

HTTP中的行結束符被定義爲\ r \ n,而不是\ n。 – EJP 2013-10-24 21:34:30

+0

class'HTTPReqHeader'從哪裏來?此外,'out.writeByte(ConnectResponse)'可能不起作用,因爲您正在編寫一個字符串,而不是一個字節。請更新您的答案以顯示一些工作代碼,理想情況下爲[SSCCE](http://sscce.org/),並提及外部依賴關係(庫)。 – kriegaex 2014-10-09 09:38:11

1

Google「https服務器在java」中,您可能會發現a relevant tutorial,a related RFCstandard documentation。我希望這個能幫上忙 :)。

+0

雖然鏈接可能會對需要在Java中使用SSL的人感興趣,但這裏不是主題。 OP要求一個可以處理HTTPS請求的HTTP代理:在這種情況下,代理本身根本不需要知道有關SSL的任何信息。 – Bruno 2012-02-20 09:53:54

2

請在下面的代碼中找到創建HTTPS代理。它不會修改響應。將它與HTTP集成在其他子句中寫入HTTP代碼。您可以在許多地方找到代理的HTTP代碼。

基本上,當客戶端發送HTTPS請求代理它帶有CONNECT關鍵字時,發生了什麼。建立與上游服務器的連接後,您必須向客戶端發送HTTP/1.1 200 OK。之後,您必須爲上游服務器和上游服務器到客戶端的傳入流提供客戶端的傳入輸入流,而無需頭/主機等。

您根本不需要考慮SSL。

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"); 
     } 
    } 
}