2015-12-08 8 views
6

this post से शुरू से डेटा का आदान-प्रदान नहीं कर सकता मैं एक छोटे से प्रॉक्सी सर्वर है कि GET और POST अनुरोधों को प्रबंधित रूप में अच्छी तरह लागू करने के लिए करने की कोशिश की है (बस नीचे एक एक करके Handler वर्ग डालें):जावा प्रॉक्सी - HTTP GET/पोस्ट अनुरोध ठीक से

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

POST अनुरोधों के साथ कुछ समस्याएं होने के बाद, मुझे पता चला कि धाराओं को सही तरीके से बंद करना महत्वपूर्ण है। तो अंत में इंटरनेट एक्सप्लोरर का उपयोग करते समय ऊपर दिया गया कोड काफी अच्छा काम कर रहा है।

अन्य ब्राउज़रों का उपयोग करना, हालांकि, ऐसा लगता है कि स्ट्रीम/सॉकेट ठीक तरह से बंद नहीं होते हैं, क्योंकि कभी-कभी लोडिंग संकेतक थोड़ी देर के लिए चल रहे हैं, हालांकि सामग्री पहले ही लोड हो रही है। कभी कभी, साइटों पूरी तरह से forwardData(...) में लोड किए गए हैं नहीं और धागे पर लटका करने लगते हैं ...

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

। मुझे नहीं पता कि मैं कैसे पता लगा सकता हूं, चाहे स्ट्रीम कुछ डेटा प्रदान करे या नहीं - या read की इस अवरुद्ध कॉल से कैसे बचें।

क्या कोई जानता है कि मैं क्या गलत कर रहा हूं और मैं डेटा को सही तरीके से कैसे अग्रेषित कर सकता हूं, ताकि सभी ब्राउज़र अनावश्यक देरी के बिना सामग्री को सही तरीके से लोड कर सकें?

उत्तर

1

देखो, समस्या यह है कि आप क्लाइंट को सर्वर प्रतिक्रियाओं की प्रतिलिपि बनाने के लिए forwardData() का उपयोग कर रहे हैं, क्योंकि इसमें 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> 

ऊपर उदाहरण प्रतिक्रिया में, प्रॉक्सी को सर्वर से 1656 बाइट पढ़ने के बाद धाराओं को बंद करना होगा। यदि ऐसा नहीं होता है, तो पढ़ा जाएगा ब्लॉक।

तो क्या आप की जरूरत कुछ कोड है कि Content-Length: क्षेत्र के लिए प्रतिक्रिया खोज करता है, उदाहरण के लिए है:

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

हालांकि कोड ऊपर वर्तमान में 100% काम कर रहे है, यह आप पर कैसे करने के लिए एक विचार देना चाहिए आगे बढ़ें। यदि सामग्री की लंबाई शून्य है और एक खाली रेखा प्राप्त होती है तो संभवतः आप स्ट्रीम को बंद भी कर सकते हैं।

+0

यह मुझे निश्चित रूप से एक बड़ा कदम आगे ले गया। आपकी सहायताके लिए धन्यवाद! – Trinimon

संबंधित मुद्दे