changeset 40829:6509fcec830c

url: allow to configure timeout on http connection By default, httplib.HTTPConnection opens connection with no timeout. If the server is hanging, Mercurial will wait indefinitely. This may be an issue for automated scripts. Differential Revision: https://phab.mercurial-scm.org/D4878
author Cédric Krier <ced@b2ck.com>
date Thu, 04 Oct 2018 11:28:48 +0200
parents 208303a8172c
children ccf4d808ec4c
files mercurial/configitems.py mercurial/help/config.txt mercurial/keepalive.py mercurial/url.py
diffstat 4 files changed, 25 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/configitems.py
+++ b/mercurial/configitems.py
@@ -737,6 +737,11 @@
 coreconfigitem('http_proxy', 'user',
     default=None,
 )
+
+coreconfigitem('http', 'timeout',
+    default=None,
+)
+
 coreconfigitem('logtoprocess', 'commandexception',
     default=None,
 )
--- a/mercurial/help/config.txt
+++ b/mercurial/help/config.txt
@@ -1322,6 +1322,15 @@
     Optional. Always use the proxy, even for localhost and any entries
     in ``http_proxy.no``. (default: False)
 
+``http``
+----------
+
+Used to configure access to Mercurial repositories via HTTP.
+
+``timeout``
+    If set, blocking operations will timeout after that many seconds.
+    (default: None)
+
 ``merge``
 ---------
 
--- a/mercurial/keepalive.py
+++ b/mercurial/keepalive.py
@@ -172,8 +172,9 @@
             return dict(self._hostmap)
 
 class KeepAliveHandler(object):
-    def __init__(self):
+    def __init__(self, timeout=None):
         self._cm = ConnectionManager()
+        self._timeout = timeout
         self.requestscount = 0
         self.sentbytescount = 0
 
@@ -234,7 +235,7 @@
                 h = self._cm.get_ready_conn(host)
             else:
                 # no (working) free connections were found.  Create a new one.
-                h = http_class(host)
+                h = http_class(host, timeout=self._timeout)
                 if DEBUG:
                     DEBUG.info("creating new connection to %s (%d)",
                                host, id(h))
--- a/mercurial/url.py
+++ b/mercurial/url.py
@@ -317,8 +317,8 @@
 
 class logginghttphandler(httphandler):
     """HTTP handler that logs socket I/O."""
-    def __init__(self, logfh, name, observeropts):
-        super(logginghttphandler, self).__init__()
+    def __init__(self, logfh, name, observeropts, timeout=None):
+        super(logginghttphandler, self).__init__(timeout=timeout)
 
         self._logfh = logfh
         self._logname = name
@@ -365,8 +365,8 @@
             sslutil.validatesocket(self.sock)
 
     class httpshandler(keepalive.KeepAliveHandler, urlreq.httpshandler):
-        def __init__(self, ui):
-            keepalive.KeepAliveHandler.__init__(self)
+        def __init__(self, ui, timeout=None):
+            keepalive.KeepAliveHandler.__init__(self, timeout=timeout)
             urlreq.httpshandler.__init__(self)
             self.ui = ui
             self.pwmgr = passwordmgr(self.ui,
@@ -525,18 +525,19 @@
     ``sendaccept`` allows controlling whether the ``Accept`` request header
     is sent. The header is sent by default.
     '''
+    timeout = ui.configwith(float, 'http', 'timeout')
     handlers = []
 
     if loggingfh:
         handlers.append(logginghttphandler(loggingfh, loggingname,
-                                           loggingopts or {}))
+                                           loggingopts or {}, timeout=timeout))
         # 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())
+        handlers.append(httphandler(timeout=timeout))
         if has_https:
-            handlers.append(httpshandler(ui))
+            handlers.append(httpshandler(ui, timeout=timeout))
 
     handlers.append(proxyhandler(ui))