简体   繁体   English

HttpURLConnection:当指定 Expect:100-Continue Header 时,为什么每个块都有 5 秒的延迟?

[英]HttpURLConnection : Why is there 5 seconds delay with every chunk when Expect:100-Continue Header is specified?

I am experimenting with RFC Expect Header in java HttpURLConnection which works perfectly except for one detail.我正在 java HttpURLConnection中使用RFC Expect Header 进行试验,除了一个细节外,它工作得很好。

There is an 5 second wait period between sending the body of an Fixed Length Mode or between each chunk in Chunk Streaming Mode在发送固定长度模式的正文之间或在块流模式中的每个块之间有 5 秒的等待时间

Here is the client class这里是客户端 class

public static void main(String[] args)throws Exception
 {
   HttpURLConnection con=(HttpURLConnection)new URL("http://192.168.1.2:2000/ActionG").openConnection();
   
   //for 100-Continue logic
   con.setRequestMethod("POST");
   con.setRequestProperty("Expect", "100-Continue");
  
   //responds to 100-continue logic
   con.setDoOutput(true);
   con.setChunkedStreamingMode(5);
   con.getOutputStream().write("Hello".getBytes());
   con.getOutputStream().flush();
   con.getOutputStream().write("World".getBytes());
   con.getOutputStream().flush();
   con.getOutputStream().write("123".getBytes());
   con.getOutputStream().flush();

   //decode response and response body/error if any
   System.out.println(con.getResponseCode()+"/"+con.getResponseMessage());      
   con.getHeaderFields().forEach((key,values)->
   {
    System.out.println(key+"="+values);
    System.out.println("====================");
   });      
   try(InputStream is=con.getInputStream()){System.out.println(new String(is.readAllBytes()));}
   catch(Exception ex)
   {
    ex.printStackTrace(System.err);
   
    InputStream err=con.getErrorStream();
    if(err!=null)
    {
     try(err){System.err.println(new String(is.readAllBytes()));}
     catch(Exception ex2){throw ex2;}
    }  
   } 
   con.disconnect();
  }

I am uploading 3 chunks.我正在上传 3 个块。 In the server side 5 packets of data are received服务器端收到5包数据

  1. All headers.所有标题。 respond with 100 Continue回复 100 继续

  2. 3 Chunks. 3块。 For each chunk respond with 100 Continue对于每个块响应 100 继续

  3. Last Chunk[Length 0].最后一个块 [长度 0]。 respond with 200 OK回复 200 OK

Here is the Test Server这里是测试服务器

final class TestServer
{
 public static void main(String[] args)throws Exception
 {
  try(ServerSocket socket=new ServerSocket(2000,0,InetAddress.getLocalHost()))
  {
   int count=0;
   try(Socket client=socket.accept())
   {
    int length;
    byte[] buffer=new byte[5000];
    InputStream is=client.getInputStream();
    
    OutputStream os=client.getOutputStream();
    while((length=is.read(buffer))!=-1)
    {
     System.out.println(++count);
     System.out.println(new String(buffer,0,length));
     System.out.println("==========");
  
     if(count<5)
     {
      os.write("HTTP/1.1 100 Continue\r\n\r\n".getBytes());
      os.flush();
     }
     else
     {
      os.write("HTTP/1.1 200 Done\r\nContent-Length:0\r\n\r\n".getBytes());
      os.flush();
      break;
     }   
    }
   }
  }
 }
}

Output: Output:

1
POST /ActionG HTTP/1.1
Expect: 100-Continue
User-Agent: Java/17.0.2
Host: 192.168.1.2:2000
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
Content-type: application/x-www-form-urlencoded
Transfer-Encoding: chunked


==========   //5 seconds later
2
5
Hello

==========   //5 seconds later
3
5
World

==========  //5 seconds later
//this and the last chunk come seperatly but with no delay
4
3
123

==========
5
0


==========

I have checked every Timeout Method in my Connection Object我已经检查了连接 Object 中的每个超时方法

System.out.println(con.getConnectTimeout());
System.out.println(con.getReadTimeout());

Both return 0两者都返回 0

So where is this 5 second delay coming from?那么这 5 秒的延迟是从哪里来的呢?

I am using jdk 17.0.2 with windows 10我正在使用 jdk 17.0.2 和 windows 10

3 Chunks. 3块。 For each chunk respond with 100 Continue对于每个块响应 100 继续

That is not how Expect: 100-Continue works.这不是Expect: 100-Continue工作方式。 Your server code is completely wrong for what you are attempting to do.您的服务器代码对于您尝试执行的操作是完全错误的。 In fact, your server code is completely wrong for an HTTP server in general.实际上,对于一般的 HTTP 服务器,您的服务器代码是完全错误的。 It is not even attempting to parse the HTTP protocol at all.它甚至根本没有尝试解析 HTTP 协议。 Not the HTTP headers, not the HTTP chunks, nothing.不是 HTTP 标头,不是 HTTP 块,什么都没有。 Is there a reason why you are not using an actual HTTP server implementation, such as Java's own HttpServer ?您是否没有使用实际的 HTTP 服务器实现,例如 Java 自己的HttpServer

When using Expect: 100-Continue , the client is required to send ONLY the request headers, and then STOP AND WAIT a few seconds to see if the server sends a 100 response or not:使用Expect: 100-Continue时,客户端只需要发送请求标头,然后停止并等待几秒钟以查看服务器是否发送100响应:

  • If the server responds with 100 , the client can then finish the request by sending the request body, and then receive a final response.如果服务器响应100 ,则客户端可以通过发送请求主体来完成请求,然后接收最终响应。

  • If the server responds with anything other than 100 , the client can fail its operation immediately without sending the request body at all.如果服务器响应100以外的任何内容,则客户端可以立即使操作失败,而根本不发送请求正文。

  • If no response is received, the client can finish the request by sending the request body and receive the final response.如果没有收到响应,客户端可以通过发送请求体完成请求并接收最终响应。

The whole point of Expect: 100-Continue is for a client to ask for permission before sending a large request body. Expect: 100-Continue的全部要点是客户端在发送大型请求正文之前请求许可。 If the server doesn't want the body (ie, the headers describe unsatisfactory conditions, etc), the client doesn't have to waste effort and bandwidth to send a request body that will just be rejected.如果服务器不想要正文(即,标头描述了不满意的条件等),则客户端不必浪费精力和带宽来发送将被拒绝的请求正文。

HttpURLConnection has built-in support for handling 100 responses, but see How to wait for Expect 100-continue response in Java using HttpURLConnection for caveats. HttpURLConnection内置支持处理100响应,但请参阅How to wait for Expect 100-continue response in Java using HttpURLConnection了解注意事项。 Also see JDK-8012625: Incorrect handling of HTTP/1.1 " Expect: 100-continue " in HttpURLConnection .另请参阅JDK-8012625:不正确处理 HTTP/1.1 “Expect: 100-continue” in HttpURLConnection

But, your server code as shown needs a major rewrite to handle HTTP properly , let alone handle chunked requests properly .但是,如图所示的服务器代码需要进行重大重写才能正确处理 HTTP,更不用说正确处理分块请求了。

So thank you @Remy Lebeau for the insights on how to properly parse this special header. I noticed after creating an basic parser and after responding properly to chunked[Transfer-Encoding:chunked header] and fixed length streaming[Content-Length header] headers that some times my client would still get stuck and wait for 5 seconds occasionally and sometimes would work with no problem.所以感谢@Remy Lebeau 就如何正确解析这个特殊的 header 提供的见解。我注意到在创建一个基本解析器并正确响应分块 [Transfer-Encoding:chunked header] 和固定长度流 [Content-Length header] 标头之后有时我的客户仍然会卡住并偶尔等待 5 秒钟,有时会毫无问题地工作。

After hours of debugging the com.sun.www.http.HttpURLConnection class i realized Another flaw was no longer in the server side but actually in the Client Side of the code.经过数小时调试com.sun.www.http.HttpURLConnection class 我意识到另一个缺陷不再在服务器端,而是实际上在代码的客户端。 Especially this bit尤其是这一点

con.getOutputStream().write("Hello".getBytes());
con.getOutputStream().flush();
con.getOutputStream().write("World".getBytes());
con.getOutputStream().flush();
con.getOutputStream().write("123".getBytes());
con.getOutputStream().flush();

I had mistakenly assumed that getOutputStream() in this class would cache the outputstream returned but infact it returns an new output stream every single time.我错误地认为 class 中的getOutputStream()会缓存返回的输出流,但实际上它每次都会返回一个新的 output stream。

So i had to change the code to this所以我不得不将代码更改为此

OutputStream os=client.getOutputStream();
os.write("Hello".getBytes());
os.flush();
os.write("World".getBytes());
os.flush();
os.write("123".getBytes());
os.flush();

This finally solved all my problems.这终于解决了我所有的问题。 Works for chunked and fixed length streaming适用于分块和固定长度的流媒体

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM