Fixed Issue1424152 in Py3k: urllib2 fails with HTTPS over Proxy.

This commit is contained in:
Senthil Kumaran 2009-07-25 04:24:38 +00:00
parent be0e177ae5
commit 97f0c6be46
5 changed files with 68 additions and 3 deletions

View file

@ -386,6 +386,12 @@ HTTPConnection Objects
.. versionadded:: 2.7 .. versionadded:: 2.7
.. method:: HTTPConnection.set_tunnel(host, port=None)
Set the host and the port for HTTP Connect Tunnelling. Normally used when it
is required to a HTTPS Connection through a proxy server.
.. versionadded:: 3.1
.. method:: HTTPConnection.connect() .. method:: HTTPConnection.connect()

View file

@ -644,11 +644,17 @@ class HTTPConnection:
self.__response = None self.__response = None
self.__state = _CS_IDLE self.__state = _CS_IDLE
self._method = None self._method = None
self._tunnel_host = None
self._tunnel_port = None
self._set_hostport(host, port) self._set_hostport(host, port)
if strict is not None: if strict is not None:
self.strict = strict self.strict = strict
def set_tunnel(self, host, port=None):
self._tunnel_host = host
self._tunnel_port = port
def _set_hostport(self, host, port): def _set_hostport(self, host, port):
if port is None: if port is None:
i = host.rfind(':') i = host.rfind(':')
@ -669,10 +675,29 @@ class HTTPConnection:
def set_debuglevel(self, level): def set_debuglevel(self, level):
self.debuglevel = level self.debuglevel = level
def _tunnel(self):
self._set_hostport(self._tunnel_host, self._tunnel_port)
connect_str = "CONNECT %s:%d HTTP/1.0\r\n\r\n" %(self.host, self.port)
connect_bytes = connect_str.encode("ascii")
self.send(connect_bytes)
response = self.response_class(self.sock, strict = self.strict,
method= self._method)
(version, code, message) = response._read_status()
if code != 200:
self.close()
raise socket.error("Tunnel connection failed: %d %s" % (code,
message.strip()))
while True:
line = response.fp.readline()
if line == b'\r\n':
break
def connect(self): def connect(self):
"""Connect to the host and port specified in __init__.""" """Connect to the host and port specified in __init__."""
self.sock = socket.create_connection((self.host,self.port), self.sock = socket.create_connection((self.host,self.port),
self.timeout) self.timeout)
if self._tunnel_host:
self._tunnel()
def close(self): def close(self):
"""Close the connection to the HTTP server.""" """Close the connection to the HTTP server."""
@ -1008,6 +1033,11 @@ else:
sock = socket.create_connection((self.host, self.port), sock = socket.create_connection((self.host, self.port),
self.timeout) self.timeout)
if self._tunnel_host:
self.sock = sock
self._tunnel()
self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file) self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file)

View file

@ -947,6 +947,23 @@ class HandlerTests(unittest.TestCase):
self.assertEqual([(handlers[0], "http_open")], self.assertEqual([(handlers[0], "http_open")],
[tup[0:2] for tup in o.calls]) [tup[0:2] for tup in o.calls])
def test_proxy_https(self):
o = OpenerDirector()
ph = urllib.request.ProxyHandler(dict(https="proxy.example.com:3128"))
o.add_handler(ph)
meth_spec = [
[("https_open", "return response")]
]
handlers = add_ordered_mock_handlers(o, meth_spec)
req = Request("https://www.example.com/")
self.assertEqual(req.get_host(), "www.example.com")
r = o.open(req)
self.assertEqual(req.get_host(), "proxy.example.com:3128")
self.assertEqual([(handlers[0], "https_open")],
[tup[0:2] for tup in o.calls])
def test_basic_auth(self, quote_char='"'): def test_basic_auth(self, quote_char='"'):
opener = OpenerDirector() opener = OpenerDirector()
password_manager = MockPasswordManager() password_manager = MockPasswordManager()

View file

@ -163,6 +163,7 @@ class Request:
self.full_url = unwrap(url) self.full_url = unwrap(url)
self.data = data self.data = data
self.headers = {} self.headers = {}
self._tunnel_host = None
for key, value in headers.items(): for key, value in headers.items():
self.add_header(key, value) self.add_header(key, value)
self.unredirected_hdrs = {} self.unredirected_hdrs = {}
@ -218,8 +219,12 @@ class Request:
# End deprecated methods # End deprecated methods
def set_proxy(self, host, type): def set_proxy(self, host, type):
self.host, self.type = host, type if self.type == 'https' and not self._tunnel_host:
self.selector = self.full_url self._tunnel_host = self.host
else:
self.type= type
self.selector = self.full_url
self.host = host
def has_proxy(self): def has_proxy(self):
return self.selector == self.full_url return self.selector == self.full_url
@ -659,7 +664,7 @@ class ProxyHandler(BaseHandler):
req.add_header('Proxy-authorization', 'Basic ' + creds) req.add_header('Proxy-authorization', 'Basic ' + creds)
hostport = unquote(hostport) hostport = unquote(hostport)
req.set_proxy(hostport, proxy_type) req.set_proxy(hostport, proxy_type)
if orig_type == proxy_type: if orig_type == proxy_type or orig_type == 'https':
# let other handlers take care of it # let other handlers take care of it
return None return None
else: else:
@ -1041,6 +1046,10 @@ class AbstractHTTPHandler(BaseHandler):
# request. # request.
headers["Connection"] = "close" headers["Connection"] = "close"
headers = dict((name.title(), val) for name, val in headers.items()) headers = dict((name.title(), val) for name, val in headers.items())
if req._tunnel_host:
h.set_tunnel(req._tunnel_host)
try: try:
h.request(req.get_method(), req.selector, req.data, headers) h.request(req.get_method(), req.selector, req.data, headers)
r = h.getresponse() # an HTTPResponse instance r = h.getresponse() # an HTTPResponse instance

View file

@ -60,6 +60,9 @@ C-API
Library Library
------- -------
- Issue #1424152: Fix for httplib, urllib2 to support SSL while working through
proxy. Original patch by Christopher Li, changes made by Senthil Kumaran
- Add importlib.abc.ExecutionLoader to represent the PEP 302 protocol for - Add importlib.abc.ExecutionLoader to represent the PEP 302 protocol for
loaders that allow for modules to be executed. Both importlib.abc.PyLoader loaders that allow for modules to be executed. Both importlib.abc.PyLoader
and PyPycLoader inherit from this class and provide implementations in and PyPycLoader inherit from this class and provide implementations in