Patchwork [3,of,8] bundle2: produce a bundle2 reply

login
register
mail settings
Submitter Pierre-Yves David
Date April 11, 2014, 10:06 p.m.
Message ID <2d021d13b34bb12a7a88.1397253960@marginatus.alto.octopoid.net>
Download mbox | patch
Permalink /patch/4286/
State Accepted
Headers show

Comments

Pierre-Yves David - April 11, 2014, 10:06 p.m.
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david@fb.com>
# Date 1396646651 25200
#      Fri Apr 04 14:24:11 2014 -0700
# Node ID 2d021d13b34bb12a7a88962511515ce99e15fe6a
# Parent  45371a6d56d2fe379693c898d0f5374cd7a00f2b
bundle2: produce a bundle2 reply

We do not know yet what kind of data future features and extensions will need to
exchange. To handle that, bundle2 allows to send arbitrary content to the
server. As a consequence, we need to be able to reply arbitrary content to the
client. And, we can use bundle2 to transmit those arbitrary data.

When a client will push a bundle2 to the server, the server will reply with a
bundle2 itself.

This changeset installs the first stone of this logic and test it.

Patch

diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py
--- a/mercurial/bundle2.py
+++ b/mercurial/bundle2.py
@@ -252,10 +252,11 @@  class bundleoperation(object):
     def __init__(self, repo, transactiongetter):
         self.repo = repo
         self.ui = repo.ui
         self.records = unbundlerecords()
         self.gettransaction = transactiongetter
+        self.reply = None
 
 class TransactionUnavailable(RuntimeError):
     pass
 
 def _notransaction():
@@ -276,10 +277,13 @@  def processbundle(repo, unbundler, trans
 
     Unknown Mandatory part will abort the process.
     """
     op = bundleoperation(repo, transactiongetter)
     # todo:
+    # - only create reply bundle if requested.
+    op.reply = bundle20(op.ui)
+    # todo:
     # - replace this is a init function soon.
     # - exception catching
     unbundler.params
     iterparts = iter(unbundler)
     try:
diff --git a/tests/test-bundle2.t b/tests/test-bundle2.t
--- a/tests/test-bundle2.t
+++ b/tests/test-bundle2.t
@@ -31,10 +31,17 @@  Create an extension to test bundle2 API
   >     for line in part.data.split('\n'):
   >         op.ui.write('    %s\n' % line)
   >         verses += 1
   >     op.records.add('song', {'verses': verses})
   > 
+  > @bundle2.parthandler('test:ping')
+  > def pinghandler(op, part):
+  >     op.ui.write('received ping request (id %i)\n' % part.id)
+  >     if op.reply is not None:
+  >         op.reply.addpart(bundle2.part('test:pong',
+  >                                       [('in-reply-to', str(part.id))]))
+  > 
   > @command('bundle2',
   >          [('', 'param', [], 'stream level parameter'),
   >           ('', 'unknown', False, 'include an unknown mandatory part in the bundle'),
   >           ('', 'parts', False, 'include some arbitrary parts to the bundle'),
   >           ('r', 'rev', [], 'includes those changeset in the bundle'),],
@@ -81,21 +88,24 @@  Create an extension to test bundle2 API
   >        bundler.addpart(part)
   >     if opts['unknown']:
   >        part = bundle2.part('test:UNKNOWN',
   >                            data='some random content')
   >        bundler.addpart(part)
+  >     if opts['parts']:
+  >        part = bundle2.part('test:ping')
+  >        bundler.addpart(part)
   > 
   >     if path is None:
   >        file = sys.stdout
   >     else:
   >         file = open(path, 'w')
   > 
   >     for chunk in bundler.getchunks():
   >         file.write(chunk)
   > 
   > @command('unbundle2', [], '')
-  > def cmdunbundle2(ui, repo):
+  > def cmdunbundle2(ui, repo, replypath=None):
   >     """process a bundle2 stream from stdin on the current repo"""
   >     try:
   >         tr = None
   >         lock = repo.lock()
   >         tr = repo.transaction('processbundle')
@@ -114,10 +124,14 @@  Create an extension to test bundle2 API
   >     if op.records['song']:
   >         totalverses = sum(r['verses'] for r in op.records['song'])
   >         ui.write('%i total verses sung\n' % totalverses)
   >     for rec in op.records['changegroup']:
   >         ui.write('addchangegroup return: %i\n' % rec['return'])
+  >     if op.reply is not None and replypath is not None:
+  >         file = open(replypath, 'w')
+  >         for chunk in op.reply.getchunks():
+  >             file.write(chunk)
   > 
   > @command('statbundle2', [], '')
   > def cmdstatbundle2(ui, repo):
   >     """print statistic on the bundle2 container read from stdin"""
   >     unbundler = bundle2.unbundle20(ui, sys.stdin)
@@ -316,23 +330,24 @@  Test part
   start of parts
   bundle part: "test:empty"
   bundle part: "test:empty"
   bundle part: "test:song"
   bundle part: "test:math"
+  bundle part: "test:ping"
   end of bundle
 
   $ cat ../parts.hg2
   HG20\x00\x00\x00\x11 (esc)
   test:empty\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11 (esc)
   test:empty\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x10	test:song\x00\x00\x00\x02\x00\x00\x00\x00\x00\xb2Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko (esc)
   Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
-  Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.\x00\x00\x00\x00\x00+	test:math\x00\x00\x00\x03\x02\x01\x02\x04\x01\x04\x07\x03pi3.14e2.72cookingraw\x00\x00\x00\x0242\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
+  Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.\x00\x00\x00\x00\x00+	test:math\x00\x00\x00\x03\x02\x01\x02\x04\x01\x04\x07\x03pi3.14e2.72cookingraw\x00\x00\x00\x0242\x00\x00\x00\x00\x00\x10	test:ping\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
 
 
   $ hg statbundle2 < ../parts.hg2
   options count: 0
-  parts count:   4
+  parts count:   5
     :test:empty:
       mandatory: 0
       advisory: 0
       payload: 0 bytes
     :test:empty:
@@ -345,10 +360,14 @@  Test part
       payload: 178 bytes
     :test:math:
       mandatory: 2
       advisory: 1
       payload: 2 bytes
+    :test:ping:
+      mandatory: 0
+      advisory: 0
+      payload: 0 bytes
 
   $ hg statbundle2 --debug < ../parts.hg2
   start processing of HG20 stream
   reading bundle2 stream parameters
   options count: 0
@@ -373,13 +392,18 @@  Test part
   part type: "test:math"
   part id: "3"
   part parameters: 3
   payload chunk size: 2
   payload chunk size: 0
+  part header size: 16
+  part type: "test:ping"
+  part id: "4"
+  part parameters: 0
+  payload chunk size: 0
   part header size: 0
   end of bundle2 stream
-  parts count:   4
+  parts count:   5
     :test:empty:
       mandatory: 0
       advisory: 0
       payload: 0 bytes
     :test:empty:
@@ -392,10 +416,14 @@  Test part
       payload: 178 bytes
     :test:math:
       mandatory: 2
       advisory: 1
       payload: 2 bytes
+    :test:ping:
+      mandatory: 0
+      advisory: 0
+      payload: 0 bytes
 
 Test actual unbundling of test part
 =======================================
 
 Process the bundle
@@ -432,15 +460,24 @@  Process the bundle
   part id: "3"
   part parameters: 3
   payload chunk size: 2
   payload chunk size: 0
   ignoring unknown advisory part 'test:math'
+  part header size: 16
+  part type: "test:ping"
+  part id: "4"
+  part parameters: 0
+  payload chunk size: 0
+  found a handler for part 'test:ping'
+  received ping request (id 4)
   part header size: 0
   end of bundle2 stream
   0 unread bytes
   3 total verses sung
 
+Unbundle with an unknown mandatory part
+(should abort)
 
   $ hg bundle2 --parts --unknown ../unknown.hg2
 
   $ hg unbundle2 < ../unknown.hg2
   The choir starts singing:
@@ -449,10 +486,36 @@  Process the bundle
       Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
   0 unread bytes
   abort: missing support for 'test:unknown'
   [255]
 
+unbundle with a reply
+
+  $ hg unbundle2 ../reply.hg2 < ../parts.hg2
+  The choir starts singing:
+      Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
+      Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
+      Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
+  received ping request (id 4)
+  0 unread bytes
+  3 total verses sung
+
+The reply is a bundle
+
+  $ cat ../reply.hg2
+  HG20\x00\x00\x00\x1e	test:pong\x00\x00\x00\x00\x01\x00\x0b\x01in-reply-to4\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
+
+The reply is valid
+
+  $ hg statbundle2 < ../reply.hg2
+  options count: 0
+  parts count:   1
+    :test:pong:
+      mandatory: 1
+      advisory: 0
+      payload: 0 bytes
+
 Support for changegroup
 ===================================
 
   $ hg unbundle $TESTDIR/bundles/rebase.hg
   adding changesets