基于对另一个问题的回答我正在使用curl 将stdout
一个进程的流作为请求的实体POST
:
myDataGeneratingApp \
| curl -H "Content-Type: application/json" -H "Transfer-Encoding: chunked" -X POST -d @- http://localhost:12000
EOF
不幸的是,curl在开始发送数据之前正在等待标准输出。我知道这一点是因为我可以独立运行我的应用程序,并且数据会立即输出到控制台,但是当我通过管道传输到curl 时,在服务开始接收数据之前会出现明显的延迟。
当数据从应用程序的标准中可用时,如何使用curl立即流式传输数据?如果在curl中不可能,那么还有其他解决方案(例如wget)吗?
答案1
浏览curl代码传输.c看起来该程序能够使用分块协议重新打包请求数据(从curl到服务器),其中每个数据块都以ascii十六进制的块长度为前缀,并以\r\n
.
连接到服务器后,似乎可以使用流式传输方式使用它-T -
。考虑这个例子:
for i in $(seq 5)
do date
sleep 1
done |
dd conv=block cbs=512 |
strace -t -e sendto,read -o /tmp/e \
curl --trace-ascii - \
-H "Transfer-Encoding: chunked" \
-H "Content-Type: application/json" \
-X POST -T - http://localhost/...
此脚本将 5 个数据块发送到管道,每个数据块均以日期开头,并通过dd
, 填充到 512 字节,并在其中strace
运行curl -T -
以读取管道。在终端中我们可以看到
== Info: Connected to localhost (::1) port 80 (#0)
=> Send header, 169 bytes (0xa9)
0000: POST /... HTTP/1.1
001e: Host: localhost
002f: User-Agent: curl/7.47.1
0048: Accept: */*
0055: Transfer-Encoding: chunked
0071: Content-Type: application/json
0091: Expect: 100-continue
00a7:
<= Recv header, 23 bytes (0x17)
0000: HTTP/1.1 100 Continue
它显示连接和发送的标头。特别curl
是没有提供Content-length:
标头,而是Expect:
服务器(apache)已回复的标头Continue
。紧接着是前 512 字节(十六进制 200)数据:
=> Send data, 519 bytes (0x207)
0000: 200
0005: Fri Sep 14 15:58:15 CEST 2018
0045:
0085:
00c5:
0105:
0145:
0185:
01c5:
=> Send data, 519 bytes (0x207)
在输出文件中,我们看到来自管道的strace
每个时间戳,并写入连接:read
sendto
16:00:00 read(0, "Fri Sep 14 16:00:00 CEST 2018 "..., 16372) = 512
16:00:00 sendto(3, "200\r\nFri Sep 14 16:00:00 CEST 20"..., 519, ...) = 519
16:00:00 read(0, "Fri Sep 14 16:00:01 CEST 2018 "..., 16372) = 512
16:00:01 sendto(3, "200\r\nFri Sep 14 16:00:01 CEST 20"..., 519, ...) = 519
16:00:01 read(0, "Fri Sep 14 16:00:02 CEST 2018 "..., 16372) = 512
16:00:02 sendto(3, "200\r\nFri Sep 14 16:00:02 CEST 20"..., 519, ...) = 519
16:00:02 read(0, "Fri Sep 14 16:00:03 CEST 2018 "..., 16372) = 512
16:00:03 sendto(3, "200\r\nFri Sep 14 16:00:03 CEST 20"..., 519, ...) = 519
16:00:03 read(0, "Fri Sep 14 16:00:04 CEST 2018 "..., 16372) = 512
16:00:04 sendto(3, "200\r\nFri Sep 14 16:00:04 CEST 20"..., 519, ...) = 519
16:00:04 read(0, "", 16372) = 0
16:00:05 sendto(3, "0\r\n\r\n", 5, ...) = 5
正如您所看到的,它们之间的间隔为 1 秒,表明数据在接收的同时也在发送。您必须至少有 512 个字节要发送,因为 正在读取数据fread()
。
答案2
请参阅下面的编辑
你想要的东西是不可能的。要发送 POST 数据,必须知道长度,因此curl
必须首先读取整个数据以确定长度。
Transfer-Encoding: chunked
是绕过该限制的一种方法,但仅适用于服务器的响应。
原因是chunked
仅在HTTP/1.1中支持,但在发送请求时,客户端无法知道服务器是否理解HTTP/1.1。该信息附带答案,但发送请求为时已晚。
编辑
从 wget 手册来看,这似乎是 wget 的限制:
请注意,Wget 需要提前知道 POST 数据的大小。因此 --post-file 的参数必须是常规文件;指定 FIFO 或 /dev/stdin 之类的东西是行不通的。目前还不太清楚如何解决 HTTP/1.0 固有的限制。尽管 HTTP/1.1 引入了不需要提前知道请求长度的分块传输,但客户端不能使用分块,除非它知道它正在与 HTTP/1.1 服务器通信。在收到响应之前它无法知道这一点,而响应又要求请求已完成——这是一个先有鸡还是先有蛋的问题。
尽管问题确实存在,但人们已经认识到这一点RFC 7230:
客户端不得发送包含 Transfer-Encoding 的请求,除非它知道服务器将处理 HTTP/1.1(或更高版本)请求;此类知识可能以特定用户配置的形式或通过记住先前收到的响应的版本来实现。
因此,发送分块 POST 数据是可能的,正如另一个答案所示,curl 已经支持它。