Patchwork D2722: url: add HTTP handler that uses a proxied socket

mail settings
Submitter phabricator
Date March 8, 2018, 5:31 a.m.
Message ID <>
Download mbox | patch
Permalink /patch/29115/
State Superseded
Headers show


phabricator - March 8, 2018, 5:31 a.m.
indygreg created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

  Now that we have a socket proxy that can log I/O, we need to teach
  mechanisms that open URLs how to use.
  We invent a custom HTTP handler class that knows how to proxy
  sockets as soon as they are opened. We teach the high-level
  opener() to accept logging arguments so a logging HTTP handler
  can be constructed.
  1. no-check-commit because we must name http_open that way

  rHG Mercurial




To: indygreg, #hg-reviewers
Cc: mercurial-devel
phabricator - March 8, 2018, 1:38 p.m.
Michaelexics added a comment.

  Romantic Piano by Andrew_Studio | AudioJungle

  rHG Mercurial


To: indygreg, #hg-reviewers
Cc: Michaelexics, Jeffreydoomb, mercurial-devel


diff --git a/mercurial/ b/mercurial/
--- a/mercurial/
+++ b/mercurial/
@@ -296,6 +296,34 @@ 
         _generic_start_transaction(self, h, req)
         return keepalive.HTTPHandler._start_transaction(self, h, req)
+class logginghttpconnection(keepalive.HTTPConnection):
+    def __init__(self, createconn, *args, **kwargs):
+        keepalive.HTTPConnection.__init__(self, *args, **kwargs)
+        self._create_connection = createconn
+class logginghttphandler(httphandler):
+    """HTTP handler that logs socket I/O."""
+    def __init__(self, logfh, name, observeropts):
+        super(logginghttphandler, self).__init__()
+        self._logfh = logfh
+        self._logname = name
+        self._observeropts = observeropts
+    # do_open() calls the passed class to instantiate an HTTPConnection. We
+    # pass in a callable method that creates a custom HTTPConnection instance
+    # whose callback to create the socket knows how to proxy the socket.
+    def http_open(self, req):
+        return self.do_open(self._makeconnection, req)
+    def _makeconnection(self, *args, **kwargs):
+        def createconnection(*args, **kwargs):
+            sock = socket.create_connection(*args, **kwargs)
+            return util.makeloggingsocket(self._logfh, sock, self._logname,
+                                          **self._observeropts)
+        return logginghttpconnection(createconnection, *args, **kwargs)
 if has_https:
     class httpsconnection(httplib.HTTPConnection):
         response_class = keepalive.HTTPResponse
@@ -465,14 +493,32 @@ 
 handlerfuncs = []
-def opener(ui, authinfo=None, useragent=None):
+def opener(ui, authinfo=None, useragent=None, loggingfh=None,
+           loggingname=b's', loggingopts=None):
     construct an opener suitable for urllib2
     authinfo will be added to the password manager
+    The opener can be configured to log socket events if the various
+    ``logging*`` arguments are specified.
+    ``loggingfh`` denotes a file object to log events to.
+    ``loggingname`` denotes the name of the to print when logging.
+    ``loggingopts`` is a dict of keyword arguments to pass to the constructed
+    ``util.socketobserver`` instance.
-    handlers = [httphandler()]
-    if has_https:
-        handlers.append(httpshandler(ui))
+    handlers = []
+    if loggingfh:
+        handlers.append(logginghttphandler(loggingfh, loggingname,
+                                           loggingopts or {}))
+        # We don't yet support HTTPS when logging I/O. If we attempt to open
+        # an HTTPS URL, we'll likely fail due to unknown protocol.
+    else:
+        handlers.append(httphandler())
+        if has_https:
+            handlers.append(httpshandler(ui))