请求库在HTTPS代理连接上强制使用HTTP/1.1

2024-09-30 12:20:47 发布

您现在位置:Python中文网/ 问答频道 /正文

我遇到了一个行为失常的HTTP代理服务器的问题。不幸的是,我无法控制代理服务器——它是IBM的“企业”产品。代理服务器是用于软件测试的服务虚拟化解决方案的一部分。在

基本问题(我认为*)是代理服务器发回HTTP/1.0响应。我可以从soapui(一个Java应用程序)和从命令行curl让它正常工作,但是Python拒绝连接。据我所知,Python的行为正常,其他两个则不正常,因为服务器需要HTTP/1.1响应(它至少希望主机头将服务请求路由到给定的存根)。在

有没有一种方法可以获取请求,或者底层的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 U Proxy环境变量配置代理)

调试输出错误之前,请注意HTTP/1.0:

^{pr2}$

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  .......PV.@D..E.
        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还清楚地显示了主机头和htp1.1。在

*我不能完全确定这是根本问题,因为我无法测试它。我确实看到了HTTP/1.0响应,可以看出我的非工作Python代码发送connecthttp/1.0消息,而工作的Java发送HTTP/1.1消息,Curl也是。有可能这个问题与此无关(尽管我觉得不太可能),或者Python行为不端,而不是Java/curl。我只是不知道足够的东西来确定。在

那么,有没有办法强制urllib3/requests始终使用httpv1.1?在


Tags: tocomhttphostloggingjavacurlrequests
2条回答

httplibwhich ^{} relies upon for HTTP(S) heavy lifting)始终使用HTTP/1.0和{}:

^{}

def _tunnel(self):
    self.send("CONNECT %s:%d HTTP/1.0\r\n" % (self._tunnel_host,
        self._tunnel_port))
    for header, value in self._tunnel_headers.iteritems():
        self.send("%s: %s\r\n" % (header, value))
    self.send("\r\n")
    <...>

所以除了编辑子例程之外,您不能在这里“强制”它使用“HTTP/1.1”。在


如果代理不支持HTTP/1.0,这可能就是问题所在—特别是,1.0不需要Host:头,而且实际上,通过将日志输出与上面的代码进行比较可以看出,httplib不发送它。While, in verity, a proxy may expect it regardless。但是如果是这种情况,您应该已经从代理或其他响应CONNECT的错误中获得了一个错误,除非代理是如此的混乱以至于它用一些默认值(或垃圾)代替Host:,无论如何都返回{},并试图连接到上帝知道的地方,此时您会超时。在

您可以使httplib添加要连接的Host:头,方法是将其添加到_tunnel_headers(间接):

^{2}$

如果不依赖于请求库,您可能会发现以下代码段很有用:

import http.client conn = http.client.HTTPSConnection("proxy.domain.lu", 8080) conn.set_tunnel("www.domain.org", 443, headers={'User-Agent': 'curl/7.56.0'}) conn.request("GET", "/api") response = conn.getresponse() print( response.read() )

和13;
和13;

相关问题 更多 >

    热门问题