我遇到了 HTTP 代理服务器行为异常的问题。不幸的是,我无法控制代理服务器——它是 IBM 的“企业”产品。代理服务器是用于软件测试的服务虚拟化解决方案的一部分。
根本问题(我认为*)是代理服务器发回 HTTP/1.0 响应。我可以从 SOAP UI(一个 Java 应用程序)让它正常工作,并从命令行执行curl,但 Python 拒绝连接。据我所知,Python 的行为正确,而其他两个则不然,因为服务器期望 HTTP/1.1 响应(它至少需要 Host 标头将服务请求路由到给定的存根)。
有没有办法让Requests,或者底层的urllib3,或者更远的http lib始终使用http1.1,即使另一端似乎使用1.0?
下面是一个示例程序(不幸的是,它需要您安装带有 RTCP 的 IBM Ration Integration Tester 才能真正复制)来重现该问题:
import http.client as http_client
http_client.HTTPConnection.debuglevel = 1
import logging
import requests
logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG)
requests_log = logging.getLogger("requests.packages.urllib3")
requests_log.setLevel(logging.DEBUG)
requests_log.propagate = True
requests.post("https://host:8443/axl",
headers={"soapAction": '"CUCM:DB ver=9.1 updateSipTrunk"'},
data='<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tns="http://www.cisco.com/AXL/API/9.1"><soapenv:Header/><soapenv:Body><tns:updateSipTrunk><name>PLACEHOLDER</name><newName>PLACEHOLDER</newName><destinations><destination><addressIpv4>10.10.1.5</addressIpv4><sortOrder>1</sortOrder></destination></destinations></tns:updateSipTrunk></soapenv:Body></soapenv:Envelope>',
verify=False)
(代理是通过 HTTPS_PROXY 环境变量配置的)
调试错误前的输出,注意HTTP/1.0:
INFO:requests.packages.urllib3.connectionpool:Starting new HTTPS connection (1): host.com
send: b'CONNECT host.com:8443 HTTP/1.0\r\n'
send: b'\r\n'
header: Host: host.com:8443
header: Proxy-agent: Green Hat HTTPS Proxy/1.0
RHEL 6 中出现的确切错误文本是:
requests.exceptions.SSLError: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:646)
尽管此处显示了主机标头,但它不会显示在线路上。我用 tcpdump 确认了这一点:
14:03:14.315049 IP sourcehost.53214 > desthost.com: Flags [P.], seq 0:32, ack 1, win 115, options [nop,nop,TS val 2743933964 ecr 4116114841], length 32
0x0000: 0000 0c07 ac00 0050 56b5 4044 0800 4500 [email protected] /cdn-cgi/l/email-protection.
0x0010: 0054 3404 4000 4006 2ca0 0af8 3f15 0afb .T4.@.@.,...?...
0x0020: 84f8 cfde 0c7f a4f8 280a 4ebd b425 8018 ........(.N..%..
0x0030: 0073 da46 0000 0101 080a a38d 1c0c f556 .s.F...........V
0x0040: XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX ..CONNECT.host
0x0050: XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX xx:8443.HTTP/1.0
0x0060: 0d0a
当我用 verbose 卷曲它时,输出如下所示:
* About to connect() to proxy proxy-host.com port 3199 (#0)
* Trying 10.**.**.** ... connected
* Connected to proxy-host.com (10.**.**.**) port 3199 (#0)
* Establish HTTP proxy tunnel to host.com:8443
> CONNECT host.com:8443 HTTP/1.1
> Host: host.com:8443
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Proxy-Connection: Keep-Alive
> soapAction: "CUCM:DB ver=9.1 updateSipTrunk"
>
< HTTP/1.0 200 OK
< Host: host.com:8443
< Proxy-agent: Green Hat HTTPS Proxy/1.0
<
* Proxy replied OK to CONNECT request
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* CAfile: /path/to/store/ca-bundle.crt
CApath: none
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
在此点之后被截断。连接后您可以看到来自代理的 HTTP/1.0 响应。 curl 的 tcpdump 还清楚地显示了主机标头以及 HTTP 1.1。
*我不能完全确定这是根本问题,因为我无法测试它。我确实看到了 HTTP/1.0 响应,并且可以看出我的非工作 Python 代码发送 CONNECT HTTP/1.0 消息,而工作 Java 发送 HTTP/1.1 消息,Curl 也是如此。问题可能是不相关的(尽管我发现不太可能)或者 Python 行为不当,而不是 Java/curl。我只是了解不够,无法确定。
那么,有没有办法强制 urllib3/requests 始终使用 HTTP v1.1 呢?