mercurial/httprepo.py
changeset 2465 c91118f425d0
parent 2447 cd00531ecc16
child 2467 4e78dc71d946
equal deleted inserted replaced
2464:09b1c9ef317c 2465:c91118f425d0
     8 from node import *
     8 from node import *
     9 from remoterepo import *
     9 from remoterepo import *
    10 from i18n import gettext as _
    10 from i18n import gettext as _
    11 from demandload import *
    11 from demandload import *
    12 demandload(globals(), "hg os urllib urllib2 urlparse zlib util httplib")
    12 demandload(globals(), "hg os urllib urllib2 urlparse zlib util httplib")
    13 demandload(globals(), "keepalive")
    13 demandload(globals(), "keepalive tempfile socket")
    14 
    14 
    15 class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm):
    15 class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm):
    16     def __init__(self, ui):
    16     def __init__(self, ui):
    17         urllib2.HTTPPasswordMgrWithDefaultRealm.__init__(self)
    17         urllib2.HTTPPasswordMgrWithDefaultRealm.__init__(self)
    18         self.ui = ui
    18         self.ui = ui
    67         else:
    67         else:
    68             userpass = urllib.quote(user)
    68             userpass = urllib.quote(user)
    69         return userpass + '@' + hostport
    69         return userpass + '@' + hostport
    70     return hostport
    70     return hostport
    71 
    71 
       
    72 class httpconnection(keepalive.HTTPConnection):
       
    73     # must be able to send big bundle as stream.
       
    74 
       
    75     def send(self, data):
       
    76         if isinstance(data, str):
       
    77             keepalive.HTTPConnection.send(self, data)
       
    78         else:
       
    79             # if auth required, some data sent twice, so rewind here
       
    80             data.seek(0)
       
    81             for chunk in util.filechunkiter(data):
       
    82                 keepalive.HTTPConnection.send(self, chunk)
       
    83 
       
    84 class httphandler(keepalive.HTTPHandler):
       
    85     def http_open(self, req):
       
    86         return self.do_open(httpconnection, req)
       
    87 
    72 class httprepository(remoterepository):
    88 class httprepository(remoterepository):
    73     def __init__(self, ui, path):
    89     def __init__(self, ui, path):
    74         self.caps = None
    90         self.caps = None
    75         scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
    91         scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
    76         if query or frag:
    92         if query or frag:
    84                                         urlpath, '', ''))
   100                                         urlpath, '', ''))
    85         self.ui = ui
   101         self.ui = ui
    86 
   102 
    87         proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
   103         proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
    88         proxyauthinfo = None
   104         proxyauthinfo = None
    89         handler = keepalive.HTTPHandler()
   105         handler = httphandler()
    90 
   106 
    91         if proxyurl:
   107         if proxyurl:
    92             # proxy can be proper url or host[:port]
   108             # proxy can be proper url or host[:port]
    93             if not (proxyurl.startswith('http:') or
   109             if not (proxyurl.startswith('http:') or
    94                     proxyurl.startswith('https:')):
   110                     proxyurl.startswith('https:')):
   152         if self.caps is None:
   168         if self.caps is None:
   153             try:
   169             try:
   154                 self.caps = self.do_read('capabilities').split()
   170                 self.caps = self.do_read('capabilities').split()
   155             except hg.RepoError:
   171             except hg.RepoError:
   156                 self.caps = ()
   172                 self.caps = ()
       
   173             self.ui.debug(_('capabilities: %s\n') %
       
   174                           (' '.join(self.caps or ['none'])))
   157         return self.caps
   175         return self.caps
   158 
   176 
   159     capabilities = property(get_caps)
   177     capabilities = property(get_caps)
   160 
   178 
   161     def dev(self):
   179     def dev(self):
   163 
   181 
   164     def lock(self):
   182     def lock(self):
   165         raise util.Abort(_('operation not supported over http'))
   183         raise util.Abort(_('operation not supported over http'))
   166 
   184 
   167     def do_cmd(self, cmd, **args):
   185     def do_cmd(self, cmd, **args):
       
   186         data = args.pop('data', None)
       
   187         headers = args.pop('headers', {})
   168         self.ui.debug(_("sending %s command\n") % cmd)
   188         self.ui.debug(_("sending %s command\n") % cmd)
   169         q = {"cmd": cmd}
   189         q = {"cmd": cmd}
   170         q.update(args)
   190         q.update(args)
   171         qs = urllib.urlencode(q)
   191         qs = urllib.urlencode(q)
   172         cu = "%s?%s" % (self.url, qs)
   192         cu = "%s?%s" % (self.url, qs)
   173         try:
   193         try:
   174             resp = urllib2.urlopen(cu)
   194             resp = urllib2.urlopen(urllib2.Request(cu, data, headers))
   175         except httplib.HTTPException, inst:
   195         except httplib.HTTPException, inst:
   176             self.ui.debug(_('http error while sending %s command\n') % cmd)
   196             self.ui.debug(_('http error while sending %s command\n') % cmd)
   177             self.ui.print_exc()
   197             self.ui.print_exc()
   178             raise IOError(None, inst)
   198             raise IOError(None, inst)
   179         try:
   199         try:
   247             yield zd.flush()
   267             yield zd.flush()
   248 
   268 
   249         return util.chunkbuffer(zgenerator(util.filechunkiter(f)))
   269         return util.chunkbuffer(zgenerator(util.filechunkiter(f)))
   250 
   270 
   251     def unbundle(self, cg, heads, source):
   271     def unbundle(self, cg, heads, source):
   252         raise util.Abort(_('operation not supported over http'))
   272         # have to stream bundle to a temp file because we do not have
       
   273         # http 1.1 chunked transfer.
       
   274 
       
   275         fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
       
   276         fp = os.fdopen(fd, 'wb+')
       
   277         try:
       
   278             for chunk in util.filechunkiter(cg):
       
   279                 fp.write(chunk)
       
   280             length = fp.tell()
       
   281             rfp = self.do_cmd(
       
   282                 'unbundle', data=fp,
       
   283                 headers={'content-length': length,
       
   284                          'content-type': 'application/octet-stream'},
       
   285                 heads=' '.join(map(hex, heads)))
       
   286             try:
       
   287                 ret = int(rfp.readline())
       
   288                 self.ui.write(rfp.read())
       
   289                 return ret
       
   290             finally:
       
   291                 rfp.close()
       
   292         finally:
       
   293             fp.close()
       
   294             os.unlink(tempname)
   253 
   295 
   254 class httpsrepository(httprepository):
   296 class httpsrepository(httprepository):
   255     pass
   297     pass