Rev 2541: Rough, working, tested against squid+apache in basic auth fix for #120678 in file:///v/home/vila/src/bugs/120678/

Vincent Ladeuil v.ladeuil+lp at free.fr
Fri Jun 22 08:56:04 BST 2007


At file:///v/home/vila/src/bugs/120678/

------------------------------------------------------------
revno: 2541
revision-id: v.ladeuil+lp at free.fr-20070622075602-1kk9gdry92v2usq3
parent: pqm at pqm.ubuntu.com-20070620092141-cniojlk01bdec2a1
committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
branch nick: 120678
timestamp: Fri 2007-06-22 09:56:02 +0200
message:
  Rough, working, tested against squid+apache in basic auth fix for #120678
  
  * bzrlib/transport/http/_urllib2_wrappers.py:
  (Response.begin): Don't close the connection after a successful
  CONNECT (grr).
  (HTTPConnection.__init__, HTTPSConnection.__init__): Add a
  proxied_host parameter. This is ugly and should be refactored.
  (HTTPSConnection.connect, HTTPSConnection.connect_to_origin):
  Split the httplib.HTTPSConnection so that we can issue a CONNECT
  request *before* swithing the socket to ssl mode.
  (Request.set_proxy): Trap the proxy setting to preserve the
  original host.
  (ConnectRequest): New specific request.
  (ConnectRequest.set_proxy): Trap the proxy setting again to avoid
  losing the original host (ugly).
  (HTTPSHandler.https_request): Wow, we really need https tests,
  https requests common headers were never set :-/
  (HTTPSHandler.https_open): When connecting through a proxy, the
  first request should be a CONNECT to establish the encrypted link.
modified:
  bzrlib/transport/http/_urllib2_wrappers.py _urllib2_wrappers.py-20060913231729-ha9ugi48ktx481ao-1
-------------- next part --------------
=== modified file 'bzrlib/transport/http/_urllib2_wrappers.py'
--- a/bzrlib/transport/http/_urllib2_wrappers.py	2007-04-22 16:32:04 +0000
+++ b/bzrlib/transport/http/_urllib2_wrappers.py	2007-06-22 07:56:02 +0000
@@ -108,6 +108,13 @@
                 if self.debuglevel > 0:
                     print "Consumed body: [%s]" % body
             self.close()
+        elif self.status == 200:
+            # Whatever the request is, it went ok, so we surely don't want to
+            # close the connection. Some cases are not correctly detected by
+            # httplib.HTTPConnection.getresponse (called by
+            # httplib.HTTPResponse.begin). The CONNECT response for the https
+            # through proxy case is one.
+            self.will_close = False
 
 
 # Not inheriting from 'object' because httplib.HTTPConnection doesn't.
@@ -125,16 +132,36 @@
         # Preserve our preciousss
         sock = self.sock
         self.sock = None
+        # Let httplib.HTTPConnection do its housekeeping 
         self.close()
+        # Restore our preciousss
         self.sock = sock
 
 
 class HTTPConnection(AbstractHTTPConnection, httplib.HTTPConnection):
-    pass
+
+    # XXX: Needs refactoring at the caller level.
+    def __init__(self, host, port=None, strict=None, proxied_host=None):
+        httplib.HTTPConnection.__init__(self, host, port, strict)
+        self.proxied_host = proxied_host
 
 
 class HTTPSConnection(AbstractHTTPConnection, httplib.HTTPSConnection):
-    pass
+
+    def __init__(self, host, port=None, key_file=None, cert_file=None,
+                 strict=None, proxied_host=None):
+        httplib.HTTPSConnection.__init__(self, host, port,
+                                         key_file, cert_file, strict)
+        self.proxied_host = proxied_host
+
+    def connect(self):
+        httplib.HTTPConnection.connect(self)
+        if self.proxied_host is None:
+            self.connect_to_origin()
+
+    def connect_to_origin(self):
+        ssl = socket.ssl(self.sock, self.key_file, self.cert_file)
+        self.sock = httplib.FakeSocket(self.sock, ssl)
 
 
 class Request(urllib2.Request):
@@ -169,10 +196,39 @@
         # Some authentication schemes may add more entries.
         self.auth = {}
         self.proxy_auth = {}
+        self.proxied_host = None
 
     def get_method(self):
         return self.method
 
+    def set_proxy(self, proxy, type):
+        self.proxied_host = self.get_host()
+        urllib2.Request.set_proxy(self, proxy, type)
+
+
+class ConnectRequest(Request):
+
+    def __init__(self, request):
+        """Constructor
+        
+        :param request: the first request sent to the proxied host, already
+            processed by the opener (i.e. proxied_host is already set).
+        """
+        # We give a fake url and redefine get_selector or urllib2 will be
+        # confused
+        Request.__init__(self, 'CONNECT', request.get_full_url(),
+                         connection=request.connection)
+        assert request.proxied_host is not None
+        self.proxied_host = request.proxied_host
+
+    def get_selector(self):
+        return self.proxied_host
+
+    def set_proxy(self, proxy, type):
+        # XXX: somthing is wrong here, we should be more precise on when this
+        # is needed.
+        urllib2.Request.set_proxy(self, proxy, type)
+
 
 def extract_credentials(url):
     """Extracts credentials information from url.
@@ -242,9 +298,11 @@
             # handled in the higher levels
             raise errors.InvalidURL(request.get_full_url(), 'no host given.')
 
-        # We create a connection (but it will not connect yet)
+        # We create a connection (but it will not connect until the first
+        # request is made)
         try:
-            connection = http_connection_class(host)
+            connection = http_connection_class(
+                host, proxied_host=request.proxied_host)
         except httplib.InvalidURL, exception:
             # There is only one occurrence of InvalidURL in httplib
             raise errors.InvalidURL(request.get_full_url(),
@@ -498,10 +556,35 @@
 class HTTPSHandler(AbstractHTTPHandler):
     """A custom handler that just thunks into HTTPSConnection"""
 
+    https_request = AbstractHTTPHandler.http_request
+
     def https_open(self, request):
+        connection = request.connection
+        if connection.sock is None and \
+                connection.proxied_host is not None and \
+                request.get_method() != 'CONNECT' : # Don't loop
+            # FIXME: We need a gazillion connection tests here:
+            # - with and without proxy
+            # - with an without authentication
+            # - with basic and digest schemes
+            # - reconnection on errors
+            # - connection persistence behaviour (including reconnection)
+
+            # We are about to connect for the first time via a proxy, we must
+            # issue a CONNECT request first to establish the encrypted link
+            connect = ConnectRequest(request)
+            response = self.parent.open(connect)
+            if response.code != 200:
+                # FIXME: mention hosts involved
+                raise ConnectionError("Can't connect via proxy")
+            # Housekeeping
+            connection.fake_close()
+            # Establish the connection encryption 
+            connection.connect_to_origin()
+            # Propagate the connection to the original request
+            request.connection = connection
         return self.do_open(HTTPSConnection, request)
 
-
 class HTTPRedirectHandler(urllib2.HTTPRedirectHandler):
     """Handles redirect requests.
 



More information about the bazaar-commits mailing list