Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/sshpeer.py @ 36403: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
36402:0c231df1ffdc | 36403: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': |