python - 与 curl 一起工作,请求失败;如何修复我的请求代码?

标签 python http curl python-requests tcpdump

我正在尝试编写一个 python 模块来与硬件设备上的固定 HTTP 服务器进行通信,以便向它发送数据。我可以通过 curl 正确发送数据,但由于某种原因,当我使用 requests 时它无法正常工作python中的模块。
我已经确认(通过使用 httpbin.org/post)这两个请求是相同的,但由于某种原因,只有一个通过 curl 发送确实有效。
当我查看两个请求的 tcpdump 时,我确实看到了一个区别:初始握手基本相同,然后数据(在两种情况下)作为三个单独的数据包发送。
来自 curl ,握手后的通信如下所示:

17:58:31.691251 IP CLIENT.56184 > SERVER.http: Flags [P.], seq 1:232, ack 1, win 29200, length 231: HTTP: POST /index.html HTTP/1.1
E.....@.@.....n:..n..x.P.......(P.r.5h..POST /index.html HTTP/1.1
User-Agent: curl/7.29.0
Host: SERVER
Accept: */*
Content-Length: 1258
Expect: 100-continue
Content-Type: multipart/form-data; boundary=----------------------------61700007fd77

.........7.?`)+.
17:58:31.766389 IP SERVER.http > CLIENT.56184: Flags [.], ack 232, win 1817, length 0
E..(;.....Ks..n...n:.P.x...(....P.... ........................
17:58:32.692418 IP CLIENT.56184 > SERVER.http: Flags [P.], seq 232:486, ack 1, win 29200, length 254: HTTP
E..&..@.@.....n:..n..x.P.......(P.r.5...------------------------------61700007fd77
< Data for packet 2 >

..........8.?`..
17:58:32.856104 IP SERVER.http > CLIENT.56184: Flags [.], ack 486, win 1563, length 0
E..(;.....Km..n...n:.P.x...(....P.... ..............x...8.?`R.
17:58:32.856139 IP CLIENT.56184 > SERVER.http: Flags [P.], seq 486:1490, ack 1, win 29200, length 1004: HTTP
E.....@.@.....n:..n..x.P.......(P.r.8m..[ID]
< Data for packet 3 >

....8.?`...6....
17:58:32.919921 IP SERVER.http > CLIENT.56184: Flags [.], ack 1490, win 2048, length 0
E..(;.....Kl..n...n:.P.x...(....P....O..................8.?`O.
17:58:32.924255 IP SERVER.http > CLIENT.56184: Flags [P.], seq 1:121, ack 1490, win 2048, length 120: HTTP: HTTP/1.0 200 OK
E...;.....J...n...n:.P.x...(....P....o..HTTP/1.0 200 OK
Content-Type: text/javascript
Access-Control-Allow-Origin: *
Content-length: 0
Connection: close

........8.?`._.7
非常干净:当我读到这篇文章时,我们发送第一个数据包,它是确认,我们发送第二个等等,最终我们在收到一个愉快的响应后关闭连接。
但是,来自请求的通信也无法正常工作。产生这个的示例代码是:
import requests

headers = {"User-Agent": "test client"}
files = {"binary": ("filename", "file contents", "application/octet-stream")}
data = {"type": "upload"}

requests.post("remote.host.url/index.html", data=data, files=files, headers=headers)
这会产生更脏的输出:
18:24:46.311756 IP CLIENT.56212 > SERVER.http: Flags [P.], seq 1:289, ack 1, win 29200, length 288: HTTP: POST /index.html HTTP/1.1
E..H..@.@.....n:..n....P.9.N..v.P.r.5...POST /index.html HTTP/1.1
Host: SERVER
User-Agent: test client
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 1247
Content-Type: multipart/form-data; boundary=d8a887dda41b5a35f61ccf79b26d7b4e

........^.?`.C..
18:24:46.311772 IP CLIENT.56212 > SERVER.http: Flags [.], seq 289:1313, ack 1, win 29200, length 1024: HTTP
E..(..@.@.....n:..n....P.9.n..v.P.r.8...--d8a887dda41b5a35f61ccf79b26d7b4e
< Data from packet 2 >

........^.?`+Z..
18:24:46.311777 IP CLIENT.56212 > SERVER.http: Flags [P.], seq 1313:1536, ack 1, win 29200, length 223: HTTP
E.....@.@.....n:..n....P.9.n..v.P.r.5`..
< Data from packet 3 >

................
18:24:46.525743 IP SERVER.http > CLIENT.56212: Flags [.], ack 289, win 1760, length 0
E..([D....,%..n...n:.P....v..9.nP....0..................^.?`..
18:24:46.800583 IP CLIENT.56212 > SERVER.http: Flags [.], seq 289:1313, ack 1, win 29200, length 1024: HTTP
E..(..@.@.....n:..n....P.9.n..v.P.r.8...--d8a887dda41b5a35f61ccf79b26d7b4e
< Data from packet 2, again >

........^.?`.../
18:24:46.803014 IP SERVER.http > CLIENT.56212: Flags [.], ack 1313, win 2048, length 0
E..([E....,$..n...n:.P....v..9.nP...................p...^.?`.R
18:24:46.803033 IP CLIENT.56212 > SERVER.http: Flags [P.], seq 1313:1536, ack 1, win 29200, length 223: HTTP
E.....@.@.....n:..n....P.9.n..v.P.r.5`..
< Data from packet 3, again >

.........^.?`k?.
18:24:46.813645 IP SERVER.http > CLIENT.56212: Flags [F.], seq 1, ack 1536, win 1825, length 0
E..([F....,#..n...n:.P....v..9.MP..!....................^.?`h.
18:24:46.813813 IP CLIENT.56212 > SERVER.http: Flags [F.], seq 1536, ack 2, win 29200, length 0
E..(..@.@.....n:..n....P.9.M..v.P.r.4...........^.?`...0
18:24:46.814339 IP SERVER.http > CLIENT.56212: Flags [.], ack 1537, win 1824, length 0
E..([G....,"..n...n:.P....v..9.NP.. ....................^.?`..
18:24:46.816550 IP CLIENT.56214 > SERVER.http: Flags [S], seq 1228421461, win 29200, options [mss 1460,sackOK,TS val 3666736130 ecr 0,nop,wscale 7], length 0
E..<.W@.@.8...n:..n....PI89U......r.4..........
................^.?`0..0....
18:24:46.817006 IP SERVER.http > CLIENT.56214: Flags [S.], seq 416609351, ack 1228421462, win 2048, options [mss 1460], length 0
E..,[H....,...n...n:.P.....GI89V`.......................^.?`..
18:24:46.817021 IP CLIENT.56214 > SERVER.http: Flags [.], ack 1, win 29200, length 0
E..(.X@.@.9...n:..n....PI89V...HP.r.4...........^.?`.0.0
18:24:46.817049 IP CLIENT.56214 > SERVER.http: Flags [P.], seq 1:289, ack 1, win 29200, length 288: HTTP: POST /index.html HTTP/1.1
E..H.Y@.@.7...n:..n....PI89V...HP.r.5...POST /index.html HTTP/1.1
Host: SERVER
User-Agent: test (EPICS base 7.0.4-E3-7.0.4-patch IOC)
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 1247
Content-Type: multipart/form-data; boundary=04a493e5def4d0baf76026663f63ae61

........^.?`.g.0
18:24:46.817063 IP CLIENT.56214 > SERVER.http: Flags [.], seq 289:1313, ack 1, win 29200, length 1024: HTTP
E..(.Z@.@.5...n:..n....PI8:v...HP.r.8...--04a493e5def4d0baf76026663f63ae61
< Data from packet 2, again! >

....p...^.?`.z.0
18:24:46.817068 IP CLIENT.56214 > SERVER.http: Flags [P.], seq 1313:1536, ack 1, win 29200, length 223: HTTP
E....[@.@.8/..n:..n....PI8>v...HP.r.5`..
< Data from packet 3, again! >

etc.
我注意到的第一件事是,在这种情况下,所有三个数据包都在第一个数据包被确认之前发送;之后发送第二个数据包并确认,然后发送第三个数据包。
然而,在这之后,由于某种原因,整个事情又被发送了一次,我们再也没有收到 HTTP/1.0 200 OK。消息以及良好的响应。
我知道两者之间发送的 HTTP header 略有不同,但即使同步这些 header 也不能修复两者之间的通信。我还注意到数据包大小不同,但我无法想象这是一个问题。
我还注意到通过 curl 发送的数据包都有PUSH标志集,但这在 python 端是不一致的。但除此之外,我真的看不出有什么不同。
所以我的问题是:为什么两者的行为不同,我怎样才能让 python 请求模块的行为更像 curl在这种情况下?

最佳答案

Python 的请求不支持“Expect: 100-continue”( [1][2] ),如果您正在与实际上需要 100-continue 来处理大型帖子的服务器进行通信(看起来就是这种情况),您的最好的办法是找到一个支持它的 http 库(例如 libcurl/Pycurl)
手动添加 Expect: 100-continue 可能行不通请求 http-request 的 header ,因为客户端应该发送该 header ,然后等待 100 Continue响应,然后发送正文,但是当只是将 header 添加到请求中时,这并没有神奇地告诉请求它必须“在发送正文之前等待 100-继续响应”,请求将立即发送正文而不等待,所以.. 是的,找到一个实际上原生支持它的 http 库。 (如 libcurl/pycurl)
..如果你能被激怒,如果你去relevant Requests feature request就好了并表达了您的支持。

关于python - 与 curl 一起工作,请求失败;如何修复我的请求代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66472805/

相关文章:

CURL -d 等效于 C

python - 如何向 pycurl 发出 HEAD 请求

python - Ubuntu AWS 上的 Cron 与 Python/Anaconda 虚拟环境

python - 获取包含值的第一个子列表的索引的最快方法

c# - UnityWebRequest如何打印所有Request headers

http - .htaccess 按文件类型限制访问

ruby-on-rails - RVM、Ruby 2.4.1 错误

python - 尝试从Python3中的C函数获取数组

python - Matplotlib 在绘图时将最后一个点连接到第一个点

http - YQL 能否解析需要基于 cookie 的身份验证的网站?