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.
Lint Skipped |
Unit Tests Skipped |
Path | Packages | |||
---|---|---|---|---|
M | mercurial/commands.py (4 lines) | |||
D | M | mercurial/sshserver.py (131 lines) | ||
M | mercurial/wireprotoserver.py (114 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 . 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__) |