##// END OF EJS Templates
pyoxidizer: produce working Python 3 Windows installers (issue6366)...
pyoxidizer: produce working Python 3 Windows installers (issue6366) While we've had code to produce Python 3 Windows installers with PyOxidizer, we haven't been advertising them on the web site due to a bug in making TLS connections and issues around resource handling. This commit upgrades our PyOxidizer install and configuration to use a recent Git commit of PyOxidizer. This new version of PyOxidizer contains a *ton* of changes, improvements, and bug fixes. Notably, Windows shared distributions now mostly "just work" and the TLS bug and random problems with Python extension modules in the standard library go away. And Python has been upgraded from 3.7 to 3.8.6. The price we pay for this upgrade is a ton of backwards incompatible changes to Starlark. I applied this commit (the overall series actually) on stable to produce Windows installers for Mercurial 5.5.2, which I published shortly before submitting this commit for review. In order to get the stable branch working, I decided to take a less aggressive approach to Python resource management. Previously, we were attempting to load all Python modules from memory and were performing some hacks to copy Mercurial's non-module resources into additional directories in Starlark. This commit implements a resource callback function in Starlark (a new feature since PyOxidizer 0.7) to dynamically assign standard library resources to in-memory loading and all other resources to filesystem loading. This means that Mercurial's files and all the other packages we ship in the Windows installers (e.g. certifi and pygments) are loaded from the filesystem instead of from memory. This avoids issues due to lack of __file__ and enables us to ship a working Python 3 installer on Windows. The end state of the install layout after this patch is not ideal for @: we still copy resource files like templates and help text to directories next to the hg.exe executable. There is code in @ to use importlib.resources to load these files and we could likely remove these copies once this lands on @. But for now, the install layout mimics what we've shipped for seemingly forever and is backwards compatible. It allows us to achieve the milestone of working Python 3 Windows installers and gets us a giant step closer to deleting Python 2. Differential Revision: https://phab.mercurial-scm.org/D9148

File last commit:

r43387:8ff1ecfa default
r46277:57b5452a default
Show More
wireprotoserver.py
858 lines | 27.5 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>
# Copyright 2005-2007 Matt Mackall <mpm@selenic.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
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: move sshserver into module (API)...
r35877 import sys
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
wireproto: extract HTTP version 2 code to own module...
r37563 wireprotov2server,
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 )
Augie Fackler
formatting: blacken the codebase...
r43346 from .interfaces import util as interfaceutil
Yuya Nishihara
procutil: bulk-replace function calls to point to new module
r37138 from .utils import (
Gregory Szorc
wireprotoserver: use our CBOR encoder...
r39478 cborutil,
util: extract compression code in `mercurial.utils.compression`...
r42208 compression,
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
SSHV2 = wireprototypes.SSHV2
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
Gregory Szorc
interfaceutil: module to stub out zope.interface...
r37828 @interfaceutil.implementer(wireprototypes.baseprotocolhandler)
Gregory Szorc
wireproto: port protocol handler to zope.interface...
r37312 class httpv1protocolhandler(object):
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
def name(self):
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
res.status = hgwebcommon.statusmessage(e.code, pycompat.bytestr(e))
# TODO This response body assumes the failed command was
# "unbundle." That assumption is not always valid.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 res.setbodybytes(b'0\n%s\n' % pycompat.bytestr(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
Gregory Szorc
wireproto: define and implement HTTP handshake to upgrade protocol...
r37575 def _availableapis(repo):
apis = set()
# Registered APIs are made available via config options of the name of
# the protocol.
for k, v in API_HANDLERS.items():
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 section, option = v[b'config']
Gregory Szorc
wireproto: define and implement HTTP handshake to upgrade protocol...
r37575 if repo.ui.configbool(section, option):
apis.add(k)
return apis
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
wireproto: support /api/* URL space for exposing APIs...
r37064 def handlewsgiapirequest(rctx, req, res, checkperm):
"""Handle requests to /api/*."""
assert req.dispatchparts[0] == b'api'
repo = rctx.repo
# This whole URL space is experimental for now. But we want to
# reserve the URL space. So, 404 all URLs if the feature isn't enabled.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if not repo.ui.configbool(b'experimental', b'web.apiserver'):
Gregory Szorc
wireproto: support /api/* URL space for exposing APIs...
r37064 res.status = b'404 Not Found'
res.headers[b'Content-Type'] = b'text/plain'
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 res.setbodybytes(_(b'Experimental API server endpoint not enabled'))
Gregory Szorc
wireproto: support /api/* URL space for exposing APIs...
r37064 return
# The URL space is /api/<protocol>/*. The structure of URLs under varies
# by <protocol>.
Gregory Szorc
wireproto: define and implement HTTP handshake to upgrade protocol...
r37575 availableapis = _availableapis(repo)
Gregory Szorc
wireproto: support /api/* URL space for exposing APIs...
r37064
# Requests to /api/ list available APIs.
if req.dispatchparts == [b'api']:
res.status = b'200 OK'
res.headers[b'Content-Type'] = b'text/plain'
Augie Fackler
formatting: blacken the codebase...
r43346 lines = [
_(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'APIs can be accessed at /api/<name>, where <name> can be '
b'one of the following:\n'
Augie Fackler
formatting: blacken the codebase...
r43346 )
]
Gregory Szorc
wireproto: support /api/* URL space for exposing APIs...
r37064 if availableapis:
lines.extend(sorted(availableapis))
else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 lines.append(_(b'(no available APIs)\n'))
Gregory Szorc
wireproto: support /api/* URL space for exposing APIs...
r37064 res.setbodybytes(b'\n'.join(lines))
return
proto = req.dispatchparts[1]
if proto not in API_HANDLERS:
res.status = b'404 Not Found'
res.headers[b'Content-Type'] = b'text/plain'
Augie Fackler
formatting: blacken the codebase...
r43346 res.setbodybytes(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b'Unknown API: %s\nKnown APIs: %s')
Augie Fackler
formatting: blacken the codebase...
r43346 % (proto, b', '.join(sorted(availableapis)))
)
Gregory Szorc
wireproto: support /api/* URL space for exposing APIs...
r37064 return
if proto not in availableapis:
res.status = b'404 Not Found'
res.headers[b'Content-Type'] = b'text/plain'
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 res.setbodybytes(_(b'API %s not enabled\n') % proto)
Gregory Szorc
wireproto: support /api/* URL space for exposing APIs...
r37064 return
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 API_HANDLERS[proto][b'handler'](
Augie Fackler
formatting: blacken the codebase...
r43346 rctx, req, res, checkperm, req.dispatchparts[2:]
)
Gregory Szorc
wireproto: support /api/* URL space for exposing APIs...
r37064
# Maps API name to metadata so custom API can be registered.
Gregory Szorc
wireproto: define and implement HTTP handshake to upgrade protocol...
r37575 # Keys are:
#
# config
# Config option that controls whether service is enabled.
# handler
# Callable receiving (rctx, req, res, checkperm, urlparts) that is called
# when a request to this API is received.
# apidescriptor
# Callable receiving (req, repo) that is called to obtain an API
# descriptor for this service. The response must be serializable to CBOR.
Gregory Szorc
wireproto: support /api/* URL space for exposing APIs...
r37064 API_HANDLERS = {
Gregory Szorc
wireproto: rename HTTPV2 so it less like HTTP/2...
r37662 wireprotov2server.HTTP_WIREPROTO_V2: {
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'config': (b'experimental', b'web.api.http-v2'),
b'handler': wireprotov2server.handlehttpv2request,
b'apidescriptor': wireprotov2server.httpv2apidescriptor,
Gregory Szorc
wireproto: support /api/* URL space for exposing APIs...
r37064 },
}
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
wireproto: define and implement HTTP handshake to upgrade protocol...
r37575 def processcapabilitieshandshake(repo, req, res, proto):
"""Called during a ?cmd=capabilities request.
If the client is advertising support for a newer protocol, we send
a CBOR response with information about available services. If no
advertised services are available, we don't handle the request.
"""
# Fall back to old behavior unless the API server is enabled.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if not repo.ui.configbool(b'experimental', b'web.apiserver'):
Gregory Szorc
wireproto: define and implement HTTP handshake to upgrade protocol...
r37575 return False
clientapis = decodevaluefromheaders(req, b'X-HgUpgrade')
protocaps = decodevaluefromheaders(req, b'X-HgProto')
if not clientapis or not protocaps:
return False
# We currently only support CBOR responses.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 protocaps = set(protocaps.split(b' '))
Gregory Szorc
wireproto: define and implement HTTP handshake to upgrade protocol...
r37575 if b'cbor' not in protocaps:
return False
descriptors = {}
for api in sorted(set(clientapis.split()) & _availableapis(repo)):
handler = API_HANDLERS[api]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 descriptorfn = handler.get(b'apidescriptor')
Gregory Szorc
wireproto: define and implement HTTP handshake to upgrade protocol...
r37575 if not descriptorfn:
continue
descriptors[api] = descriptorfn(req, repo)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 v1caps = wireprotov1server.dispatch(repo, proto, b'capabilities')
Gregory Szorc
wireproto: define and implement HTTP handshake to upgrade protocol...
r37575 assert isinstance(v1caps, wireprototypes.bytesresponse)
m = {
# TODO allow this to be configurable.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'apibase': b'api/',
b'apis': descriptors,
b'v1capabilities': v1caps.data,
Gregory Szorc
wireproto: define and implement HTTP handshake to upgrade protocol...
r37575 }
res.status = b'200 OK'
res.headers[b'Content-Type'] = b'application/mercurial-cbor'
Gregory Szorc
wireprotoserver: use our CBOR encoder...
r39478 res.setbodybytes(b''.join(cborutil.streamencode(m)))
Gregory Szorc
wireproto: define and implement HTTP handshake to upgrade protocol...
r37575
return True
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
Gregory Szorc
wireproto: define and implement HTTP handshake to upgrade protocol...
r37575 # Possibly handle a modern client wanting to switch protocols.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if cmd == b'capabilities' and processcapabilitieshandshake(
Augie Fackler
formatting: blacken the codebase...
r43346 repo, req, res, proto
):
Gregory Szorc
wireproto: define and implement HTTP handshake to upgrade protocol...
r37575
return
Gregory Szorc
wireproto: rename wireproto to wireprotov1server (API)...
r37803 rsp = wireprotov1server.dispatch(repo, proto, cmd)
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
Gregory Szorc
interfaceutil: module to stub out zope.interface...
r37828 @interfaceutil.implementer(wireprototypes.baseprotocolhandler)
Gregory Szorc
wireproto: port protocol handler to zope.interface...
r37312 class sshv1protocolhandler(object):
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
def name(self):
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()
Gregory Szorc
global: use pycompat.xrange()...
r38806 for n in pycompat.xrange(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 = {}
Gregory Szorc
global: use pycompat.xrange()...
r38806 for k in pycompat.xrange(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
Gregory Szorc
wireprotoserver: handle SSH protocol version 2 upgrade requests...
r36233 class sshv2protocolhandler(sshv1protocolhandler):
"""Protocol handler for version 2 of the SSH protocol."""
Gregory Szorc
wireprotoserver: identify requests via version 2 of SSH protocol as such...
r36628 @property
def name(self):
return wireprototypes.SSHV2
Gregory Szorc
wireproto: implement capabilities for wire protocol v2...
r37551 def addcapabilities(self, repo, caps):
return caps
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
wireprotoserver: ability to run an SSH server until an event is set...
r36540 def _runsshserver(ui, repo, fin, fout, ev):
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.
#
# protov2-serving
# Server is in protocol version 2 serving mode.
#
# upgrade-initial
# The server is going to process an upgrade request.
#
# upgrade-v2-filter-legacy-handshake
# The protocol is being upgraded to version 2. The server is expecting
# the legacy handshake from version 1.
#
# upgrade-v2-finish
# The upgrade to version 2 of the protocol is imminent.
#
# 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.
#
# protov1-serving -> upgrade-initial
# An upgrade request line was seen.
#
# upgrade-initial -> upgrade-v2-filter-legacy-handshake
# Upgrade to version 2 in progress. Server is expecting to
# process a legacy handshake.
#
# upgrade-v2-filter-legacy-handshake -> shutdown
# Client did not fulfill upgrade handshake requirements.
#
# upgrade-v2-filter-legacy-handshake -> upgrade-v2-finish
# Client fulfilled version 2 upgrade requirements. Finishing that
# upgrade.
#
# upgrade-v2-finish -> protov2-serving
# Protocol upgrade to version 2 complete. Server can now speak protocol
# version 2.
#
# protov2-serving -> protov1-serving
# Ths happens by default since protocol version 2 is the same as
# version 1 except for the handshake.
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: handle SSH protocol version 2 upgrade requests...
r36233 protoswitched = False
Gregory Szorc
wireprotoserver: move SSH server operation to a standalone function...
r36232
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
wireprotoserver: handle SSH protocol version 2 upgrade requests...
r36233 # It looks like a protocol upgrade request. Transition state to
# handle it.
if request.startswith(b'upgrade '):
if protoswitched:
Augie Fackler
formatting: blacken the codebase...
r43346 _sshv1respondooberror(
fout,
ui.ferr,
Martin von Zweigbergk
cleanup: join string literals that are already on one line...
r43387 b'cannot upgrade protocols multiple times',
Augie Fackler
formatting: blacken the codebase...
r43346 )
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 state = b'shutdown'
Gregory Szorc
wireprotoserver: handle SSH protocol version 2 upgrade requests...
r36233 continue
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 state = b'upgrade-initial'
Gregory Szorc
wireprotoserver: handle SSH protocol version 2 upgrade requests...
r36233 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
Gregory Szorc
wireproto: rename wireproto to wireprotov1server (API)...
r37803 rsp = wireprotov1server.dispatch(repo, proto, request)
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
Gregory Szorc
wireprotoserver: handle SSH protocol version 2 upgrade requests...
r36233 # For now, protocol version 2 serving just goes back to version 1.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif state == b'protov2-serving':
state = b'protov1-serving'
Gregory Szorc
wireprotoserver: handle SSH protocol version 2 upgrade requests...
r36233 continue
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif state == b'upgrade-initial':
Gregory Szorc
wireprotoserver: handle SSH protocol version 2 upgrade requests...
r36233 # We should never transition into this state if we've switched
# protocols.
assert not protoswitched
Gregory Szorc
wireprotoserver: move SSHV1 and SSHV2 constants to wireprototypes...
r36553 assert proto.name == wireprototypes.SSHV1
Gregory Szorc
wireprotoserver: handle SSH protocol version 2 upgrade requests...
r36233
# Expected: upgrade <token> <capabilities>
# If we get something else, the request is malformed. It could be
# from a future client that has altered the upgrade line content.
# We treat this as an unknown command.
try:
token, caps = request.split(b' ')[1:]
except ValueError:
_sshv1respondbytes(fout, b'')
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 state = b'protov1-serving'
Gregory Szorc
wireprotoserver: handle SSH protocol version 2 upgrade requests...
r36233 continue
# Send empty response if we don't support upgrading protocols.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if not ui.configbool(b'experimental', b'sshserver.support-v2'):
Gregory Szorc
wireprotoserver: handle SSH protocol version 2 upgrade requests...
r36233 _sshv1respondbytes(fout, b'')
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 state = b'protov1-serving'
Gregory Szorc
wireprotoserver: handle SSH protocol version 2 upgrade requests...
r36233 continue
try:
caps = urlreq.parseqs(caps)
except ValueError:
_sshv1respondbytes(fout, b'')
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 state = b'protov1-serving'
Gregory Szorc
wireprotoserver: handle SSH protocol version 2 upgrade requests...
r36233 continue
# We don't see an upgrade request to protocol version 2. Ignore
# the upgrade request.
wantedprotos = caps.get(b'proto', [b''])[0]
if SSHV2 not in wantedprotos:
_sshv1respondbytes(fout, b'')
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 state = b'protov1-serving'
Gregory Szorc
wireprotoserver: handle SSH protocol version 2 upgrade requests...
r36233 continue
# It looks like we can honor this upgrade request to protocol 2.
# Filter the rest of the handshake protocol request lines.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 state = b'upgrade-v2-filter-legacy-handshake'
Gregory Szorc
wireprotoserver: handle SSH protocol version 2 upgrade requests...
r36233 continue
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif state == b'upgrade-v2-filter-legacy-handshake':
Gregory Szorc
wireprotoserver: handle SSH protocol version 2 upgrade requests...
r36233 # Client should have sent legacy handshake after an ``upgrade``
# request. Expected lines:
#
# hello
# between
# pairs 81
# 0000...-0000...
ok = True
for line in (b'hello', b'between', b'pairs 81'):
request = fin.readline()[:-1]
if request != line:
Augie Fackler
formatting: blacken the codebase...
r43346 _sshv1respondooberror(
fout,
ui.ferr,
Martin von Zweigbergk
cleanup: join string literals that are already on one line...
r43387 b'malformed handshake protocol: missing %s' % line,
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
wireprotoserver: handle SSH protocol version 2 upgrade requests...
r36233 ok = False
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 state = b'shutdown'
Gregory Szorc
wireprotoserver: handle SSH protocol version 2 upgrade requests...
r36233 break
if not ok:
continue
request = fin.read(81)
if request != b'%s-%s' % (b'0' * 40, b'0' * 40):
Augie Fackler
formatting: blacken the codebase...
r43346 _sshv1respondooberror(
fout,
ui.ferr,
b'malformed handshake protocol: '
b'missing between argument value',
)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 state = b'shutdown'
Gregory Szorc
wireprotoserver: handle SSH protocol version 2 upgrade requests...
r36233 continue
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 state = b'upgrade-v2-finish'
Gregory Szorc
wireprotoserver: handle SSH protocol version 2 upgrade requests...
r36233 continue
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif state == b'upgrade-v2-finish':
Gregory Szorc
wireprotoserver: handle SSH protocol version 2 upgrade requests...
r36233 # Send the upgrade response.
fout.write(b'upgraded %s %s\n' % (token, SSHV2))
Gregory Szorc
wireproto: rename wireproto to wireprotov1server (API)...
r37803 servercaps = wireprotov1server.capabilities(repo, proto)
Gregory Szorc
wireprotoserver: handle SSH protocol version 2 upgrade requests...
r36233 rsp = b'capabilities: %s' % servercaps.data
fout.write(b'%d\n%s\n' % (len(rsp), rsp))
fout.flush()
proto = sshv2protocolhandler(ui, fin, fout)
protoswitched = True
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 state = b'protov2-serving'
Gregory Szorc
wireprotoserver: handle SSH protocol version 2 upgrade requests...
r36233 continue
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
wireprotoserver: split ssh protocol handler and server...
r36082 class sshserver(object):
Gregory Szorc
wireprotoserver: support logging SSH server I/O to a file descriptor...
r36543 def __init__(self, ui, repo, logfh=None):
Gregory Szorc
wireprotoserver: split ssh protocol handler and server...
r36082 self._ui = ui
self._repo = repo
Yuya Nishihara
ui: proxy protect/restorestdio() calls to update internal flag...
r41321 self._fin, self._fout = ui.protectfinout()
Gregory Szorc
wireprotoserver: split ssh protocol handler and server...
r36082
Gregory Szorc
wireprotoserver: support logging SSH server I/O to a file descriptor...
r36543 # Log write I/O to stdout and stderr if configured.
if logfh:
self._fout = util.makeloggingfileobject(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 logfh, self._fout, b'o', logdata=True
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
wireprotoserver: support logging SSH server I/O to a file descriptor...
r36543 ui.ferr = util.makeloggingfileobject(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 logfh, ui.ferr, b'e', logdata=True
Augie Fackler
formatting: blacken the codebase...
r43346 )
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())
Yuya Nishihara
ui: proxy protect/restorestdio() calls to update internal flag...
r41321 self._ui.restorefinout(self._fin, self._fout)
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877 sys.exit(0)
Gregory Szorc
wireprotoserver: ability to run an SSH server until an event is set...
r36540
def serveuntil(self, ev):
"""Serve until a threading.Event is set."""
_runsshserver(self._ui, self._repo, self._fin, self._fout, ev)