comparison mercurial/sshpeer.py @ 36372:b8d0761a85c7

wireproto: document the wonky push protocol for SSH It took me several minutes to figure out how the "unbundle" protocol worked. It turns out that the SSH protocol handler sends an empty reply that is interpreted as "OK to send" and only then does the client send the bundle payload. On top of that, the response is different depending on whether the operation was successful or not. I nearly pulled out my hair deciphering this. Differential Revision: https://phab.mercurial-scm.org/D2385
author Gregory Szorc <gregory.szorc@gmail.com>
date Wed, 21 Feb 2018 16:47:39 -0800
parents 11ba1a96f946
children dabf86721200
comparison
equal deleted inserted replaced
36371:0c231df1ffdc 36372:b8d0761a85c7
464 def _call(self, cmd, **args): 464 def _call(self, cmd, **args):
465 args = pycompat.byteskwargs(args) 465 args = pycompat.byteskwargs(args)
466 return self._sendrequest(cmd, args, framed=True).read() 466 return self._sendrequest(cmd, args, framed=True).read()
467 467
468 def _callpush(self, cmd, fp, **args): 468 def _callpush(self, cmd, fp, **args):
469 # The server responds with an empty frame if the client should
470 # continue submitting the payload.
469 r = self._call(cmd, **args) 471 r = self._call(cmd, **args)
470 if r: 472 if r:
471 return '', r 473 return '', r
474
475 # The payload consists of frames with content followed by an empty
476 # frame.
472 for d in iter(lambda: fp.read(4096), ''): 477 for d in iter(lambda: fp.read(4096), ''):
473 self._writeframed(d) 478 self._writeframed(d)
474 self._writeframed("", flush=True) 479 self._writeframed("", flush=True)
480
481 # In case of success, there is an empty frame and a frame containing
482 # the integer result (as a string).
483 # In case of error, there is a non-empty frame containing the error.
475 r = self._readframed() 484 r = self._readframed()
476 if r: 485 if r:
477 return '', r 486 return '', r
478 return self._readframed(), '' 487 return self._readframed(), ''
479 488
480 def _calltwowaystream(self, cmd, fp, **args): 489 def _calltwowaystream(self, cmd, fp, **args):
490 # The server responds with an empty frame if the client should
491 # continue submitting the payload.
481 r = self._call(cmd, **args) 492 r = self._call(cmd, **args)
482 if r: 493 if r:
483 # XXX needs to be made better 494 # XXX needs to be made better
484 raise error.Abort(_('unexpected remote reply: %s') % r) 495 raise error.Abort(_('unexpected remote reply: %s') % r)
496
497 # The payload consists of frames with content followed by an empty
498 # frame.
485 for d in iter(lambda: fp.read(4096), ''): 499 for d in iter(lambda: fp.read(4096), ''):
486 self._writeframed(d) 500 self._writeframed(d)
487 self._writeframed("", flush=True) 501 self._writeframed("", flush=True)
502
488 return self._pipei 503 return self._pipei
489 504
490 def _getamount(self): 505 def _getamount(self):
491 l = self._pipei.readline() 506 l = self._pipei.readline()
492 if l == '\n': 507 if l == '\n':