mercurial/url.py
changeset 14244 e7525a555a64
parent 14204 5fa21960b2f4
child 14964 376c32a5ccdc
child 15005 4a43e23b8c55
equal deleted inserted replaced
14243:861f28212398 14244:e7525a555a64
     6 #
     6 #
     7 # This software may be used and distributed according to the terms of the
     7 # This software may be used and distributed according to the terms of the
     8 # GNU General Public License version 2 or any later version.
     8 # GNU General Public License version 2 or any later version.
     9 
     9 
    10 import urllib, urllib2, httplib, os, socket, cStringIO
    10 import urllib, urllib2, httplib, os, socket, cStringIO
    11 import __builtin__
       
    12 from i18n import _
    11 from i18n import _
    13 import keepalive, util, sslutil
    12 import keepalive, util, sslutil
    14 
    13 import httpconnection as httpconnectionmod
    15 def readauthforuri(ui, uri):
       
    16     # Read configuration
       
    17     config = dict()
       
    18     for key, val in ui.configitems('auth'):
       
    19         if '.' not in key:
       
    20             ui.warn(_("ignoring invalid [auth] key '%s'\n") % key)
       
    21             continue
       
    22         group, setting = key.rsplit('.', 1)
       
    23         gdict = config.setdefault(group, dict())
       
    24         if setting in ('username', 'cert', 'key'):
       
    25             val = util.expandpath(val)
       
    26         gdict[setting] = val
       
    27 
       
    28     # Find the best match
       
    29     scheme, hostpath = uri.split('://', 1)
       
    30     bestlen = 0
       
    31     bestauth = None
       
    32     for group, auth in config.iteritems():
       
    33         prefix = auth.get('prefix')
       
    34         if not prefix:
       
    35             continue
       
    36         p = prefix.split('://', 1)
       
    37         if len(p) > 1:
       
    38             schemes, prefix = [p[0]], p[1]
       
    39         else:
       
    40             schemes = (auth.get('schemes') or 'https').split()
       
    41         if (prefix == '*' or hostpath.startswith(prefix)) and \
       
    42             len(prefix) > bestlen and scheme in schemes:
       
    43             bestlen = len(prefix)
       
    44             bestauth = group, auth
       
    45     return bestauth
       
    46 
    14 
    47 class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm):
    15 class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm):
    48     def __init__(self, ui):
    16     def __init__(self, ui):
    49         urllib2.HTTPPasswordMgrWithDefaultRealm.__init__(self)
    17         urllib2.HTTPPasswordMgrWithDefaultRealm.__init__(self)
    50         self.ui = ui
    18         self.ui = ui
    56         if user and passwd:
    24         if user and passwd:
    57             self._writedebug(user, passwd)
    25             self._writedebug(user, passwd)
    58             return (user, passwd)
    26             return (user, passwd)
    59 
    27 
    60         if not user:
    28         if not user:
    61             res = readauthforuri(self.ui, authuri)
    29             res = httpconnectionmod.readauthforuri(self.ui, authuri)
    62             if res:
    30             if res:
    63                 group, auth = res
    31                 group, auth = res
    64                 user, passwd = auth.get('username'), auth.get('password')
    32                 user, passwd = auth.get('username'), auth.get('password')
    65                 self.ui.debug("using auth.%s.* for authentication\n" % group)
    33                 self.ui.debug("using auth.%s.* for authentication\n" % group)
    66         if not user or not passwd:
    34         if not user or not passwd:
   147                 return baseclass.add_header(self, key, val)
   115                 return baseclass.add_header(self, key, val)
   148         req.__class__ = _request
   116         req.__class__ = _request
   149 
   117 
   150         return urllib2.ProxyHandler.proxy_open(self, req, proxy, type_)
   118         return urllib2.ProxyHandler.proxy_open(self, req, proxy, type_)
   151 
   119 
   152 class httpsendfile(object):
       
   153     """This is a wrapper around the objects returned by python's "open".
       
   154 
       
   155     Its purpose is to send file-like objects via HTTP and, to do so, it
       
   156     defines a __len__ attribute to feed the Content-Length header.
       
   157     """
       
   158 
       
   159     def __init__(self, ui, *args, **kwargs):
       
   160         # We can't just "self._data = open(*args, **kwargs)" here because there
       
   161         # is an "open" function defined in this module that shadows the global
       
   162         # one
       
   163         self.ui = ui
       
   164         self._data = __builtin__.open(*args, **kwargs)
       
   165         self.seek = self._data.seek
       
   166         self.close = self._data.close
       
   167         self.write = self._data.write
       
   168         self._len = os.fstat(self._data.fileno()).st_size
       
   169         self._pos = 0
       
   170         self._total = len(self) / 1024 * 2
       
   171 
       
   172     def read(self, *args, **kwargs):
       
   173         try:
       
   174             ret = self._data.read(*args, **kwargs)
       
   175         except EOFError:
       
   176             self.ui.progress(_('sending'), None)
       
   177         self._pos += len(ret)
       
   178         # We pass double the max for total because we currently have
       
   179         # to send the bundle twice in the case of a server that
       
   180         # requires authentication. Since we can't know until we try
       
   181         # once whether authentication will be required, just lie to
       
   182         # the user and maybe the push succeeds suddenly at 50%.
       
   183         self.ui.progress(_('sending'), self._pos / 1024,
       
   184                          unit=_('kb'), total=self._total)
       
   185         return ret
       
   186 
       
   187     def __len__(self):
       
   188         return self._len
       
   189 
       
   190 def _gen_sendfile(orgsend):
   120 def _gen_sendfile(orgsend):
   191     def _sendfile(self, data):
   121     def _sendfile(self, data):
   192         # send a file
   122         # send a file
   193         if isinstance(data, httpsendfile):
   123         if isinstance(data, httpconnectionmod.httpsendfile):
   194             # if auth required, some data sent twice, so rewind here
   124             # if auth required, some data sent twice, so rewind here
   195             data.seek(0)
   125             data.seek(0)
   196             for chunk in util.filechunkiter(data):
   126             for chunk in util.filechunkiter(data):
   197                 orgsend(self, chunk)
   127                 orgsend(self, chunk)
   198         else:
   128         else:
   410         def _start_transaction(self, h, req):
   340         def _start_transaction(self, h, req):
   411             _generic_start_transaction(self, h, req)
   341             _generic_start_transaction(self, h, req)
   412             return keepalive.KeepAliveHandler._start_transaction(self, h, req)
   342             return keepalive.KeepAliveHandler._start_transaction(self, h, req)
   413 
   343 
   414         def https_open(self, req):
   344         def https_open(self, req):
   415             res = readauthforuri(self.ui, req.get_full_url())
   345             res = httpconnectionmod.readauthforuri(self.ui, req.get_full_url())
   416             if res:
   346             if res:
   417                 group, auth = res
   347                 group, auth = res
   418                 self.auth = auth
   348                 self.auth = auth
   419                 self.ui.debug("using auth.%s.* for authentication\n" % group)
   349                 self.ui.debug("using auth.%s.* for authentication\n" % group)
   420             else:
   350             else:
   493 def opener(ui, authinfo=None):
   423 def opener(ui, authinfo=None):
   494     '''
   424     '''
   495     construct an opener suitable for urllib2
   425     construct an opener suitable for urllib2
   496     authinfo will be added to the password manager
   426     authinfo will be added to the password manager
   497     '''
   427     '''
   498     handlers = [httphandler()]
   428     if ui.configbool('ui', 'usehttp2', False):
   499     if has_https:
   429         handlers = [httpconnectionmod.http2handler(ui, passwordmgr(ui))]
   500         handlers.append(httpshandler(ui))
   430     else:
       
   431         handlers = [httphandler()]
       
   432         if has_https:
       
   433             handlers.append(httpshandler(ui))
   501 
   434 
   502     handlers.append(proxyhandler(ui))
   435     handlers.append(proxyhandler(ui))
   503 
   436 
   504     passmgr = passwordmgr(ui)
   437     passmgr = passwordmgr(ui)
   505     if authinfo is not None:
   438     if authinfo is not None: