Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 97f0c6b

Browse files
committed
Fixed Issue1424152 in Py3k: urllib2 fails with HTTPS over Proxy.
1 parent be0e177 commit 97f0c6b

5 files changed

Lines changed: 68 additions & 3 deletions

File tree

Doc/library/http.client.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,12 @@ HTTPConnection Objects
386386

387387
.. versionadded:: 2.7
388388

389+
.. method:: HTTPConnection.set_tunnel(host, port=None)
390+
391+
Set the host and the port for HTTP Connect Tunnelling. Normally used when it
392+
is required to a HTTPS Connection through a proxy server.
393+
394+
.. versionadded:: 3.1
389395

390396
.. method:: HTTPConnection.connect()
391397

Lib/http/client.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,11 +644,17 @@ def __init__(self, host, port=None, strict=None,
644644
self.__response = None
645645
self.__state = _CS_IDLE
646646
self._method = None
647+
self._tunnel_host = None
648+
self._tunnel_port = None
647649

648650
self._set_hostport(host, port)
649651
if strict is not None:
650652
self.strict = strict
651653

654+
def set_tunnel(self, host, port=None):
655+
self._tunnel_host = host
656+
self._tunnel_port = port
657+
652658
def _set_hostport(self, host, port):
653659
if port is None:
654660
i = host.rfind(':')
@@ -669,10 +675,29 @@ def _set_hostport(self, host, port):
669675
def set_debuglevel(self, level):
670676
self.debuglevel = level
671677

678+
def _tunnel(self):
679+
self._set_hostport(self._tunnel_host, self._tunnel_port)
680+
connect_str = "CONNECT %s:%d HTTP/1.0\r\n\r\n" %(self.host, self.port)
681+
connect_bytes = connect_str.encode("ascii")
682+
self.send(connect_bytes)
683+
response = self.response_class(self.sock, strict = self.strict,
684+
method= self._method)
685+
(version, code, message) = response._read_status()
686+
if code != 200:
687+
self.close()
688+
raise socket.error("Tunnel connection failed: %d %s" % (code,
689+
message.strip()))
690+
while True:
691+
line = response.fp.readline()
692+
if line == b'\r\n':
693+
break
694+
672695
def connect(self):
673696
"""Connect to the host and port specified in __init__."""
674697
self.sock = socket.create_connection((self.host,self.port),
675698
self.timeout)
699+
if self._tunnel_host:
700+
self._tunnel()
676701

677702
def close(self):
678703
"""Close the connection to the HTTP server."""
@@ -1008,6 +1033,11 @@ def connect(self):
10081033

10091034
sock = socket.create_connection((self.host, self.port),
10101035
self.timeout)
1036+
1037+
if self._tunnel_host:
1038+
self.sock = sock
1039+
self._tunnel()
1040+
10111041
self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file)
10121042

10131043

Lib/test/test_urllib2.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -947,6 +947,23 @@ def test_proxy(self):
947947
self.assertEqual([(handlers[0], "http_open")],
948948
[tup[0:2] for tup in o.calls])
949949

950+
def test_proxy_https(self):
951+
o = OpenerDirector()
952+
ph = urllib.request.ProxyHandler(dict(https="proxy.example.com:3128"))
953+
o.add_handler(ph)
954+
meth_spec = [
955+
[("https_open", "return response")]
956+
]
957+
handlers = add_ordered_mock_handlers(o, meth_spec)
958+
959+
req = Request("https://www.example.com/")
960+
self.assertEqual(req.get_host(), "www.example.com")
961+
r = o.open(req)
962+
self.assertEqual(req.get_host(), "proxy.example.com:3128")
963+
self.assertEqual([(handlers[0], "https_open")],
964+
[tup[0:2] for tup in o.calls])
965+
966+
950967
def test_basic_auth(self, quote_char='"'):
951968
opener = OpenerDirector()
952969
password_manager = MockPasswordManager()

Lib/urllib/request.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ def __init__(self, url, data=None, headers={},
163163
self.full_url = unwrap(url)
164164
self.data = data
165165
self.headers = {}
166+
self._tunnel_host = None
166167
for key, value in headers.items():
167168
self.add_header(key, value)
168169
self.unredirected_hdrs = {}
@@ -218,8 +219,12 @@ def get_origin_req_host(self):
218219
# End deprecated methods
219220

220221
def set_proxy(self, host, type):
221-
self.host, self.type = host, type
222-
self.selector = self.full_url
222+
if self.type == 'https' and not self._tunnel_host:
223+
self._tunnel_host = self.host
224+
else:
225+
self.type= type
226+
self.selector = self.full_url
227+
self.host = host
223228

224229
def has_proxy(self):
225230
return self.selector == self.full_url
@@ -659,7 +664,7 @@ def proxy_open(self, req, proxy, type):
659664
req.add_header('Proxy-authorization', 'Basic ' + creds)
660665
hostport = unquote(hostport)
661666
req.set_proxy(hostport, proxy_type)
662-
if orig_type == proxy_type:
667+
if orig_type == proxy_type or orig_type == 'https':
663668
# let other handlers take care of it
664669
return None
665670
else:
@@ -1041,6 +1046,10 @@ def do_open(self, http_class, req):
10411046
# request.
10421047
headers["Connection"] = "close"
10431048
headers = dict((name.title(), val) for name, val in headers.items())
1049+
1050+
if req._tunnel_host:
1051+
h.set_tunnel(req._tunnel_host)
1052+
10441053
try:
10451054
h.request(req.get_method(), req.selector, req.data, headers)
10461055
r = h.getresponse() # an HTTPResponse instance

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ C-API
6060
Library
6161
-------
6262

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

0 commit comments

Comments
 (0)