##// END OF EJS Templates
interfaces: convert `repository.imanifestlog` from zope `Attribute` attrs...
interfaces: convert `repository.imanifestlog` from zope `Attribute` attrs This is the same transformation as b455dfddfed0 did for dirstate.

File last commit:

r53018:1938d72e default
r53379:ed70604d 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,
)