##// END OF EJS Templates
py3: factor out helpers to apply string conversion recursively
py3: factor out helpers to apply string conversion recursively

File last commit:

r35918:72de5c50 default
r35918:72de5c50 default
Show More
wireprotoserver.py
357 lines | 10.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>
# 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: make abstractserverproto a proper abstract base class...
r35890 import abc
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 import cgi
import struct
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877 import sys
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,
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877 hook,
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 pycompat,
util,
wireproto,
)
stringio = util.stringio
urlerr = util.urlerr
urlreq = util.urlreq
Gregory Szorc
wireprotoserver: don't import symbol from hgweb.common...
r35876 HTTP_OK = 200
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 HGTYPE = 'application/mercurial-0.1'
HGTYPE2 = 'application/mercurial-0.2'
HGERRTYPE = 'application/hg-error'
Gregory Szorc
wireprotoserver: move abstractserverproto class from wireproto...
r35878 class abstractserverproto(object):
"""abstract class that summarizes the protocol API
Used as reference and documentation.
"""
Gregory Szorc
wireprotoserver: make abstractserverproto a proper abstract base class...
r35890 __metaclass__ = abc.ABCMeta
Gregory Szorc
wireprotoserver: make name part of protocol interface...
r35891 @abc.abstractproperty
def name(self):
"""The name of the protocol implementation.
Used for uniquely identifying the transport type.
"""
Gregory Szorc
wireprotoserver: make abstractserverproto a proper abstract base class...
r35890 @abc.abstractmethod
Gregory Szorc
wireprotoserver: move abstractserverproto class from wireproto...
r35878 def getargs(self, args):
"""return the value for arguments in <args>
returns a list of values (same order as <args>)"""
Gregory Szorc
wireprotoserver: make abstractserverproto a proper abstract base class...
r35890 @abc.abstractmethod
Gregory Szorc
wireprotoserver: move abstractserverproto class from wireproto...
r35878 def getfile(self, fp):
"""write the whole content of a file into a file like object
The file is in the form::
(<chunk-size>\n<chunk>)+0\n
chunk size is the ascii version of the int.
"""
Gregory Szorc
wireprotoserver: make abstractserverproto a proper abstract base class...
r35890 @abc.abstractmethod
Gregory Szorc
wireprotoserver: move abstractserverproto class from wireproto...
r35878 def redirect(self):
"""may setup interception for stdout and stderr
See also the `restore` method."""
# If the `redirect` function does install interception, the `restore`
# function MUST be defined. If interception is not used, this function
# MUST NOT be defined.
#
# left commented here on purpose
#
#def restore(self):
# """reinstall previous stdout and stderr and return intercepted stdout
# """
# raise NotImplementedError()
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 def decodevaluefromheaders(req, headerprefix):
"""Decode a long value from multiple HTTP request headers.
Returns the value as a bytes, not a str.
"""
chunks = []
i = 1
prefix = headerprefix.upper().replace(r'-', r'_')
while True:
v = req.env.get(r'HTTP_%s_%d' % (prefix, i))
if v is None:
break
chunks.append(pycompat.bytesurl(v))
i += 1
return ''.join(chunks)
Gregory Szorc
wireprotoserver: move abstractserverproto class from wireproto...
r35878 class webproto(abstractserverproto):
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 def __init__(self, req, ui):
Gregory Szorc
wireprotoserver: make attributes private...
r35884 self._req = req
self._ui = ui
Gregory Szorc
wireprotoserver: make name part of protocol interface...
r35891
@property
def name(self):
return 'http'
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:
if k == '*':
star = {}
for key in knownargs.keys():
if key != 'cmd' and key not in keys:
star[key] = knownargs[key][0]
data['*'] = star
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):
Yuya Nishihara
py3: factor out helpers to apply string conversion recursively
r35918 args = util.rapply(pycompat.bytesurl, self._req.form.copy())
Gregory Szorc
wireprotoserver: make attributes private...
r35884 postlen = int(self._req.env.get(r'HTTP_X_HGARGS_POST', 0))
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 if postlen:
args.update(cgi.parse_qs(
Gregory Szorc
wireprotoserver: make attributes private...
r35884 self._req.read(postlen), keep_blank_values=True))
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 return args
Gregory Szorc
wireprotoserver: make attributes private...
r35884 argvalue = decodevaluefromheaders(self._req, r'X-HgArg')
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 args.update(cgi.parse_qs(argvalue, keep_blank_values=True))
return args
Gregory Szorc
wireprotoserver: add some blank lines between methods...
r35881
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 def getfile(self, fp):
Gregory Szorc
wireprotoserver: make attributes private...
r35884 length = int(self._req.env[r'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: make attributes private...
r35884 length -= int(self._req.env.get(r'HTTP_X_HGARGS_POST', 0))
for s in util.filechunkiter(self._req, limit=length):
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 fp.write(s)
Gregory Szorc
wireprotoserver: add some blank lines between methods...
r35881
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 def redirect(self):
Gregory Szorc
wireprotoserver: make attributes private...
r35884 self._oldio = self._ui.fout, self._ui.ferr
self._ui.ferr = self._ui.fout = stringio()
Gregory Szorc
wireprotoserver: add some blank lines between methods...
r35881
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 def restore(self):
Gregory Szorc
wireprotoserver: make attributes private...
r35884 val = self._ui.fout.getvalue()
self._ui.ferr, self._ui.fout = self._oldio
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 return val
def _client(self):
return 'remote:%s:%s:%s' % (
Gregory Szorc
wireprotoserver: make attributes private...
r35884 self._req.env.get('wsgi.url_scheme') or 'http',
urlreq.quote(self._req.env.get('REMOTE_HOST', '')),
urlreq.quote(self._req.env.get('REMOTE_USER', '')))
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874
def responsetype(self, prefer_uncompressed):
"""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.
Gregory Szorc
wireprotoserver: make attributes private...
r35884 protocaps = decodevaluefromheaders(self._req, r'X-HgProto').split(' ')
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874
if '0.2' in protocaps:
# All clients are expected to support uncompressed data.
if prefer_uncompressed:
return HGTYPE2, util._noopengine(), {}
# Default as defined by wire protocol spec.
compformats = ['zlib', 'none']
for cap in protocaps:
if cap.startswith('comp='):
compformats = cap[5:].split(',')
break
# Now find an agreed upon compression format.
Gregory Szorc
wireprotoserver: make attributes private...
r35884 for engine in wireproto.supportedcompengines(self._ui, self,
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 util.SERVERROLE):
if engine.wireprotosupport().name in compformats:
opts = {}
Gregory Szorc
wireprotoserver: make attributes private...
r35884 level = self._ui.configint('server',
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 '%slevel' % engine.name())
if level is not None:
opts['level'] = level
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.
Gregory Szorc
wireprotoserver: make attributes private...
r35884 opts = {'level': self._ui.configint('server', 'zliblevel')}
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 return HGTYPE, util.compengines['zlib'], opts
def iscmd(cmd):
return cmd in wireproto.commands
Gregory Szorc
wireprotoserver: rename call to callhttp...
r35875 def callhttp(repo, req, cmd):
Gregory Szorc
wireprotoserver: rename p to proto...
r35882 proto = webproto(req, repo.ui)
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
yield struct.pack('B', len(name))
yield name
for chunk in gen:
yield chunk
Gregory Szorc
wireprotoserver: rename p to proto...
r35882 rsp = wireproto.dispatch(repo, proto, cmd)
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 if isinstance(rsp, bytes):
req.respond(HTTP_OK, HGTYPE, body=rsp)
return []
elif isinstance(rsp, wireproto.streamres_legacy):
gen = rsp.gen
req.respond(HTTP_OK, HGTYPE)
return gen
elif isinstance(rsp, wireproto.streamres):
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: rename p to proto...
r35882 mediatype, engine, engineopts = proto.responsetype(
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)
req.respond(HTTP_OK, mediatype)
return gen
elif isinstance(rsp, wireproto.pushres):
Gregory Szorc
wireprotoserver: rename p to proto...
r35882 val = proto.restore()
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 rsp = '%d\n%s' % (rsp.res, val)
req.respond(HTTP_OK, HGTYPE, body=rsp)
return []
elif isinstance(rsp, wireproto.pusherr):
# drain the incoming bundle
req.drain()
Gregory Szorc
wireprotoserver: rename p to proto...
r35882 proto.restore()
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 rsp = '0\n%s\n' % rsp.res
req.respond(HTTP_OK, HGTYPE, body=rsp)
return []
elif isinstance(rsp, wireproto.ooberror):
rsp = rsp.message
req.respond(HTTP_OK, HGERRTYPE, body=rsp)
return []
raise error.ProgrammingError('hgweb.protocol internal failure', rsp)
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877
Gregory Szorc
wireprotoserver: move abstractserverproto class from wireproto...
r35878 class sshserver(abstractserverproto):
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877 def __init__(self, ui, repo):
Gregory Szorc
wireprotoserver: make some instance attributes private...
r35888 self._ui = ui
self._repo = repo
self._fin = ui.fin
self._fout = ui.fout
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877
hook.redirect(True)
ui.fout = repo.ui.fout = ui.ferr
# Prevent insertion/deletion of CRs
Gregory Szorc
wireprotoserver: make some instance attributes private...
r35888 util.setbinary(self._fin)
util.setbinary(self._fout)
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877
Gregory Szorc
wireprotoserver: make name part of protocol interface...
r35891 @property
def name(self):
return 'ssh'
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877 def getargs(self, args):
data = {}
keys = args.split()
for n in 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:
raise error.Abort(_("unexpected parameter %r") % arg)
if arg == '*':
star = {}
for k in 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
data['*'] = star
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]
def getfile(self, fpout):
Gregory Szorc
wireprotoserver: make response handling attributes private...
r35889 self._sendresponse('')
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:
Gregory Szorc
wireprotoserver: make some instance attributes private...
r35888 fpout.write(self._fin.read(count))
count = int(self._fin.readline())
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877
def redirect(self):
pass
Gregory Szorc
wireprotoserver: make response handling attributes private...
r35889 def _sendresponse(self, v):
Gregory Szorc
wireprotoserver: make some instance attributes private...
r35888 self._fout.write("%d\n" % len(v))
self._fout.write(v)
self._fout.flush()
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877
Gregory Szorc
wireprotoserver: make response handling attributes private...
r35889 def _sendstream(self, source):
Gregory Szorc
wireprotoserver: make some instance attributes private...
r35888 write = self._fout.write
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877 for chunk in source.gen:
write(chunk)
Gregory Szorc
wireprotoserver: make some instance attributes private...
r35888 self._fout.flush()
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877
Gregory Szorc
wireprotoserver: make response handling attributes private...
r35889 def _sendpushresponse(self, rsp):
self._sendresponse('')
self._sendresponse(str(rsp.res))
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877
Gregory Szorc
wireprotoserver: make response handling attributes private...
r35889 def _sendpusherror(self, rsp):
self._sendresponse(rsp.res)
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877
Gregory Szorc
wireprotoserver: make response handling attributes private...
r35889 def _sendooberror(self, rsp):
Gregory Szorc
wireprotoserver: make some instance attributes private...
r35888 self._ui.ferr.write('%s\n-\n' % rsp.message)
self._ui.ferr.flush()
self._fout.write('\n')
self._fout.flush()
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877
def serve_forever(self):
Gregory Szorc
wireprotoserver: remove lock references...
r35886 while self.serve_one():
pass
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877 sys.exit(0)
Gregory Szorc
wireprotoserver: make response handling attributes private...
r35889 _handlers = {
str: _sendresponse,
wireproto.streamres: _sendstream,
wireproto.streamres_legacy: _sendstream,
wireproto.pushres: _sendpushresponse,
wireproto.pusherr: _sendpusherror,
wireproto.ooberror: _sendooberror,
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877 }
def serve_one(self):
Gregory Szorc
wireprotoserver: make some instance attributes private...
r35888 cmd = self._fin.readline()[:-1]
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877 if cmd and cmd in wireproto.commands:
Gregory Szorc
wireprotoserver: make some instance attributes private...
r35888 rsp = wireproto.dispatch(self._repo, self, cmd)
Gregory Szorc
wireprotoserver: make response handling attributes private...
r35889 self._handlers[rsp.__class__](self, rsp)
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877 elif cmd:
Gregory Szorc
wireprotoserver: make response handling attributes private...
r35889 self._sendresponse("")
Gregory Szorc
wireprotoserver: move sshserver into module (API)...
r35877 return cmd != ''
def _client(self):
client = encoding.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
return 'remote:ssh:' + client