push over http: client support.
authorVadim Gelfer <vadim.gelfer@gmail.com>
Tue, 20 Jun 2006 15:17:28 -0700
changeset 2465 c91118f425d0
parent 2464 09b1c9ef317c
child 2466 e10665147d26
push over http: client support. stream bundle data using PUT request.
mercurial/httprepo.py
--- a/mercurial/httprepo.py	Tue Jun 20 15:16:50 2006 -0700
+++ b/mercurial/httprepo.py	Tue Jun 20 15:17:28 2006 -0700
@@ -10,7 +10,7 @@
 from i18n import gettext as _
 from demandload import *
 demandload(globals(), "hg os urllib urllib2 urlparse zlib util httplib")
-demandload(globals(), "keepalive")
+demandload(globals(), "keepalive tempfile socket")
 
 class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm):
     def __init__(self, ui):
@@ -69,6 +69,22 @@
         return userpass + '@' + hostport
     return hostport
 
+class httpconnection(keepalive.HTTPConnection):
+    # must be able to send big bundle as stream.
+
+    def send(self, data):
+        if isinstance(data, str):
+            keepalive.HTTPConnection.send(self, data)
+        else:
+            # if auth required, some data sent twice, so rewind here
+            data.seek(0)
+            for chunk in util.filechunkiter(data):
+                keepalive.HTTPConnection.send(self, chunk)
+
+class httphandler(keepalive.HTTPHandler):
+    def http_open(self, req):
+        return self.do_open(httpconnection, req)
+
 class httprepository(remoterepository):
     def __init__(self, ui, path):
         self.caps = None
@@ -86,7 +102,7 @@
 
         proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
         proxyauthinfo = None
-        handler = keepalive.HTTPHandler()
+        handler = httphandler()
 
         if proxyurl:
             # proxy can be proper url or host[:port]
@@ -154,6 +170,8 @@
                 self.caps = self.do_read('capabilities').split()
             except hg.RepoError:
                 self.caps = ()
+            self.ui.debug(_('capabilities: %s\n') %
+                          (' '.join(self.caps or ['none'])))
         return self.caps
 
     capabilities = property(get_caps)
@@ -165,13 +183,15 @@
         raise util.Abort(_('operation not supported over http'))
 
     def do_cmd(self, cmd, **args):
+        data = args.pop('data', None)
+        headers = args.pop('headers', {})
         self.ui.debug(_("sending %s command\n") % cmd)
         q = {"cmd": cmd}
         q.update(args)
         qs = urllib.urlencode(q)
         cu = "%s?%s" % (self.url, qs)
         try:
-            resp = urllib2.urlopen(cu)
+            resp = urllib2.urlopen(urllib2.Request(cu, data, headers))
         except httplib.HTTPException, inst:
             self.ui.debug(_('http error while sending %s command\n') % cmd)
             self.ui.print_exc()
@@ -249,7 +269,29 @@
         return util.chunkbuffer(zgenerator(util.filechunkiter(f)))
 
     def unbundle(self, cg, heads, source):
-        raise util.Abort(_('operation not supported over http'))
+        # have to stream bundle to a temp file because we do not have
+        # http 1.1 chunked transfer.
+
+        fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
+        fp = os.fdopen(fd, 'wb+')
+        try:
+            for chunk in util.filechunkiter(cg):
+                fp.write(chunk)
+            length = fp.tell()
+            rfp = self.do_cmd(
+                'unbundle', data=fp,
+                headers={'content-length': length,
+                         'content-type': 'application/octet-stream'},
+                heads=' '.join(map(hex, heads)))
+            try:
+                ret = int(rfp.readline())
+                self.ui.write(rfp.read())
+                return ret
+            finally:
+                rfp.close()
+        finally:
+            fp.close()
+            os.unlink(tempname)
 
 class httpsrepository(httprepository):
     pass