[Python-checkins] r74198 - in python/branches/py3k: Doc/library/http.client.rst Lib/http/client.py Lib/test/test_urllib2.py Lib/urllib/request.py Misc/NEWS

senthil.kumaran python-checkins at python.org
Sat Jul 25 06:24:38 CEST 2009


Author: senthil.kumaran
Date: Sat Jul 25 06:24:38 2009
New Revision: 74198

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



Modified:
   python/branches/py3k/Doc/library/http.client.rst
   python/branches/py3k/Lib/http/client.py
   python/branches/py3k/Lib/test/test_urllib2.py
   python/branches/py3k/Lib/urllib/request.py
   python/branches/py3k/Misc/NEWS

Modified: python/branches/py3k/Doc/library/http.client.rst
==============================================================================
--- python/branches/py3k/Doc/library/http.client.rst	(original)
+++ python/branches/py3k/Doc/library/http.client.rst	Sat Jul 25 06:24:38 2009
@@ -386,6 +386,12 @@
 
    .. 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()
 

Modified: python/branches/py3k/Lib/http/client.py
==============================================================================
--- python/branches/py3k/Lib/http/client.py	(original)
+++ python/branches/py3k/Lib/http/client.py	Sat Jul 25 06:24:38 2009
@@ -644,11 +644,17 @@
         self.__response = None
         self.__state = _CS_IDLE
         self._method = None
+        self._tunnel_host = None
+        self._tunnel_port = None
 
         self._set_hostport(host, port)
         if strict is not None:
             self.strict = strict
 
+    def set_tunnel(self, host, port=None):
+        self._tunnel_host = host
+        self._tunnel_port = port
+
     def _set_hostport(self, host, port):
         if port is None:
             i = host.rfind(':')
@@ -669,10 +675,29 @@
     def set_debuglevel(self, 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):
         """Connect to the host and port specified in __init__."""
         self.sock = socket.create_connection((self.host,self.port),
                                              self.timeout)
+        if self._tunnel_host:
+            self._tunnel()
 
     def close(self):
         """Close the connection to the HTTP server."""
@@ -1008,6 +1033,11 @@
 
             sock = socket.create_connection((self.host, self.port),
                                             self.timeout)
+
+            if self._tunnel_host:
+                self.sock = sock
+                self._tunnel()
+
             self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file)
 
 

Modified: python/branches/py3k/Lib/test/test_urllib2.py
==============================================================================
--- python/branches/py3k/Lib/test/test_urllib2.py	(original)
+++ python/branches/py3k/Lib/test/test_urllib2.py	Sat Jul 25 06:24:38 2009
@@ -947,6 +947,23 @@
         self.assertEqual([(handlers[0], "http_open")],
                          [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='"'):
         opener = OpenerDirector()
         password_manager = MockPasswordManager()

Modified: python/branches/py3k/Lib/urllib/request.py
==============================================================================
--- python/branches/py3k/Lib/urllib/request.py	(original)
+++ python/branches/py3k/Lib/urllib/request.py	Sat Jul 25 06:24:38 2009
@@ -163,6 +163,7 @@
         self.full_url = unwrap(url)
         self.data = data
         self.headers = {}
+        self._tunnel_host = None
         for key, value in headers.items():
             self.add_header(key, value)
         self.unredirected_hdrs = {}
@@ -218,8 +219,12 @@
     # End deprecated methods
 
     def set_proxy(self, host, type):
-        self.host, self.type = host, type
-        self.selector = self.full_url
+        if self.type == 'https' and not self._tunnel_host:
+            self._tunnel_host = self.host
+        else:
+            self.type= type
+            self.selector = self.full_url
+        self.host = host
 
     def has_proxy(self):
         return self.selector == self.full_url
@@ -659,7 +664,7 @@
             req.add_header('Proxy-authorization', 'Basic ' + creds)
         hostport = unquote(hostport)
         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
             return None
         else:
@@ -1041,6 +1046,10 @@
         # request.
         headers["Connection"] = "close"
         headers = dict((name.title(), val) for name, val in headers.items())
+
+        if req._tunnel_host:
+            h.set_tunnel(req._tunnel_host)
+
         try:
             h.request(req.get_method(), req.selector, req.data, headers)
             r = h.getresponse()  # an HTTPResponse instance

Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS	(original)
+++ python/branches/py3k/Misc/NEWS	Sat Jul 25 06:24:38 2009
@@ -60,6 +60,9 @@
 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
   loaders that allow for modules to be executed. Both importlib.abc.PyLoader
   and PyPycLoader inherit from this class and provide implementations in


More information about the Python-checkins mailing list