HttpURLConnection:为什么在指定 Expect:100-Continue Header 时每个块有 5 秒的延迟?
HttpURLConnection : Why is there 5 seconds delay with every chunk when Expect:100-Continue Header is specified?
我正在试验 RFC Expect Header in java HttpURLConnection 除了一个细节外,它工作得很好。
发送 Fixed Length Mode or between each chunk in Chunk Streaming Mode
的 body 之间有 5 秒的等待时间
这里是客户端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();
}
我正在上传 3 个块。服务器端收到5包数据
全部headers。回复 100 继续
3 个块。对于每个块响应 100 Continue
最后一个区块[长度 0]。回复 200 OK
这是测试服务器
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;
}
}
}
}
}
}
输出:
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
==========
我已经检查了连接中的每个超时方法Object
System.out.println(con.getConnectTimeout());
System.out.println(con.getReadTimeout());
两个return 0
那么这 5 秒的延迟是从哪里来的呢?
我正在使用 jdk 17.0.2 和 windows 10
3 Chunks. For each chunk respond with 100 Continue
那不是 Expect: 100-Continue
的工作方式。您的服务器代码对于您尝试执行的操作是完全错误的。实际上,对于一般的 HTTP 服务器,您的服务器代码是完全错误的。它甚至根本不尝试解析 HTTP 协议。不是 HTTP headers,不是 HTTP 块,什么都不是。您是否没有使用实际的 HTTP 服务器实现,例如 Java 自己的 HttpServer
?
当使用 Expect: 100-Continue
时,客户端只需要发送请求 headers,然后停止并等待几秒钟以查看服务器是否发送 100
响应或不是:
如果服务器响应100
,客户端可以通过发送请求body完成请求,然后接收最终响应。
如果服务器响应 100
以外的任何内容,客户端可能会立即失败而不发送请求 body。
如果没有收到响应,客户端可以通过发送请求body完成请求并接收最终响应。
Expect: 100-Continue
的全部要点是客户端在发送大请求之前请求许可 body。如果服务器不想要 body(即 headers 描述不满意的条件等),客户端不必浪费精力和带宽来发送请求 body只会被拒绝。
HttpURLConnection
built-in 支持处理 100
响应,但请参阅 How to wait for Expect 100-continue response in Java using HttpURLConnection for caveats. Also see JDK-8012625: Incorrect handling of HTTP/1.1 " Expect: 100-continue " in HttpURLConnection。
但是,如图所示的服务器代码需要进行重大重写才能正确处理 HTTP,更不用说处理分块请求正确。
非常感谢@Remy Lebeau 就如何正确解析此特殊内容提供的见解 header。我注意到在创建一个基本解析器并正确响应分块 [Transfer-Encoding:chunked header] 和固定长度流 [Content-Length header] header 之后有时我的客户仍然会卡住并偶尔等待 5 秒,有时会正常工作。
经过数小时的 com.sun.www.http.HttpURLConnection class 调试后,我意识到另一个缺陷不再出现在服务器端,而是实际上出现在代码的客户端。尤其是这一点
con.getOutputStream().write("Hello".getBytes());
con.getOutputStream().flush();
con.getOutputStream().write("World".getBytes());
con.getOutputStream().flush();
con.getOutputStream().write("123".getBytes());
con.getOutputStream().flush();
我错误地认为 class 中的 getOutputStream() 会缓存返回的输出流,但实际上它 returns 每次都是一个新的输出流。
所以我不得不将代码更改为这个
OutputStream os=client.getOutputStream();
os.write("Hello".getBytes());
os.flush();
os.write("World".getBytes());
os.flush();
os.write("123".getBytes());
os.flush();
这终于解决了我所有的问题。适用于分块和固定长度流
我正在试验 RFC Expect Header in java HttpURLConnection 除了一个细节外,它工作得很好。
发送 Fixed Length Mode or between each chunk in Chunk Streaming Mode
的 body 之间有 5 秒的等待时间这里是客户端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();
}
我正在上传 3 个块。服务器端收到5包数据
全部headers。回复 100 继续
3 个块。对于每个块响应 100 Continue
最后一个区块[长度 0]。回复 200 OK
这是测试服务器
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;
}
}
}
}
}
}
输出:
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
==========
我已经检查了连接中的每个超时方法Object
System.out.println(con.getConnectTimeout());
System.out.println(con.getReadTimeout());
两个return 0
那么这 5 秒的延迟是从哪里来的呢?
我正在使用 jdk 17.0.2 和 windows 10
3 Chunks. For each chunk respond with 100 Continue
那不是 Expect: 100-Continue
的工作方式。您的服务器代码对于您尝试执行的操作是完全错误的。实际上,对于一般的 HTTP 服务器,您的服务器代码是完全错误的。它甚至根本不尝试解析 HTTP 协议。不是 HTTP headers,不是 HTTP 块,什么都不是。您是否没有使用实际的 HTTP 服务器实现,例如 Java 自己的 HttpServer
?
当使用 Expect: 100-Continue
时,客户端只需要发送请求 headers,然后停止并等待几秒钟以查看服务器是否发送 100
响应或不是:
如果服务器响应
100
,客户端可以通过发送请求body完成请求,然后接收最终响应。如果服务器响应
100
以外的任何内容,客户端可能会立即失败而不发送请求 body。如果没有收到响应,客户端可以通过发送请求body完成请求并接收最终响应。
Expect: 100-Continue
的全部要点是客户端在发送大请求之前请求许可 body。如果服务器不想要 body(即 headers 描述不满意的条件等),客户端不必浪费精力和带宽来发送请求 body只会被拒绝。
HttpURLConnection
built-in 支持处理 100
响应,但请参阅 How to wait for Expect 100-continue response in Java using HttpURLConnection for caveats. Also see JDK-8012625: Incorrect handling of HTTP/1.1 " Expect: 100-continue " in HttpURLConnection。
但是,如图所示的服务器代码需要进行重大重写才能正确处理 HTTP,更不用说处理分块请求正确。
非常感谢@Remy Lebeau 就如何正确解析此特殊内容提供的见解 header。我注意到在创建一个基本解析器并正确响应分块 [Transfer-Encoding:chunked header] 和固定长度流 [Content-Length header] header 之后有时我的客户仍然会卡住并偶尔等待 5 秒,有时会正常工作。
经过数小时的 com.sun.www.http.HttpURLConnection class 调试后,我意识到另一个缺陷不再出现在服务器端,而是实际上出现在代码的客户端。尤其是这一点
con.getOutputStream().write("Hello".getBytes());
con.getOutputStream().flush();
con.getOutputStream().write("World".getBytes());
con.getOutputStream().flush();
con.getOutputStream().write("123".getBytes());
con.getOutputStream().flush();
我错误地认为 class 中的 getOutputStream() 会缓存返回的输出流,但实际上它 returns 每次都是一个新的输出流。
所以我不得不将代码更改为这个
OutputStream os=client.getOutputStream();
os.write("Hello".getBytes());
os.flush();
os.write("World".getBytes());
os.flush();
os.write("123".getBytes());
os.flush();
这终于解决了我所有的问题。适用于分块和固定长度流