# HG changeset patch # User Pierre-Yves David # Date 1396646651 25200 # Node ID d7df4b7378ae194106ade2e14c6bafa334334fc4 # Parent ed3c5e18a047037ae7327fc744d701fb14a4935f 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. diff -r ed3c5e18a047 -r d7df4b7378ae mercurial/bundle2.py --- a/mercurial/bundle2.py Fri Apr 11 08:24:59 2014 -0700 +++ b/mercurial/bundle2.py Fri Apr 04 14:24:11 2014 -0700 @@ -254,6 +254,7 @@ self.ui = repo.ui self.records = unbundlerecords() self.gettransaction = transactiongetter + self.reply = None class TransactionUnavailable(RuntimeError): pass @@ -278,6 +279,9 @@ """ 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 diff -r ed3c5e18a047 -r d7df4b7378ae tests/test-bundle2.t --- a/tests/test-bundle2.t Fri Apr 11 08:24:59 2014 -0700 +++ b/tests/test-bundle2.t Fri Apr 04 14:24:11 2014 -0700 @@ -33,6 +33,13 @@ > 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'), @@ -83,6 +90,9 @@ > 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 @@ -93,7 +103,7 @@ > 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 @@ -116,6 +126,10 @@ > 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): @@ -318,6 +332,7 @@ bundle part: "test:empty" bundle part: "test:song" bundle part: "test:math" + bundle part: "test:ping" end of bundle $ cat ../parts.hg2 @@ -325,12 +340,12 @@ 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 @@ -347,6 +362,10 @@ 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 @@ -375,9 +394,14 @@ 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 @@ -394,6 +418,10 @@ mandatory: 2 advisory: 1 payload: 2 bytes + :test:ping: + mandatory: 0 + advisory: 0 + payload: 0 bytes Test actual unbundling of test part ======================================= @@ -434,11 +462,20 @@ 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 @@ -451,6 +488,32 @@ 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 ===================================