2015-12-08 12 views
6

Bắt đầu từ this post tôi cố gắng thực hiện một máy chủ proxy ít để xử lý GETPOST yêu cầu cũng như (chỉ cần thay thế các lớp Handler bởi hình dưới đây):Java Proxy - không thể trao đổi dữ liệu từ yêu cầu HTTP GET/POST đúng

public static class Handler extends Thread { 

    public static final Pattern CONNECT_PATTERN 
      = Pattern.compile("CONNECT (.+):(.+) HTTP/(1\\.[01])", Pattern.CASE_INSENSITIVE); 
    public static final Pattern GET_POST_PATTERN 
      = Pattern.compile("(GET|POST) (?:http)://([^/:]*)(?::([^/]*))?(/.*) 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, Integer.MAX_VALUE); 

      Matcher connectMatcher = CONNECT_PATTERN.matcher(request); 
      Matcher getNpostMatcher = GET_POST_PATTERN.matcher(request); 

      System.out.println("Request: " +request); 
      if (connectMatcher.matches()) { 
       // ... 

      } else if (getNpostMatcher.matches()) { 
       String method = getNpostMatcher.group(1); 
       String hostString = getNpostMatcher.group(2); 
       String portString = getNpostMatcher.group(3); 
       String lengthString = null; 
       String line; 
       ArrayList<String> buffer = new ArrayList<String>(); 
       Integer port = portString == null || "".equals(portString) ? 80 : Integer.parseInt(portString); 
       Integer length = null; 

       buffer.add(request); 
       while ((line = readLine(clientSocket, Integer.MAX_VALUE)) != null) { 
        buffer.add(line); 

        if ("".equals(line)) break; 

        if (lengthString == null && line.startsWith("Content-Length: ")) { 
         lengthString = line.substring(16); 
         length = Integer.parseInt(lengthString); 
        } 
       } 

       try { 
        final Socket forwardSocket; 
        try { 
         forwardSocket = new Socket(hostString, port); 
         System.out.println(" " + forwardSocket); 

        } catch (IOException | NumberFormatException e) { 
         OutputStreamWriter outputStreamWriter 
           = new OutputStreamWriter(clientSocket.getOutputStream(), "ISO-8859-1"); 

         e.printStackTrace(); 
         outputStreamWriter.write("HTTP/" + connectMatcher.group(3) + " 502 Bad Gateway\r\n"); 
         outputStreamWriter.write("Proxy-agent: Simple/0.1\r\n"); 
         outputStreamWriter.write("\r\n"); 
         outputStreamWriter.flush(); 
         return; 
        } 

        PrintWriter printWriter = new PrintWriter(forwardSocket.getOutputStream()); 

        for (String bufferedLine : buffer) { 
         printWriter.println(bufferedLine); 
        } 
        printWriter.flush(); 

        if ("POST".equals(method) && length > 0) { 
         System.out.println ("Posting data ..."); 
         if (previousWasR) { // skip \n if existing 
          int read = clientSocket.getInputStream().read(); 
          if (read != '\n') { 
           forwardSocket.getOutputStream().write(read); 
          } 
          forwardData(threadId, clientSocket, forwardSocket, length, true); // only forward "Content-length" bytes 
         } else { 
          forwardData(threadId, clientSocket, forwardSocket, length, true); // only forward "Content-length" bytes 
         } 
        } 

        System.out.println ("Forwarding response ..."); 
        forwardData(threadId, forwardSocket, clientSocket, null, false); 

        if (forwardSocket != null) { 
         forwardSocket.close(); 
        } 

       } catch (Exception e) { 
        e.printStackTrace(); 
       } 
      } 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } finally { 
      try { 
       clientSocket.close(); 

      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 

    private static void forwardData(int threadId, Socket inputSocket, Socket outputSocket, Integer length, boolean isPost) { 
     try { 
      InputStream inputStream = inputSocket.getInputStream(); 
      try { 
       OutputStream outputStream = outputSocket.getOutputStream(); 
       try { 
        byte[] buffer = new byte[4096]; 
        int read; 
        if (length == null || length > 0) { 
         do { 
          if ((read = inputStream.read(buffer)) > 0) { 
           outputStream.write(buffer, 0, read); 
           if (inputStream.available() < 1) { 
            outputStream.flush(); 
           } 
           if (length != null) { 
            length = length - read; 
           } 
          } 
         } while (read >= 0 && (length == null || length > 0)); 
        } 
       } finally { 
        if (!outputSocket.isOutputShutdown()) { 
         if (!isPost) { 
          outputSocket.shutdownOutput(); 
         } 
        } 
       } 
      } finally { 
       if (!inputSocket.isInputShutdown()) { 
        inputSocket.shutdownInput(); 
       } 
      } 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 

    private String readLine(Socket socket, Integer noOfBytes) throws IOException { 
     ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 
     int next; 
     readerLoop: 
     while (noOfBytes-- > 0 && (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"); 
    } 
} 

Sau khi có một số vấn đề với yêu cầu POST, tôi đã tìm ra rằng điều quan trọng là phải tắt luồng đúng cách. Vì vậy, cuối cùng mã ở trên hoạt động khá tốt khi sử dụng Internet Explorer. Tuy nhiên,

Sử dụng các trình duyệt khác, có vẻ như các luồng/ổ cắm không được đóng đúng cách, vì đôi khi các chỉ báo tải đang chạy khá lâu, mặc dù nội dung có vẻ đã được tải. Đôi khi, các trang web không được tải hoàn toàn và các chủ đề dường như bị treo tại ...

if ((read = inputStream.read(buffer)) > 0) { 

in forwardData(...). Tôi không biết làm thế nào tôi có thể tìm hiểu, cho dù dòng có thể cung cấp một số dữ liệu hay không - hoặc làm thế nào để tránh cuộc gọi chặn này của read ở tất cả.

Không ai biết tôi đang làm gì sai và cách tôi có thể chuyển tiếp dữ liệu đúng cách để tất cả trình duyệt tải nội dung chính xác mà không có sự chậm trễ không cần thiết?

Trả lời

1

Hãy xem, vấn đề là bạn đang sử dụng forwardData() để sao chép phản hồi của máy chủ cho máy khách, vì nó không liên quan đến trường Content-Length:.

HTTP/1.1{HTTP/1.1 200 OK 
Cache-Control: no-cache, must-revalidate 
Pragma: no-cache 
Content-Type: text/html; Charset=utf-8 
Content-Encoding: gzip 
Expires: Fri, 01 Jan 1990 00:00:00 GMT 
Vary: Accept-Encoding 
Server: Microsoft-IIS/8.0 
Access-Control-Allow-Origin: * 
X-RADID: P301511016-T104210110-C24000000000248666 
P3P: CP="BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo" 
Date: Tue, 15 Dec 2015 09:33:41 GMT 
Content-Length: 1656 

<1656 Bytes> 

Trong ví dụ trả lời ở trên, proxy phải tắt luồng sau khi đọc 1656 byte từ máy chủ. Nếu không, đọc sẽ chặn.

Vì vậy, những gì bạn cần là một số mã để tìm kiếm câu trả lời cho trường Content-Length:, ví dụ:

private static void forwardData(int threadId, Socket inputSocket, Socket outputSocket, Integer length, boolean isPost) { 
    int cLength = -1; 
    int count = 0; 
    try { 
     InputStream inputStream = inputSocket.getInputStream(); 
     try { 
      OutputStream outputStream = outputSocket.getOutputStream(); 
      try { 
       byte[] buffer = new byte[4096]; 
       int read; 
       if (length == null || length > 0) { 
        do { 
         if ((read = inputStream.read(buffer)) > 0) { // search for "Content-Length: " 
          if (cLength == -1) { 
           String response = new String(buffer, "UTF-8"); 
           int pos = response.indexOf("Content-Length:"); 
           if (pos > 0) { 
            String lString = response.substring(pos + 16, pos + 24).replaceAll("([0-9]*).*\\n?\\r?.*", "$1"); 
            cLength = Integer.parseInt(lString); 
           } 
          } 

          if (cLength != -1) { // if length is given, count bytes from empty line on 
           if (count > 0) { // already started - so just add 
            count = count + read; 
           } else { // check if empty line exists, "\r\n\r\n" or "\r\r" 
            for (int n = 0; n < read; n++) { 
             if (buffer[n] == 13 && buffer[n + 1] == 13) { 
              count = read - (n + 2); // if so, set count to bytes read after the empty line 
             } 
             if (buffer[n] == 13 && buffer[n + 1] == 10 && buffer[n + 2] == 13 && buffer[n + 3] == 10) { 
              count = read - (n + 4); // same as above 
             } 
            } 
           } 
          } 

          outputStream.write(buffer, 0, read); 
          if (inputStream.available() < 1) { 
           outputStream.flush(); 
          } 
          if (length != null) { 
           length = length - read; 
          } 
         } 
        } while (read >= 0 && (length == null || length > 0) && (cLength == -1 || count < cLength)); 
       } 

      } finally { 
       if (!outputSocket.isOutputShutdown()) { 
        if (!isPost) { 
         outputSocket.shutdownOutput(); 
        } 
       } 
      } 
     } finally { 
      if (!inputSocket.isInputShutdown()) { 
       inputSocket.shutdownInput(); 
      } 
     } 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
} 

Mặc dù đoạn mã trên hiện không phải là 100% làm việc, nó sẽ cho bạn một ý tưởng về cách tiến hành. Nếu độ dài nội dung là null và một dòng trống được nhận, bạn cũng có thể tắt luồng.

+0

Điều này đã cho tôi chắc chắn là một bước tiến lớn. Cảm ơn bạn đã giúp đỡ! – Trinimon

Các vấn đề liên quan