python3 jsonrpclib 发送两个不同的冲突内容类型头字段

Posted

技术标签:

【中文标题】python3 jsonrpclib 发送两个不同的冲突内容类型头字段【英文标题】:python3 jsonrpclib sends two different conclicting Content-type header fields 【发布时间】:2021-08-09 22:06:05 【问题描述】:

我一直在将一些 Python 2 脚本升级到 Python 3。我使用 2to3 来重构代码。使用 python3 运行,我得到一个异常。只需三行代码,我就能重现问题;

import jsonrpclib
p = jsonrpclib.Server("http://r195/cgi-bin/streamscape_api")
print(p.nodeid())

使用 python2,它可以工作:

$ python rpc.py 
[u'3011']

当我用 Python3 运行完全相同的代码时,我得到了这个异常:

$ python3 rpc.py 
Traceback (most recent call last):
 File "rpc.py", line 6, in <module>
  print(p.nodeid())
 File "/home/kory/.local/lib/python3.8/site-packages/jsonrpclib/jsonrpc.py", line 265, in __call__
  return self.__send(self.__name, kwargs)
 File "/home/kory/.local/lib/python3.8/site-packages/jsonrpclib/jsonrpc.py", line 212, in _request
  response = self._run_request(request)
 File "/home/kory/.local/lib/python3.8/site-packages/jsonrpclib/jsonrpc.py", line 226, in _run_request
  response = self.__transport.request(
 File "/usr/lib/python3.8/xmlrpc/client.py", line 1153, in request
  return self.single_request(host, handler, request_body, verbose)
 File "/usr/lib/python3.8/xmlrpc/client.py", line 1183, in single_request
  raise ProtocolError(
xmlrpc.client.ProtocolError: <ProtocolError for r195/cgi-bin/streamscape_api: 400 Bad Request>

使用 WireShark,我捕获了 python 脚本和网络服务器之间的流量。唯一的区别是 tje 标头。在 python2 中,thid 是发送到网络服务器的标头;

Host: r195
Accept-Encoding: gzip
User-Agent: jsonrpclib/0.1 (Python 2.7.18)
Content-Type: application/json-rpc

使用python3,标题为:

Host: r195
Accept-Encoding: gzip
Content-Type: text/xml
User-Agent: jsonrpclib/0.1 (Python 3.8.10)
Content-Type: application/json-rpc

请注意,python3 发送两个“Content-Type”标头。使用卷曲

构建一个请求包的头部,问题是“Content-Type: text/xml”。使用没有该内容类型的 curl 发送请求,可以正常工作。

为了确定,作为测试,我从 xmlrpc/client.py 中注释掉了 this line,该脚本现在可以与 python3 一起使用。但是注释掉那条线并不是一个可行的解决方案。我相信网络服务器正在运行 lighttpd 1.4.54。

部分问题是由于 xmlrpc.py 从 python2 更改为 python3 的方式。 send_content() 曾经是添加内容类型标头的那个,而 send_request 只会在 2 中发送请求。

在 3 中,xmlrpc 在 send_request() 中添加了 content-type,这在语义上是不正确的。因此,当 jsonrpclib 重载 send_content() 时,它会添加额外的 content-type 标头。

这个项目:https://github.com/tcalmant/jsonrpclib/tree/master/jsonrpclib 也通过重载 send_request() 来纠正这个问题,并且不添加 content-type 标头,这在重载的 send_content 中是正确的。所以使用它可以解决这个问题。然而,当有重复的内容类型时,为什么 lighttpd 失败的根本问题很奇怪。如果没有答案,我们可以继续忽略,因为大多数客户不会这样做。

我想知道这是python3 jdonrpclib bug还是lighthttpd bug?

【问题讨论】:

【参考方案1】:

我想知道这是python3 jdonrpclib bug还是lighthttpd bug?

通过请求发送两个(或更多)不同的 Content-Type 标头绝对是 python 库/库交互中的错误。

但是,为什么 lighttpd 在重复的内容类型时失败的根本问题很奇怪。

不,这并不奇怪。 lighttpd 故意拒绝请求中重复的 Content-Type,并且从 lighttpd 1.3.12(2005 年发布)开始就这样做了。那些重复的 Content-Type 标头在您的无效请求中相互冲突。

您可以设置 lighttpd.conf debug.log-request-header-on-error = "enable" 并且 lighttpd 将在 lighttpd 错误日志中针对无效请求报告以下内容:“duplicate Content-Type header -> 400”

[编辑] 供参考: RFC7231 超文本传输​​协议 (HTTP/1.1):语义和内容

RFC 7231 Appendix D. Collected ABNF 定义Content-Type = media-type,允许单个media-type,而不是变量号。因此,规范不允许重复的 Content-Type 标头。

【讨论】:

以上是关于python3 jsonrpclib 发送两个不同的冲突内容类型头字段的主要内容,如果未能解决你的问题,请参考以下文章

python3/syslog:多个系统日志流?

python3套接字发送接收'字节'对象没有属性'读取'

如何使用 python 3x 从两个单独的运行 python 文件发送数据

Python3网络爬虫:利用urllib.urlopen向有道翻译发送数据获得翻译结果

python3:利用smtplib库和smtp.qq.com邮件服务器发送邮件

Python3 & TCP协议和UDP协议的特点和区别