##// END OF EJS Templates
config: move edition code in its own module...
config: move edition code in its own module We start to move code related to the command outside of the main commands modules for clarity. This also highlight some flaw in this code and the new flag are missing some error handling. However we will deal with them later. This move removes the needs for a few module import in the `commands.py` which I see as a good sign.

File last commit:

r53018:1938d72e default
r53314:c97e0fd2 default
Show More
wireprotoserver.py
558 lines | 17.7 KiB | text/x-python | PythonLexer
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
Raphaël Gomès
contributor: change mentions of mpm to olivia...
r47575 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 #
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
Matt Harbison
typing: add `from __future__ import annotations` to most files...
r52756 from __future__ import annotations
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874
Gregory Szorc
wireprotoserver: add context manager mechanism for redirecting stdio...
r36083 import contextlib
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 import struct
Gregory Szorc
wireprotoserver: ability to run an SSH server until an event is set...
r36540 import threading
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877 from .i18n import _
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 from . import (
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877 encoding,
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 error,
pycompat,
util,
Gregory Szorc
wireprototypes: move wire protocol response types to new module...
r36090 wireprototypes,
Gregory Szorc
wireproto: rename wireproto to wireprotov1server (API)...
r37803 wireprotov1server,
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 )
Yuya Nishihara
procutil: bulk-replace function calls to point to new module
r37138 from .utils import (
util: extract compression code in `mercurial.utils.compression`...
r42208 compression,
Matt Harbison
wireprotoserver: convert ErrorResponse to bytes...
r47524 stringutil,
Yuya Nishihara
procutil: bulk-replace function calls to point to new module
r37138 )
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874
stringio = util.stringio
urlerr = util.urlerr
urlreq = util.urlreq
Gregory Szorc
wireprotoserver: don't import symbol from hgweb.common...
r35876 HTTP_OK = 200
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 HGTYPE = b'application/mercurial-0.1'
HGTYPE2 = b'application/mercurial-0.2'
HGERRTYPE = b'application/hg-error'
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874
Gregory Szorc
wireprotoserver: move SSHV1 and SSHV2 constants to wireprototypes...
r36553 SSHV1 = wireprototypes.SSHV1
Gregory Szorc
sshpeer: initial definition and implementation of new SSH protocol...
r35994
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
wireprotoserver: access headers through parsed request...
r36862 def decodevaluefromheaders(req, headerprefix):
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 """Decode a long value from multiple HTTP request headers.
Returns the value as a bytes, not a str.
"""
chunks = []
i = 1
while True:
Gregory Szorc
wireprotoserver: access headers through parsed request...
r36862 v = req.headers.get(b'%s-%d' % (headerprefix, i))
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 if v is None:
break
chunks.append(pycompat.bytesurl(v))
i += 1
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return b''.join(chunks)
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Harbison
wireprotoserver: subclass the new `baseprotocolhandler` Protocol class
r53018 class httpv1protocolhandler(wireprototypes.baseprotocolhandler):
Gregory Szorc
hgweb: expose URL scheme and REMOTE_* attributes...
r36883 def __init__(self, req, ui, checkperm):
Gregory Szorc
wireprotoserver: access headers through parsed request...
r36862 self._req = req
Gregory Szorc
wireprotoserver: make attributes private...
r35884 self._ui = ui
Gregory Szorc
wireproto: formalize permissions checking as part of protocol interface...
r36819 self._checkperm = checkperm
Joerg Sonnenberger
wireproto: provide accessors for client capabilities...
r37411 self._protocaps = None
Gregory Szorc
wireprotoserver: make name part of protocol interface...
r35891
@property
Matt Harbison
wireprotoserver: subclass the new `baseprotocolhandler` Protocol class
r53018 def name(self) -> bytes:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return b'http-v1'
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874
def getargs(self, args):
knownargs = self._args()
data = {}
keys = args.split()
for k in keys:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if k == b'*':
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 star = {}
for key in knownargs.keys():
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if key != b'cmd' and key not in keys:
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 star[key] = knownargs[key][0]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 data[b'*'] = star
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 else:
data[k] = knownargs[k][0]
return [data[k] for k in keys]
Gregory Szorc
wireprotoserver: add some blank lines between methods...
r35881
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 def _args(self):
Gregory Szorc
hgweb: use a multidict for holding query string parameters...
r36878 args = self._req.qsparams.asdictoflists()
Gregory Szorc
wireprotoserver: access headers through parsed request...
r36862 postlen = int(self._req.headers.get(b'X-HgArgs-Post', 0))
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 if postlen:
Augie Fackler
formatting: blacken the codebase...
r43346 args.update(
urlreq.parseqs(
self._req.bodyfh.read(postlen), keep_blank_values=True
)
)
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 return args
Gregory Szorc
wireprotoserver: access headers through parsed request...
r36862 argvalue = decodevaluefromheaders(self._req, b'X-HgArg')
Gregory Szorc
wireprotoserver: define and use parse_qs from urllib...
r36094 args.update(urlreq.parseqs(argvalue, keep_blank_values=True))
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 return args
Gregory Szorc
wireprotoserver: add some blank lines between methods...
r35881
Joerg Sonnenberger
wireproto: provide accessors for client capabilities...
r37411 def getprotocaps(self):
if self._protocaps is None:
Augie Fackler
wireprotoserver: headers are bytes for us internally, use bytes...
r37608 value = decodevaluefromheaders(self._req, b'X-HgProto')
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self._protocaps = set(value.split(b' '))
Joerg Sonnenberger
wireproto: provide accessors for client capabilities...
r37411 return self._protocaps
Joerg Sonnenberger
wireproto: allow direct stream processing for unbundle...
r37432 def getpayload(self):
Gregory Szorc
hgweb: handle CONTENT_LENGTH...
r36863 # Existing clients *always* send Content-Length.
length = int(self._req.headers[b'Content-Length'])
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 # If httppostargs is used, we need to read Content-Length
# minus the amount that was consumed by args.
Gregory Szorc
wireprotoserver: access headers through parsed request...
r36862 length -= int(self._req.headers.get(b'X-HgArgs-Post', 0))
Joerg Sonnenberger
wireproto: allow direct stream processing for unbundle...
r37432 return util.filechunkiter(self._req.bodyfh, limit=length)
Gregory Szorc
wireprotoserver: add some blank lines between methods...
r35881
Gregory Szorc
wireprotoserver: add context manager mechanism for redirecting stdio...
r36083 @contextlib.contextmanager
def mayberedirectstdio(self):
oldout = self._ui.fout
olderr = self._ui.ferr
out = util.stringio()
try:
self._ui.fout = out
self._ui.ferr = out
yield out
finally:
self._ui.fout = oldout
self._ui.ferr = olderr
Gregory Szorc
wireprotoserver: rename _client to client (API)...
r36086 def client(self):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return b'remote:%s:%s:%s' % (
Gregory Szorc
hgweb: expose URL scheme and REMOTE_* attributes...
r36883 self._req.urlscheme,
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 urlreq.quote(self._req.remotehost or b''),
urlreq.quote(self._req.remoteuser or b''),
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874
Gregory Szorc
wireproto: add transport specific capabilities in the transport...
r36631 def addcapabilities(self, repo, caps):
Gregory Szorc
wireproto: nominally don't expose "batch" to version 2 wire transports...
r37071 caps.append(b'batch')
Augie Fackler
formatting: blacken the codebase...
r43346 caps.append(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'httpheader=%d' % repo.ui.configint(b'server', b'maxhttpheaderlen')
Augie Fackler
formatting: blacken the codebase...
r43346 )
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if repo.ui.configbool(b'experimental', b'httppostargs'):
caps.append(b'httppostargs')
Gregory Szorc
wireproto: add transport specific capabilities in the transport...
r36631
# FUTURE advertise 0.2rx once support is implemented
# FUTURE advertise minrx and mintx after consulting config option
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 caps.append(b'httpmediatype=0.1rx,0.1tx,0.2tx')
Gregory Szorc
wireproto: add transport specific capabilities in the transport...
r36631
Augie Fackler
formatting: blacken the codebase...
r43346 compengines = wireprototypes.supportedcompengines(
repo.ui, compression.SERVERROLE
)
Gregory Szorc
wireproto: add transport specific capabilities in the transport...
r36631 if compengines:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 comptypes = b','.join(
Augie Fackler
formatting: blacken the codebase...
r43346 urlreq.quote(e.wireprotosupport().name) for e in compengines
)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 caps.append(b'compression=%s' % comptypes)
Gregory Szorc
wireproto: add transport specific capabilities in the transport...
r36631
return caps
Gregory Szorc
wireproto: formalize permissions checking as part of protocol interface...
r36819 def checkperm(self, perm):
return self._checkperm(perm)
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
wireprotoserver: return to using iscmd() method...
r36249 # This method exists mostly so that extensions like remotefilelog can
# disable a kludgey legacy method only over http. As of early 2018,
# there are no other known users, so with any luck we can discard this
# hook if remotefilelog becomes a first-party extension.
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 def iscmd(cmd):
Gregory Szorc
wireproto: rename wireproto to wireprotov1server (API)...
r37803 return cmd in wireprotov1server.commands
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
hgweb: transition permissions hooks to modern request type (API)...
r36893 def handlewsgirequest(rctx, req, res, checkperm):
Gregory Szorc
wireprotoserver: move all wire protocol handling logic out of hgweb...
r36830 """Possibly process a wire protocol request.
Gregory Szorc
wireprotoserver: move protocol parsing and dispatch out of hgweb...
r36002
Gregory Szorc
wireprotoserver: move all wire protocol handling logic out of hgweb...
r36830 If the current request is a wire protocol request, the request is
processed by this function.
Gregory Szorc
wireprotoserver: move protocol parsing and dispatch out of hgweb...
r36002
Gregory Szorc
hgweb: only recognize wire protocol commands from query string (BC)...
r36828 ``req`` is a ``parsedrequest`` instance.
Gregory Szorc
hgweb: create dedicated type for WSGI responses...
r36877 ``res`` is a ``wsgiresponse`` instance.
Gregory Szorc
wireprotoserver: move all wire protocol handling logic out of hgweb...
r36830
Gregory Szorc
hgweb: create dedicated type for WSGI responses...
r36877 Returns a bool indicating if the request was serviced. If set, the caller
should stop processing the request, as a response has already been issued.
Gregory Szorc
wireprotoserver: move protocol parsing and dispatch out of hgweb...
r36002 """
Gregory Szorc
wireprotoserver: move all wire protocol handling logic out of hgweb...
r36830 # Avoid cycle involving hg module.
from .hgweb import common as hgwebcommon
Gregory Szorc
wireproto: formalize permissions checking as part of protocol interface...
r36819 repo = rctx.repo
Gregory Szorc
wireprotoserver: move protocol parsing and dispatch out of hgweb...
r36002 # HTTP version 1 wire protocol requests are denoted by a "cmd" query
# string parameter. If it isn't present, this isn't a wire protocol
# request.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if b'cmd' not in req.qsparams:
Gregory Szorc
hgweb: create dedicated type for WSGI responses...
r36877 return False
Gregory Szorc
wireprotoserver: move protocol parsing and dispatch out of hgweb...
r36002
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 cmd = req.qsparams[b'cmd']
Gregory Szorc
wireprotoserver: move protocol parsing and dispatch out of hgweb...
r36002
# The "cmd" request parameter is used by both the wire protocol and hgweb.
# While not all wire protocol commands are available for all transports,
# if we see a "cmd" value that resembles a known wire protocol command, we
# route it to a protocol handler. This is better than routing possible
# wire protocol requests to hgweb because it prevents hgweb from using
# known wire protocol commands and it is less confusing for machine
# clients.
Augie Fackler
wireprotoserver: return to using iscmd() method...
r36249 if not iscmd(cmd):
Gregory Szorc
hgweb: create dedicated type for WSGI responses...
r36877 return False
Gregory Szorc
wireprotoserver: move all wire protocol handling logic out of hgweb...
r36830
# The "cmd" query string argument is only valid on the root path of the
# repo. e.g. ``/?cmd=foo``, ``/repo?cmd=foo``. URL paths within the repo
# like ``/blah?cmd=foo`` are not allowed. So don't recognize the request
# in this case. We send an HTTP 404 for backwards compatibility reasons.
if req.dispatchpath:
Gregory Szorc
hgweb: create dedicated type for WSGI responses...
r36877 res.status = hgwebcommon.statusmessage(404)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 res.headers[b'Content-Type'] = HGTYPE
Gregory Szorc
hgweb: create dedicated type for WSGI responses...
r36877 # TODO This is not a good response to issue for this request. This
# is mostly for BC for now.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 res.setbodybytes(b'0\n%s\n' % b'Not Found')
Gregory Szorc
hgweb: create dedicated type for WSGI responses...
r36877 return True
Gregory Szorc
wireprotoserver: move protocol parsing and dispatch out of hgweb...
r36002
Augie Fackler
formatting: blacken the codebase...
r43346 proto = httpv1protocolhandler(
req, repo.ui, lambda perm: checkperm(rctx, req, perm)
)
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874
Gregory Szorc
wireprotoserver: move all wire protocol handling logic out of hgweb...
r36830 # The permissions checker should be the only thing that can raise an
# ErrorResponse. It is kind of a layer violation to catch an hgweb
# exception here. So consider refactoring into a exception type that
# is associated with the wire protocol.
try:
Gregory Szorc
hgweb: expose URL scheme and REMOTE_* attributes...
r36883 _callhttp(repo, req, res, proto, cmd)
Gregory Szorc
wireprotoserver: move all wire protocol handling logic out of hgweb...
r36830 except hgwebcommon.ErrorResponse as e:
Gregory Szorc
hgweb: create dedicated type for WSGI responses...
r36877 for k, v in e.headers:
res.headers[k] = v
Matt Harbison
wireprotoserver: convert ErrorResponse to bytes...
r47524 res.status = hgwebcommon.statusmessage(
e.code, stringutil.forcebytestr(e)
)
Gregory Szorc
hgweb: create dedicated type for WSGI responses...
r36877 # TODO This response body assumes the failed command was
# "unbundle." That assumption is not always valid.
Matt Harbison
wireprotoserver: convert ErrorResponse to bytes...
r47524 res.setbodybytes(b'0\n%s\n' % stringutil.forcebytestr(e))
Gregory Szorc
wireprotoserver: move all wire protocol handling logic out of hgweb...
r36830
Gregory Szorc
hgweb: create dedicated type for WSGI responses...
r36877 return True
Gregory Szorc
wireprotoserver: move protocol parsing and dispatch out of hgweb...
r36002
Augie Fackler
formatting: blacken the codebase...
r43346
Joerg Sonnenberger
wireproto: provide accessors for client capabilities...
r37411 def _httpresponsetype(ui, proto, prefer_uncompressed):
Gregory Szorc
wireprotoserver: move responsetype() out of http handler...
r36089 """Determine the appropriate response type and compression settings.
Returns a tuple of (mediatype, compengine, engineopts).
"""
# Determine the response media type and compression engine based
# on the request parameters.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if b'0.2' in proto.getprotocaps():
Gregory Szorc
wireprotoserver: move responsetype() out of http handler...
r36089 # All clients are expected to support uncompressed data.
if prefer_uncompressed:
util: extract compression code in `mercurial.utils.compression`...
r42208 return HGTYPE2, compression._noopengine(), {}
Gregory Szorc
wireprotoserver: move responsetype() out of http handler...
r36089
# Now find an agreed upon compression format.
Gregory Szorc
wireproto: rename wireproto to wireprotov1server (API)...
r37803 compformats = wireprotov1server.clientcompressionsupport(proto)
Augie Fackler
formatting: blacken the codebase...
r43346 for engine in wireprototypes.supportedcompengines(
ui, compression.SERVERROLE
):
Gregory Szorc
wireprotoserver: move responsetype() out of http handler...
r36089 if engine.wireprotosupport().name in compformats:
opts = {}
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 level = ui.configint(b'server', b'%slevel' % engine.name())
Gregory Szorc
wireprotoserver: move responsetype() out of http handler...
r36089 if level is not None:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 opts[b'level'] = level
Gregory Szorc
wireprotoserver: move responsetype() out of http handler...
r36089
return HGTYPE2, engine, opts
# No mutually supported compression format. Fall back to the
# legacy protocol.
# Don't allow untrusted settings because disabling compression or
# setting a very high compression level could lead to flooding
# the server's network or CPU.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 opts = {b'level': ui.configint(b'server', b'zliblevel')}
return HGTYPE, util.compengines[b'zlib'], opts
Gregory Szorc
wireprotoserver: move responsetype() out of http handler...
r36089
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
hgweb: expose URL scheme and REMOTE_* attributes...
r36883 def _callhttp(repo, req, res, proto, cmd):
Gregory Szorc
hgweb: create dedicated type for WSGI responses...
r36877 # Avoid cycle involving hg module.
from .hgweb import common as hgwebcommon
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 def genversion2(gen, engine, engineopts):
# application/mercurial-0.2 always sends a payload header
# identifying the compression engine.
name = engine.wireprotosupport().name
assert 0 < len(name) < 256
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield struct.pack(b'B', len(name))
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 yield name
for chunk in gen:
yield chunk
Gregory Szorc
hgweb: create dedicated type for WSGI responses...
r36877 def setresponse(code, contenttype, bodybytes=None, bodygen=None):
if code == HTTP_OK:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 res.status = b'200 Script output follows'
Gregory Szorc
hgweb: create dedicated type for WSGI responses...
r36877 else:
res.status = hgwebcommon.statusmessage(code)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 res.headers[b'Content-Type'] = contenttype
Gregory Szorc
hgweb: create dedicated type for WSGI responses...
r36877
if bodybytes is not None:
res.setbodybytes(bodybytes)
if bodygen is not None:
res.setbodygen(bodygen)
Gregory Szorc
wireproto: rename wireproto to wireprotov1server (API)...
r37803 if not wireprotov1server.commands.commandavailable(cmd, proto):
Augie Fackler
formatting: blacken the codebase...
r43346 setresponse(
HTTP_OK,
HGERRTYPE,
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(
b'requested wire protocol command is not available over '
b'HTTP'
),
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
hgweb: create dedicated type for WSGI responses...
r36877 return
Gregory Szorc
wireproto: function for testing if wire protocol command is available...
r36000
Gregory Szorc
wireproto: rename wireproto to wireprotov1server (API)...
r37803 proto.checkperm(wireprotov1server.commands[cmd].permission)
Gregory Szorc
wireprotoserver: check permissions in main dispatch function...
r36817
Manuel Jacob
hidden: add support to explicitly access hidden changesets via HTTP...
r51312 accesshidden = hgwebcommon.hashiddenaccess(repo, req)
rsp = wireprotov1server.dispatch(repo, proto, cmd, accesshidden)
Gregory Szorc
wireprotoserver: check if command available before calling it...
r36816
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 if isinstance(rsp, bytes):
Gregory Szorc
hgweb: create dedicated type for WSGI responses...
r36877 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp)
Gregory Szorc
wireproto: introduce type for raw byte responses (API)...
r36091 elif isinstance(rsp, wireprototypes.bytesresponse):
Gregory Szorc
hgweb: create dedicated type for WSGI responses...
r36877 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp.data)
Gregory Szorc
wireprototypes: move wire protocol response types to new module...
r36090 elif isinstance(rsp, wireprototypes.streamreslegacy):
Gregory Szorc
hgweb: create dedicated type for WSGI responses...
r36877 setresponse(HTTP_OK, HGTYPE, bodygen=rsp.gen)
Gregory Szorc
wireprototypes: move wire protocol response types to new module...
r36090 elif isinstance(rsp, wireprototypes.streamres):
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 gen = rsp.gen
# This code for compression should not be streamres specific. It
# is here because we only compress streamres at the moment.
Gregory Szorc
wireprotoserver: move responsetype() out of http handler...
r36089 mediatype, engine, engineopts = _httpresponsetype(
Augie Fackler
formatting: blacken the codebase...
r43346 repo.ui, proto, rsp.prefer_uncompressed
)
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 gen = engine.compressstream(gen, engineopts)
if mediatype == HGTYPE2:
gen = genversion2(gen, engine, engineopts)
Gregory Szorc
hgweb: create dedicated type for WSGI responses...
r36877 setresponse(HTTP_OK, mediatype, bodygen=gen)
Gregory Szorc
wireprototypes: move wire protocol response types to new module...
r36090 elif isinstance(rsp, wireprototypes.pushres):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 rsp = b'%d\n%s' % (rsp.res, rsp.output)
Gregory Szorc
hgweb: create dedicated type for WSGI responses...
r36877 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp)
Gregory Szorc
wireprototypes: move wire protocol response types to new module...
r36090 elif isinstance(rsp, wireprototypes.pusherr):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 rsp = b'0\n%s\n' % rsp.res
Gregory Szorc
hgweb: create dedicated type for WSGI responses...
r36877 res.drain = True
setresponse(HTTP_OK, HGTYPE, bodybytes=rsp)
Gregory Szorc
wireprototypes: move wire protocol response types to new module...
r36090 elif isinstance(rsp, wireprototypes.ooberror):
Gregory Szorc
hgweb: create dedicated type for WSGI responses...
r36877 setresponse(HTTP_OK, HGERRTYPE, bodybytes=rsp.message)
else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ProgrammingError(b'hgweb.protocol internal failure', rsp)
Gregory Szorc
wireprotoserver: move error response handling out of hgweb...
r36004
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
wireprotoserver: extract SSH response handling functions...
r36081 def _sshv1respondbytes(fout, value):
"""Send a bytes response for protocol version 1."""
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 fout.write(b'%d\n' % len(value))
Gregory Szorc
wireprotoserver: extract SSH response handling functions...
r36081 fout.write(value)
fout.flush()
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
wireprotoserver: extract SSH response handling functions...
r36081 def _sshv1respondstream(fout, source):
write = fout.write
for chunk in source.gen:
write(chunk)
fout.flush()
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
wireprotoserver: extract SSH response handling functions...
r36081 def _sshv1respondooberror(fout, ferr, rsp):
ferr.write(b'%s\n-\n' % rsp)
ferr.flush()
fout.write(b'\n')
fout.flush()
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Harbison
wireprotoserver: subclass the new `baseprotocolhandler` Protocol class
r53018 class sshv1protocolhandler(wireprototypes.baseprotocolhandler):
Gregory Szorc
wireprotoserver: split ssh protocol handler and server...
r36082 """Handler for requests services via version 1 of SSH protocol."""
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
wireprotoserver: split ssh protocol handler and server...
r36082 def __init__(self, ui, fin, fout):
Gregory Szorc
wireprotoserver: make some instance attributes private...
r35888 self._ui = ui
Gregory Szorc
wireprotoserver: split ssh protocol handler and server...
r36082 self._fin = fin
self._fout = fout
Joerg Sonnenberger
wireproto: provide accessors for client capabilities...
r37411 self._protocaps = set()
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877
Gregory Szorc
wireprotoserver: make name part of protocol interface...
r35891 @property
Matt Harbison
wireprotoserver: subclass the new `baseprotocolhandler` Protocol class
r53018 def name(self) -> bytes:
Gregory Szorc
wireprotoserver: move SSHV1 and SSHV2 constants to wireprototypes...
r36553 return wireprototypes.SSHV1
Gregory Szorc
wireprotoserver: make name part of protocol interface...
r35891
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877 def getargs(self, args):
data = {}
keys = args.split()
Manuel Jacob
py3: replace `pycompat.xrange` by `range`
r50179 for n in range(len(keys)):
Gregory Szorc
wireprotoserver: make some instance attributes private...
r35888 argline = self._fin.readline()[:-1]
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877 arg, l = argline.split()
if arg not in keys:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.Abort(_(b"unexpected parameter %r") % arg)
if arg == b'*':
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877 star = {}
Manuel Jacob
py3: replace `pycompat.xrange` by `range`
r50179 for k in range(int(l)):
Gregory Szorc
wireprotoserver: make some instance attributes private...
r35888 argline = self._fin.readline()[:-1]
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877 arg, l = argline.split()
Gregory Szorc
wireprotoserver: make some instance attributes private...
r35888 val = self._fin.read(int(l))
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877 star[arg] = val
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 data[b'*'] = star
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877 else:
Gregory Szorc
wireprotoserver: make some instance attributes private...
r35888 val = self._fin.read(int(l))
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877 data[arg] = val
return [data[k] for k in keys]
Joerg Sonnenberger
wireproto: provide accessors for client capabilities...
r37411 def getprotocaps(self):
return self._protocaps
Joerg Sonnenberger
wireproto: allow direct stream processing for unbundle...
r37432 def getpayload(self):
Gregory Szorc
wireproto: document the wonky push protocol for SSH...
r36390 # We initially send an empty response. This tells the client it is
# OK to start sending data. If a client sees any other response, it
# interprets it as an error.
_sshv1respondbytes(self._fout, b'')
Gregory Szorc
wireprotoserver: rename getfile() to forwardpayload() (API)...
r36087 # The file is in the form:
#
# <chunk size>\n<chunk>
# ...
# 0\n
Gregory Szorc
wireprotoserver: make some instance attributes private...
r35888 count = int(self._fin.readline())
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877 while count:
Joerg Sonnenberger
wireproto: allow direct stream processing for unbundle...
r37432 yield self._fin.read(count)
Gregory Szorc
wireprotoserver: make some instance attributes private...
r35888 count = int(self._fin.readline())
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877
Gregory Szorc
wireprotoserver: add context manager mechanism for redirecting stdio...
r36083 @contextlib.contextmanager
def mayberedirectstdio(self):
yield None
Gregory Szorc
wireprotoserver: rename _client to client (API)...
r36086 def client(self):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 client = encoding.environ.get(b'SSH_CLIENT', b'').split(b' ', 1)[0]
return b'remote:ssh:' + client
Gregory Szorc
wireprotoserver: split ssh protocol handler and server...
r36082
Gregory Szorc
wireproto: add transport specific capabilities in the transport...
r36631 def addcapabilities(self, repo, caps):
Joerg Sonnenberger
wireproto: provide accessors for client capabilities...
r37411 if self.name == wireprototypes.SSHV1:
caps.append(b'protocaps')
Gregory Szorc
wireproto: nominally don't expose "batch" to version 2 wire transports...
r37071 caps.append(b'batch')
Gregory Szorc
wireproto: add transport specific capabilities in the transport...
r36631 return caps
Gregory Szorc
wireproto: formalize permissions checking as part of protocol interface...
r36819 def checkperm(self, perm):
pass
Augie Fackler
formatting: blacken the codebase...
r43346
Manuel Jacob
hidden: add support to explicitly access hidden changesets with SSH peers...
r51316 def _runsshserver(ui, repo, fin, fout, ev, accesshidden=False):
Gregory Szorc
wireprotoserver: handle SSH protocol version 2 upgrade requests...
r36233 # This function operates like a state machine of sorts. The following
# states are defined:
#
# protov1-serving
# Server is in protocol version 1 serving mode. Commands arrive on
# new lines. These commands are processed in this state, one command
# after the other.
#
# shutdown
# The server is shutting down, possibly in reaction to a client event.
#
# And here are their transitions:
#
# protov1-serving -> shutdown
# When server receives an empty request or encounters another
# error.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 state = b'protov1-serving'
Gregory Szorc
wireprotoserver: move SSH server operation to a standalone function...
r36232 proto = sshv1protocolhandler(ui, fin, fout)
Gregory Szorc
wireprotoserver: ability to run an SSH server until an event is set...
r36540 while not ev.is_set():
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if state == b'protov1-serving':
Gregory Szorc
wireprotoserver: move SSH server operation to a standalone function...
r36232 # Commands are issued on new lines.
request = fin.readline()[:-1]
# Empty lines signal to terminate the connection.
if not request:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 state = b'shutdown'
Gregory Szorc
wireprotoserver: move SSH server operation to a standalone function...
r36232 continue
Gregory Szorc
wireproto: rename wireproto to wireprotov1server (API)...
r37803 available = wireprotov1server.commands.commandavailable(
Augie Fackler
formatting: blacken the codebase...
r43346 request, proto
)
Gregory Szorc
wireprotoserver: move SSH server operation to a standalone function...
r36232
# This command isn't available. Send an empty response and go
# back to waiting for a new command.
if not available:
_sshv1respondbytes(fout, b'')
continue
Manuel Jacob
hidden: add support to explicitly access hidden changesets with SSH peers...
r51316 rsp = wireprotov1server.dispatch(
repo, proto, request, accesshidden=accesshidden
)
sshserver: flush stream after command dispatch...
r43166 repo.ui.fout.flush()
repo.ui.ferr.flush()
Gregory Szorc
wireprotoserver: move SSH server operation to a standalone function...
r36232
if isinstance(rsp, bytes):
_sshv1respondbytes(fout, rsp)
elif isinstance(rsp, wireprototypes.bytesresponse):
_sshv1respondbytes(fout, rsp.data)
elif isinstance(rsp, wireprototypes.streamres):
_sshv1respondstream(fout, rsp)
elif isinstance(rsp, wireprototypes.streamreslegacy):
_sshv1respondstream(fout, rsp)
elif isinstance(rsp, wireprototypes.pushres):
_sshv1respondbytes(fout, b'')
_sshv1respondbytes(fout, b'%d' % rsp.res)
elif isinstance(rsp, wireprototypes.pusherr):
_sshv1respondbytes(fout, rsp.res)
elif isinstance(rsp, wireprototypes.ooberror):
_sshv1respondooberror(fout, ui.ferr, rsp.message)
else:
Augie Fackler
formatting: blacken the codebase...
r43346 raise error.ProgrammingError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'unhandled response type from '
b'wire protocol command: %s' % rsp
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
wireprotoserver: move SSH server operation to a standalone function...
r36232
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif state == b'shutdown':
Gregory Szorc
wireprotoserver: move SSH server operation to a standalone function...
r36232 break
else:
Augie Fackler
formatting: blacken the codebase...
r43346 raise error.ProgrammingError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'unhandled ssh server state: %s' % state
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
wireprotoserver: move SSH server operation to a standalone function...
r36232
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class sshserver:
Manuel Jacob
hidden: add support to explicitly access hidden changesets with SSH peers...
r51316 def __init__(self, ui, repo, logfh=None, accesshidden=False):
Gregory Szorc
wireprotoserver: split ssh protocol handler and server...
r36082 self._ui = ui
self._repo = repo
Manuel Jacob
hidden: add support to explicitly access hidden changesets with SSH peers...
r51316 self._accesshidden = accesshidden
Arseniy Alekseyev
wireprotoserver: ensure that output stream gets flushed on exception...
r52465 self._logfh = logfh
Gregory Szorc
wireprotoserver: support logging SSH server I/O to a file descriptor...
r36543
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877 def serve_forever(self):
Gregory Szorc
wireprotoserver: ability to run an SSH server until an event is set...
r36540 self.serveuntil(threading.Event())
def serveuntil(self, ev):
"""Serve until a threading.Event is set."""
Arseniy Alekseyev
wireprotoserver: ensure that output stream gets flushed on exception...
r52465 with self._ui.protectedfinout() as (fin, fout):
if self._logfh:
# Log write I/O to stdout and stderr if configured.
fout = util.makeloggingfileobject(
self._logfh,
fout,
b'o',
logdata=True,
)
self._ui.ferr = util.makeloggingfileobject(
self._logfh,
self._ui.ferr,
b'e',
logdata=True,
)
_runsshserver(
self._ui,
self._repo,
fin,
fout,
ev,
self._accesshidden,
)