debugcommands: add debugserve command
`hg serve --stdio` requires the exact command argument form
`hg -R <path> serve --stdio` for security reasons. An upcoming
commit will need to start an SSH protocol server process with
custom settings.
This commit creates a `hg debugserve` command for starting servers
with custom options. There are no security restrictions and we can
add options here that aren't appropriate for built-in commands.
We currently only support starting an SSH protocol server using
the process's stdio file descriptors. The server supports logging
its I/O activity to a file descriptor number passed as a command
argument.
Differential Revision: https://phab.mercurial-scm.org/D2464
--- a/mercurial/debugcommands.py Sun Feb 25 11:16:09 2018 -0800
+++ b/mercurial/debugcommands.py Tue Feb 27 15:47:44 2018 -0800
@@ -73,6 +73,7 @@
url as urlmod,
util,
vfs as vfsmod,
+ wireprotoserver,
)
release = lockmod.release
@@ -2230,6 +2231,37 @@
for c in revs:
ui.write("%d\n" % c)
+@command('debugserve', [
+ ('', 'sshstdio', False, _('run an SSH server bound to process handles')),
+ ('', 'logiofd', '', _('file descriptor to log server I/O to')),
+ ('', 'logiofile', '', _('file to log server I/O to')),
+], '')
+def debugserve(ui, repo, **opts):
+ """run a server with advanced settings
+
+ This command is similar to :hg:`serve`. It exists partially as a
+ workaround to the fact that ``hg serve --stdio`` must have specific
+ arguments for security reasons.
+ """
+ opts = pycompat.byteskwargs(opts)
+
+ if not opts['sshstdio']:
+ raise error.Abort(_('only --sshstdio is currently supported'))
+
+ logfh = None
+
+ if opts['logiofd'] and opts['logiofile']:
+ raise error.Abort(_('cannot use both --logiofd and --logiofile'))
+
+ if opts['logiofd']:
+ # Line buffered because output is line based.
+ logfh = os.fdopen(int(opts['logiofd']), 'ab', 1)
+ elif opts['logiofile']:
+ logfh = open(opts['logiofile'], 'ab', 1)
+
+ s = wireprotoserver.sshserver(ui, repo, logfh=logfh)
+ s.serve_forever()
+
@command('debugsetparents', [], _('REV1 [REV2]'))
def debugsetparents(ui, repo, rev1, rev2=None):
"""manually set the parents of the current working directory
--- a/tests/test-completion.t Sun Feb 25 11:16:09 2018 -0800
+++ b/tests/test-completion.t Tue Feb 27 15:47:44 2018 -0800
@@ -111,6 +111,7 @@
debugrename
debugrevlog
debugrevspec
+ debugserve
debugsetparents
debugssl
debugsub
@@ -291,6 +292,7 @@
debugrename: rev
debugrevlog: changelog, manifest, dir, dump
debugrevspec: optimize, show-revs, show-set, show-stage, no-optimized, verify-optimized
+ debugserve: sshstdio, logiofd, logiofile
debugsetparents:
debugssl:
debugsub: rev
--- a/tests/test-help.t Sun Feb 25 11:16:09 2018 -0800
+++ b/tests/test-help.t Tue Feb 27 15:47:44 2018 -0800
@@ -967,6 +967,7 @@
debugrename dump rename information
debugrevlog show data and statistics about a revlog
debugrevspec parse and apply a revision specification
+ debugserve run a server with advanced settings
debugsetparents
manually set the parents of the current working directory
debugssl test a secure connection to a server
--- a/tests/test-ssh-proto.t Sun Feb 25 11:16:09 2018 -0800
+++ b/tests/test-ssh-proto.t Tue Feb 27 15:47:44 2018 -0800
@@ -39,6 +39,43 @@
384
capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
+`hg debugserve --sshstdio` works
+
+ $ cd server
+ $ hg debugserve --sshstdio << EOF
+ > hello
+ > EOF
+ 384
+ capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
+
+I/O logging works
+
+ $ hg debugserve --sshstdio --logiofd 1 << EOF
+ > hello
+ > EOF
+ o> write(4) -> None:
+ o> 384\n
+ o> write(384) -> None:
+ o> capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN\n
+ 384
+ capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
+ o> flush() -> None
+
+ $ hg debugserve --sshstdio --logiofile $TESTTMP/io << EOF
+ > hello
+ > EOF
+ 384
+ capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
+
+ $ cat $TESTTMP/io
+ o> write(4) -> None:
+ o> 384\n
+ o> write(384) -> None:
+ o> capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN\n
+ o> flush() -> None
+
+ $ cd ..
+
>=0.9.1 clients send a "hello" + "between" for the null range as part of handshake.
Server should reply with capabilities and should send "1\n\n" as a successful
reply with empty response to the "between".