Kaydet (Commit) 97f0c6be authored tarafından Senthil Kumaran's avatar Senthil Kumaran

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

üst be0e177a
...@@ -386,6 +386,12 @@ HTTPConnection Objects ...@@ -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()
......
...@@ -644,11 +644,17 @@ class HTTPConnection: ...@@ -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: ...@@ -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: ...@@ -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)
......
...@@ -947,6 +947,23 @@ class HandlerTests(unittest.TestCase): ...@@ -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()
......
...@@ -163,6 +163,7 @@ class Request: ...@@ -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: ...@@ -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): ...@@ -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): ...@@ -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
......
...@@ -60,6 +60,9 @@ C-API ...@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment