我想手动(使用socket和ssl模块)通过使用HTTPS
的代理进行HTTPS
请求。
我可以很好地执行初始的CONNECT
交换:
import ssl, socket
PROXY_ADDR = ("proxy-addr", 443)
CONNECT = "CONNECT example.com:443 HTTP/1.1\r\n\r\n"
sock = socket.create_connection(PROXY_ADDR)
sock = ssl.wrap_socket(sock)
sock.sendall(CONNECT)
s = ""
while s[-4:] != "\r\n\r\n":
s += sock.recv(1)
print repr(s)
以上代码打印了HTTP/1.1 200 Connection established
和一些标头,这是我所期望的。现在我应该准备好发出请求了,例如:
sock.sendall("GET / HTTP/1.1\r\n\r\n")
但是上述代码返回
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
Reason: You're speaking plain HTTP to an SSL-enabled server port.<br />
Instead use the HTTPS scheme to access this URL, please.<br />
</body></html>
这也是有道理的,因为我仍然需要与我正在进行隧道传输的example.com
服务器进行SSL握手。但是,如果我不立即发送GET
请求,而是说
sock = ssl.wrap_socket(sock)
尝试与远程服务器握手时,我遇到了异常:
Traceback (most recent call last):
File "so_test.py", line 18, in <module>
ssl.wrap_socket(sock)
File "/usr/lib/python2.6/ssl.py", line 350, in wrap_socket
suppress_ragged_eofs=suppress_ragged_eofs)
File "/usr/lib/python2.6/ssl.py", line 118, in __init__
self.do_handshake()
File "/usr/lib/python2.6/ssl.py", line 293, in do_handshake
self._sslobj.do_handshake()
ssl.SSLError: [Errno 1] _ssl.c:480: error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol
那么我该如何与远程服务器example.com
进行SSL握手呢?
编辑:我很确定在第二次调用wrap_socket
之前没有可用的额外数据,因为调用sock.recv(1)
会无限期地阻塞。
ssl.wrap_socket
关心套接字连接状态。通常你会创建套接字,然后包装它,然后连接。在这里,你先创建套接字,连接,然后再包装。也许ssl只是被已经连接的底层套接字状态所困惑了。https://github.com/kennethreitz/requests/blob/598f977df4f52b1d778a40cf4243dd93e486a58a/requests/packages/urllib3/contrib/pyopenssl.py#L333 - Dima Tisnek