Let's welcome the SSH protocol handler to our new central home
for protocol handlers.
.. api::
Content from mercurial.sshserver has been moved into mercurial.wireprotoserver.
Let's welcome the SSH protocol handler to our new central home
for protocol handlers.
.. api::
Content from mercurial.sshserver has been moved into mercurial.wireprotoserver.
| Automatic diff as part of commit; lint not applicable. |
| Automatic diff as part of commit; unit tests not applicable. |
| Path | Packages | |||
|---|---|---|---|---|
| M | mercurial/commands.py (4 lines) | |||
| D | M | mercurial/sshserver.py (131 lines) | ||
| M | mercurial/wireprotoserver.py (115 lines) | |||
| M | tests/test-sshserver.py (4 lines) |
| phases, | phases, | ||||
| pycompat, | pycompat, | ||||
| rcutil, | rcutil, | ||||
| registrar, | registrar, | ||||
| revsetlang, | revsetlang, | ||||
| rewriteutil, | rewriteutil, | ||||
| scmutil, | scmutil, | ||||
| server, | server, | ||||
| sshserver, | |||||
| streamclone, | streamclone, | ||||
| tags as tagsmod, | tags as tagsmod, | ||||
| templatekw, | templatekw, | ||||
| ui as uimod, | ui as uimod, | ||||
| util, | util, | ||||
| wireprotoserver, | |||||
| ) | ) | ||||
| release = lockmod.release | release = lockmod.release | ||||
| table = {} | table = {} | ||||
| table.update(debugcommandsmod.command._table) | table.update(debugcommandsmod.command._table) | ||||
| command = registrar.command(table) | command = registrar.command(table) | ||||
| opts = pycompat.byteskwargs(opts) | opts = pycompat.byteskwargs(opts) | ||||
| if opts["stdio"] and opts["cmdserver"]: | if opts["stdio"] and opts["cmdserver"]: | ||||
| raise error.Abort(_("cannot use --stdio with --cmdserver")) | raise error.Abort(_("cannot use --stdio with --cmdserver")) | ||||
| if opts["stdio"]: | if opts["stdio"]: | ||||
| if repo is None: | if repo is None: | ||||
| raise error.RepoError(_("there is no Mercurial repository here" | raise error.RepoError(_("there is no Mercurial repository here" | ||||
| " (.hg not found)")) | " (.hg not found)")) | ||||
| s = sshserver.sshserver(ui, repo) | s = wireprotoserver.sshserver(ui, repo) | ||||
| s.serve_forever() | s.serve_forever() | ||||
| service = server.createservice(ui, repo, opts) | service = server.createservice(ui, repo, opts) | ||||
| return server.runservice(opts, initfn=service.init, runfn=service.run) | return server.runservice(opts, initfn=service.init, runfn=service.run) | ||||
| @command('^status|st', | @command('^status|st', | ||||
| [('A', 'all', None, _('show status of all files')), | [('A', 'all', None, _('show status of all files')), | ||||
| ('m', 'modified', None, _('show only modified files')), | ('m', 'modified', None, _('show only modified files')), | ||||
| # sshserver.py - ssh protocol server support for mercurial | |||||
| # | |||||
| # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> | |||||
| # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com> | |||||
| # | |||||
| # This software may be used and distributed according to the terms of the | |||||
| # GNU General Public License version 2 or any later version. | |||||
| from __future__ import absolute_import | |||||
| import sys | |||||
| from .i18n import _ | |||||
| from . import ( | |||||
| encoding, | |||||
| error, | |||||
| hook, | |||||
| util, | |||||
| wireproto, | |||||
| ) | |||||
| class sshserver(wireproto.abstractserverproto): | |||||
| def __init__(self, ui, repo): | |||||
| self.ui = ui | |||||
| self.repo = repo | |||||
| self.lock = None | |||||
| self.fin = ui.fin | |||||
| self.fout = ui.fout | |||||
| self.name = 'ssh' | |||||
| hook.redirect(True) | |||||
| ui.fout = repo.ui.fout = ui.ferr | |||||
| # Prevent insertion/deletion of CRs | |||||
| util.setbinary(self.fin) | |||||
| util.setbinary(self.fout) | |||||
| def getargs(self, args): | |||||
| data = {} | |||||
| keys = args.split() | |||||
| for n in xrange(len(keys)): | |||||
| argline = self.fin.readline()[:-1] | |||||
| arg, l = argline.split() | |||||
| if arg not in keys: | |||||
| raise error.Abort(_("unexpected parameter %r") % arg) | |||||
| if arg == '*': | |||||
| star = {} | |||||
| for k in xrange(int(l)): | |||||
| argline = self.fin.readline()[:-1] | |||||
| arg, l = argline.split() | |||||
| val = self.fin.read(int(l)) | |||||
| star[arg] = val | |||||
| data['*'] = star | |||||
| else: | |||||
| val = self.fin.read(int(l)) | |||||
| data[arg] = val | |||||
| return [data[k] for k in keys] | |||||
| def getarg(self, name): | |||||
| return self.getargs(name)[0] | |||||
| def getfile(self, fpout): | |||||
| self.sendresponse('') | |||||
| count = int(self.fin.readline()) | |||||
| while count: | |||||
| fpout.write(self.fin.read(count)) | |||||
| count = int(self.fin.readline()) | |||||
| def redirect(self): | |||||
| pass | |||||
| def sendresponse(self, v): | |||||
| self.fout.write("%d\n" % len(v)) | |||||
| self.fout.write(v) | |||||
| self.fout.flush() | |||||
| def sendstream(self, source): | |||||
| write = self.fout.write | |||||
| for chunk in source.gen: | |||||
| write(chunk) | |||||
| self.fout.flush() | |||||
| def sendpushresponse(self, rsp): | |||||
| self.sendresponse('') | |||||
| self.sendresponse(str(rsp.res)) | |||||
| def sendpusherror(self, rsp): | |||||
| self.sendresponse(rsp.res) | |||||
| def sendooberror(self, rsp): | |||||
| self.ui.ferr.write('%s\n-\n' % rsp.message) | |||||
| self.ui.ferr.flush() | |||||
| self.fout.write('\n') | |||||
| self.fout.flush() | |||||
| def serve_forever(self): | |||||
| try: | |||||
| while self.serve_one(): | |||||
| pass | |||||
| finally: | |||||
| if self.lock is not None: | |||||
| self.lock.release() | |||||
| sys.exit(0) | |||||
| handlers = { | |||||
| str: sendresponse, | |||||
| wireproto.streamres: sendstream, | |||||
| wireproto.streamres_legacy: sendstream, | |||||
| wireproto.pushres: sendpushresponse, | |||||
| wireproto.pusherr: sendpusherror, | |||||
| wireproto.ooberror: sendooberror, | |||||
| } | |||||
| def serve_one(self): | |||||
| cmd = self.fin.readline()[:-1] | |||||
| if cmd and cmd in wireproto.commands: | |||||
| rsp = wireproto.dispatch(self.repo, self, cmd) | |||||
| self.handlers[rsp.__class__](self, rsp) | |||||
| elif cmd: | |||||
| impl = getattr(self, 'do_' + cmd, None) | |||||
| if impl: | |||||
| r = impl() | |||||
| if r is not None: | |||||
| self.sendresponse(r) | |||||
| else: | |||||
| self.sendresponse("") | |||||
| return cmd != '' | |||||
| def _client(self): | |||||
| client = encoding.environ.get('SSH_CLIENT', '').split(' ', 1)[0] | |||||
| return 'remote:ssh:' + client | |||||
| # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net> | # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net> | ||||
| # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> | # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> | ||||
| # | # | ||||
| # This software may be used and distributed according to the terms of the | # This software may be used and distributed according to the terms of the | ||||
| # GNU General Public License version 2 or any later version. | # GNU General Public License version 2 or any later version. | ||||
| from __future__ import absolute_import | from __future__ import absolute_import | ||||
| import cgi | import cgi | ||||
| import struct | import struct | ||||
| import sys | |||||
| from .i18n import _ | |||||
| from . import ( | from . import ( | ||||
| encoding, | |||||
| error, | error, | ||||
| hook, | |||||
| pycompat, | pycompat, | ||||
| util, | util, | ||||
| wireproto, | wireproto, | ||||
| ) | ) | ||||
| stringio = util.stringio | stringio = util.stringio | ||||
| urlerr = util.urlerr | urlerr = util.urlerr | ||||
| rsp = '0\n%s\n' % rsp.res | rsp = '0\n%s\n' % rsp.res | ||||
| req.respond(HTTP_OK, HGTYPE, body=rsp) | req.respond(HTTP_OK, HGTYPE, body=rsp) | ||||
| return [] | return [] | ||||
| elif isinstance(rsp, wireproto.ooberror): | elif isinstance(rsp, wireproto.ooberror): | ||||
| rsp = rsp.message | rsp = rsp.message | ||||
| req.respond(HTTP_OK, HGERRTYPE, body=rsp) | req.respond(HTTP_OK, HGERRTYPE, body=rsp) | ||||
| return [] | return [] | ||||
| raise error.ProgrammingError('hgweb.protocol internal failure', rsp) | raise error.ProgrammingError('hgweb.protocol internal failure', rsp) | ||||
| class sshserver(wireproto.abstractserverproto): | |||||
| def __init__(self, ui, repo): | |||||
| self.ui = ui | |||||
| self.repo = repo | |||||
| self.lock = None | |||||
| self.fin = ui.fin | |||||
| self.fout = ui.fout | |||||
| self.name = 'ssh' | |||||
| hook.redirect(True) | |||||
| ui.fout = repo.ui.fout = ui.ferr | |||||
| # Prevent insertion/deletion of CRs | |||||
| util.setbinary(self.fin) | |||||
| util.setbinary(self.fout) | |||||
| def getargs(self, args): | |||||
| data = {} | |||||
| keys = args.split() | |||||
| for n in xrange(len(keys)): | |||||
| argline = self.fin.readline()[:-1] | |||||
| arg, l = argline.split() | |||||
| if arg not in keys: | |||||
| raise error.Abort(_("unexpected parameter %r") % arg) | |||||
| if arg == '*': | |||||
| star = {} | |||||
| for k in xrange(int(l)): | |||||
| argline = self.fin.readline()[:-1] | |||||
| arg, l = argline.split() | |||||
| val = self.fin.read(int(l)) | |||||
| star[arg] = val | |||||
| data['*'] = star | |||||
| else: | |||||
| val = self.fin.read(int(l)) | |||||
| data[arg] = val | |||||
| return [data[k] for k in keys] | |||||
| def getarg(self, name): | |||||
| return self.getargs(name)[0] | |||||
| def getfile(self, fpout): | |||||
| self.sendresponse('') | |||||
| count = int(self.fin.readline()) | |||||
| while count: | |||||
| fpout.write(self.fin.read(count)) | |||||
| count = int(self.fin.readline()) | |||||
| def redirect(self): | |||||
| pass | |||||
| def sendresponse(self, v): | |||||
| self.fout.write("%d\n" % len(v)) | |||||
| self.fout.write(v) | |||||
| self.fout.flush() | |||||
| def sendstream(self, source): | |||||
| write = self.fout.write | |||||
| for chunk in source.gen: | |||||
| write(chunk) | |||||
| self.fout.flush() | |||||
| def sendpushresponse(self, rsp): | |||||
| self.sendresponse('') | |||||
| self.sendresponse(str(rsp.res)) | |||||
| def sendpusherror(self, rsp): | |||||
| self.sendresponse(rsp.res) | |||||
| def sendooberror(self, rsp): | |||||
| self.ui.ferr.write('%s\n-\n' % rsp.message) | |||||
| self.ui.ferr.flush() | |||||
| self.fout.write('\n') | |||||
| self.fout.flush() | |||||
| def serve_forever(self): | |||||
| try: | |||||
| while self.serve_one(): | |||||
| pass | |||||
| finally: | |||||
| if self.lock is not None: | |||||
| self.lock.release() | |||||
| sys.exit(0) | |||||
| handlers = { | |||||
| str: sendresponse, | |||||
| wireproto.streamres: sendstream, | |||||
| wireproto.streamres_legacy: sendstream, | |||||
| wireproto.pushres: sendpushresponse, | |||||
| wireproto.pusherr: sendpusherror, | |||||
| wireproto.ooberror: sendooberror, | |||||
| } | |||||
| def serve_one(self): | |||||
| cmd = self.fin.readline()[:-1] | |||||
| if cmd and cmd in wireproto.commands: | |||||
| rsp = wireproto.dispatch(self.repo, self, cmd) | |||||
| self.handlers[rsp.__class__](self, rsp) | |||||
| elif cmd: | |||||
| impl = getattr(self, 'do_' + cmd, None) | |||||
| if impl: | |||||
| r = impl() | |||||
| if r is not None: | |||||
| self.sendresponse(r) | |||||
| else: | |||||
| self.sendresponse("") | |||||
| return cmd != '' | |||||
| def _client(self): | |||||
| client = encoding.environ.get('SSH_CLIENT', '').split(' ', 1)[0] | |||||
| return 'remote:ssh:' + client | |||||
| from __future__ import absolute_import, print_function | from __future__ import absolute_import, print_function | ||||
| import io | import io | ||||
| import unittest | import unittest | ||||
| import silenttestrunner | import silenttestrunner | ||||
| from mercurial import ( | from mercurial import ( | ||||
| sshserver, | |||||
| util, | util, | ||||
| wireproto, | wireproto, | ||||
| wireprotoserver, | |||||
| ) | ) | ||||
| class SSHServerGetArgsTests(unittest.TestCase): | class SSHServerGetArgsTests(unittest.TestCase): | ||||
| def testparseknown(self): | def testparseknown(self): | ||||
| tests = [ | tests = [ | ||||
| ('* 0\nnodes 0\n', ['', {}]), | ('* 0\nnodes 0\n', ['', {}]), | ||||
| ('* 0\nnodes 40\n1111111111111111111111111111111111111111\n', | ('* 0\nnodes 40\n1111111111111111111111111111111111111111\n', | ||||
| ['1111111111111111111111111111111111111111', {}]), | ['1111111111111111111111111111111111111111', {}]), | ||||
| ] | ] | ||||
| for input, expected in tests: | for input, expected in tests: | ||||
| self.assertparse('known', input, expected) | self.assertparse('known', input, expected) | ||||
| def assertparse(self, cmd, input, expected): | def assertparse(self, cmd, input, expected): | ||||
| server = mockserver(input) | server = mockserver(input) | ||||
| _func, spec = wireproto.commands[cmd] | _func, spec = wireproto.commands[cmd] | ||||
| self.assertEqual(server.getargs(spec), expected) | self.assertEqual(server.getargs(spec), expected) | ||||
| def mockserver(inbytes): | def mockserver(inbytes): | ||||
| ui = mockui(inbytes) | ui = mockui(inbytes) | ||||
| repo = mockrepo(ui) | repo = mockrepo(ui) | ||||
| return sshserver.sshserver(ui, repo) | return wireprotoserver.sshserver(ui, repo) | ||||
| class mockrepo(object): | class mockrepo(object): | ||||
| def __init__(self, ui): | def __init__(self, ui): | ||||
| self.ui = ui | self.ui = ui | ||||
| class mockui(object): | class mockui(object): | ||||
| def __init__(self, inbytes): | def __init__(self, inbytes): | ||||
| self.fin = io.BytesIO(inbytes) | self.fin = io.BytesIO(inbytes) | ||||
| self.fout = io.BytesIO() | self.fout = io.BytesIO() | ||||
| self.ferr = io.BytesIO() | self.ferr = io.BytesIO() | ||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||
| # Don't call into msvcrt to set BytesIO to binary mode | # Don't call into msvcrt to set BytesIO to binary mode | ||||
| util.setbinary = lambda fp: True | util.setbinary = lambda fp: True | ||||
| silenttestrunner.main(__name__) | silenttestrunner.main(__name__) | ||||