##// END OF EJS Templates
sshpeer: initial definition and implementation of new SSH protocol...
sshpeer: initial definition and implementation of new SSH protocol The existing SSH protocol has several design flaws. Future commits will elaborate on these flaws as new features are introduced to combat these flaws. For now, hopefully you can take me for my word that a ground up rewrite of the SSH protocol is needed. This commit lays the foundation for a new SSH protocol by defining a mechanism to upgrade the SSH transport channel away from the default (version 1) protocol to something modern (which we'll call "version 2" for now). This upgrade process is detailed in the internals documentation for the wire protocol. The gist of it is the client sends a request line preceding the "hello" command/line which basically says "I'm requesting an upgrade: here's what I support." If the server recognizes that line, it processes the upgrade request and the transport channel is switched to use the new version of the protocol. If not, it sends an empty response, which is how all Mercurial SSH servers from the beginning of time reacted to unknown commands. The upgrade request is effectively ignored and the client continues to use the existing version of the protocol as if nothing happened. The new version of the SSH protocol is completely identical to version 1 aside from the upgrade dance and the bytes that follow. The immediate bytes that follow the protocol switch are defined to be a length framed "capabilities: " line containing the remote's advertised capabilities. In reality, this looks very similar to what the "hello" response would look like. But it will evolve quickly. The methodology by which the protocol will evolve is important. I'm not going to introduce the new protocol all at once. That would likely lead to endless bike shedding and forward progress would stall. Instead, I intend to tricle out new features and diversions from the existing protocol in small, incremental changes. To support the gradual evolution of the protocol, the on-the-wire advertised protocol name contains an "exp" to denote "experimental" and a 4 digit field to capture the sub-version of the protocol. Whenever we make a BC change to the wire protocol, we can increment this version and lock out all older clients because it will appear as a completely different protocol version. This means we can incur as many breaking changes as we want. We don't have to commit to supporting any one feature or idea for a long period of time. We can even evolve the handshake mechanism, because that is defined as being an implementation detail of the negotiated protocol version! Hopefully this lowers the barrier to accepting changes to the protocol and for experimenting with "radical" ideas during its development. In core, sshpeer received most of the attention. We haven't even implemented the server bits for the new protocol in core yet. Instead, we add very primitive support to our test server, mainly just to exercise the added code paths in sshpeer. Differential Revision: https://phab.mercurial-scm.org/D2061 # no-check-commit because of required foo_bar naming

File last commit:

r34310:b94db178 default
r35994:48a3a928 default
Show More
test-chg.t
205 lines | 4.9 KiB | text/troff | Tads3Lexer
#require chg
$ cp $HGRCPATH $HGRCPATH.orig
init repo
$ chg init foo
$ cd foo
ill-formed config
$ chg status
$ echo '=brokenconfig' >> $HGRCPATH
$ chg status
hg: parse error at * (glob)
[255]
$ cp $HGRCPATH.orig $HGRCPATH
long socket path
$ sockpath=$TESTTMP/this/path/should/be/longer/than/one-hundred-and-seven/characters/where/107/is/the/typical/size/limit/of/unix-domain-socket
$ mkdir -p $sockpath
$ bakchgsockname=$CHGSOCKNAME
$ CHGSOCKNAME=$sockpath/server
$ export CHGSOCKNAME
$ chg root
$TESTTMP/foo
$ rm -rf $sockpath
$ CHGSOCKNAME=$bakchgsockname
$ export CHGSOCKNAME
$ cd ..
editor
------
$ cat >> pushbuffer.py <<EOF
> def reposetup(ui, repo):
> repo.ui.pushbuffer(subproc=True)
> EOF
$ chg init editor
$ cd editor
by default, system() should be redirected to the client:
$ touch foo
$ CHGDEBUG= HGEDITOR=cat chg ci -Am channeled --edit 2>&1 \
> | egrep "HG:|run 'cat"
chg: debug: * run 'cat "*"' at '$TESTTMP/editor' (glob)
HG: Enter commit message. Lines beginning with 'HG:' are removed.
HG: Leave message empty to abort commit.
HG: --
HG: user: test
HG: branch 'default'
HG: added foo
but no redirection should be made if output is captured:
$ touch bar
$ CHGDEBUG= HGEDITOR=cat chg ci -Am bufferred --edit \
> --config extensions.pushbuffer="$TESTTMP/pushbuffer.py" 2>&1 \
> | egrep "HG:|run 'cat"
[1]
check that commit commands succeeded:
$ hg log -T '{rev}:{desc}\n'
1:bufferred
0:channeled
$ cd ..
pager
-----
$ cat >> fakepager.py <<EOF
> import sys
> for line in sys.stdin:
> sys.stdout.write('paged! %r\n' % line)
> EOF
enable pager extension globally, but spawns the master server with no tty:
$ chg init pager
$ cd pager
$ cat >> $HGRCPATH <<EOF
> [extensions]
> pager =
> [pager]
> pager = $PYTHON $TESTTMP/fakepager.py
> EOF
$ chg version > /dev/null
$ touch foo
$ chg ci -qAm foo
pager should be enabled if the attached client has a tty:
$ chg log -l1 -q --config ui.formatted=True
paged! '0:1f7b0de80e11\n'
$ chg log -l1 -q --config ui.formatted=False
0:1f7b0de80e11
chg waits for pager if runcommand raises
$ cat > $TESTTMP/crash.py <<EOF
> from mercurial import registrar
> cmdtable = {}
> command = registrar.command(cmdtable)
> @command(b'crash')
> def pagercrash(ui, repo, *pats, **opts):
> ui.write('going to crash\n')
> raise Exception('.')
> EOF
$ cat > $TESTTMP/fakepager.py <<EOF
> from __future__ import absolute_import
> import sys
> import time
> for line in iter(sys.stdin.readline, ''):
> if 'crash' in line: # only interested in lines containing 'crash'
> # if chg exits when pager is sleeping (incorrectly), the output
> # will be captured by the next test case
> time.sleep(1)
> sys.stdout.write('crash-pager: %s' % line)
> EOF
$ cat >> .hg/hgrc <<EOF
> [extensions]
> crash = $TESTTMP/crash.py
> EOF
$ chg crash --pager=on --config ui.formatted=True 2>/dev/null
crash-pager: going to crash
[255]
$ cd ..
server lifecycle
----------------
chg server should be restarted on code change, and old server will shut down
automatically. In this test, we use the following time parameters:
- "sleep 1" to make mtime different
- "sleep 2" to notice mtime change (polling interval is 1 sec)
set up repository with an extension:
$ chg init extreload
$ cd extreload
$ touch dummyext.py
$ cat <<EOF >> .hg/hgrc
> [extensions]
> dummyext = dummyext.py
> EOF
isolate socket directory for stable result:
$ OLDCHGSOCKNAME=$CHGSOCKNAME
$ mkdir chgsock
$ CHGSOCKNAME=`pwd`/chgsock/server
warm up server:
$ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start'
chg: debug: * start cmdserver at $TESTTMP/extreload/chgsock/server.* (glob)
new server should be started if extension modified:
$ sleep 1
$ touch dummyext.py
$ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start'
chg: debug: * instruction: unlink $TESTTMP/extreload/chgsock/server-* (glob)
chg: debug: * instruction: reconnect (glob)
chg: debug: * start cmdserver at $TESTTMP/extreload/chgsock/server.* (glob)
old server will shut down, while new server should still be reachable:
$ sleep 2
$ CHGDEBUG= chg log 2>&1 | (egrep 'instruction|start' || true)
socket file should never be unlinked by old server:
(simulates unowned socket by updating mtime, which makes sure server exits
at polling cycle)
$ ls chgsock/server-*
chgsock/server-* (glob)
$ touch chgsock/server-*
$ sleep 2
$ ls chgsock/server-*
chgsock/server-* (glob)
since no server is reachable from socket file, new server should be started:
(this test makes sure that old server shut down automatically)
$ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start'
chg: debug: * start cmdserver at $TESTTMP/extreload/chgsock/server.* (glob)
shut down servers and restore environment:
$ rm -R chgsock
$ CHGSOCKNAME=$OLDCHGSOCKNAME
$ cd ..