python3 jsonrpclib 发送两个不同的冲突 Content-type header 字段

python3 jsonrpclib sends two different conclicting Content-type header fields

我一直在将一些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 脚本和网络服务器之间的流量。唯一的区别是 header。使用 python2,thid 是发送到网络服务器的 header;

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

对于python3,header是:

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”header。使用卷曲

构建请求数据包headers,问题是“Content-Type:text/xml”。在没有该内容类型的情况下使用 curl 发送请求,工作正常。

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

部分问题是由于 xmlrpc.py 从 python2 更改为 python3 的方式所致。 send_content() 曾经是添加 content-type header 和 send_request 的那个,只会在 2.

中发送请求

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

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

我想知道这是 python3 jdonrpclib 错误还是 lighthttpd 错误?

What I would like to know is that is this a python3 jdonrpclib bug or a lighthttpd bug?

发送两个(或更多)不同的 Content-Type header 请求绝对是 python library/libraries 交互中的错误。

However the underlying issue of why lighttpd fails when there are repeated content-type is weird.

不,这并不奇怪。 lighttpd 有意拒绝请求中重复的 Content-Type,并且自 lighttpd 1.3.12(2005 年发布)以来一直这样做。在您的无效请求中,重复的 Content-Type header 相互冲突。

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

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

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