mercurial/hgweb/protocol.py
changeset 30764 e75463e3179f
parent 30759 3f5f0c98cd18
child 33228 35c233975b78
equal deleted inserted replaced
30763:a520aefb96f1 30764:e75463e3179f
     6 # GNU General Public License version 2 or any later version.
     6 # GNU General Public License version 2 or any later version.
     7 
     7 
     8 from __future__ import absolute_import
     8 from __future__ import absolute_import
     9 
     9 
    10 import cgi
    10 import cgi
       
    11 import struct
    11 
    12 
    12 from .common import (
    13 from .common import (
    13     HTTP_OK,
    14     HTTP_OK,
    14 )
    15 )
    15 
    16 
    21 
    22 
    22 urlerr = util.urlerr
    23 urlerr = util.urlerr
    23 urlreq = util.urlreq
    24 urlreq = util.urlreq
    24 
    25 
    25 HGTYPE = 'application/mercurial-0.1'
    26 HGTYPE = 'application/mercurial-0.1'
       
    27 HGTYPE2 = 'application/mercurial-0.2'
    26 HGERRTYPE = 'application/hg-error'
    28 HGERRTYPE = 'application/hg-error'
    27 
    29 
    28 def decodevaluefromheaders(req, headerprefix):
    30 def decodevaluefromheaders(req, headerprefix):
    29     """Decode a long value from multiple HTTP request headers."""
    31     """Decode a long value from multiple HTTP request headers."""
    30     chunks = []
    32     chunks = []
    81     def restore(self):
    83     def restore(self):
    82         val = self.ui.fout.getvalue()
    84         val = self.ui.fout.getvalue()
    83         self.ui.ferr, self.ui.fout = self.oldio
    85         self.ui.ferr, self.ui.fout = self.oldio
    84         return val
    86         return val
    85 
    87 
    86     def compresschunks(self, chunks):
       
    87         # Don't allow untrusted settings because disabling compression or
       
    88         # setting a very high compression level could lead to flooding
       
    89         # the server's network or CPU.
       
    90         opts = {'level': self.ui.configint('server', 'zliblevel', -1)}
       
    91         return util.compengines['zlib'].compressstream(chunks, opts)
       
    92 
       
    93     def _client(self):
    88     def _client(self):
    94         return 'remote:%s:%s:%s' % (
    89         return 'remote:%s:%s:%s' % (
    95             self.req.env.get('wsgi.url_scheme') or 'http',
    90             self.req.env.get('wsgi.url_scheme') or 'http',
    96             urlreq.quote(self.req.env.get('REMOTE_HOST', '')),
    91             urlreq.quote(self.req.env.get('REMOTE_HOST', '')),
    97             urlreq.quote(self.req.env.get('REMOTE_USER', '')))
    92             urlreq.quote(self.req.env.get('REMOTE_USER', '')))
    98 
    93 
       
    94     def responsetype(self, v1compressible=False):
       
    95         """Determine the appropriate response type and compression settings.
       
    96 
       
    97         The ``v1compressible`` argument states whether the response with
       
    98         application/mercurial-0.1 media types should be zlib compressed.
       
    99 
       
   100         Returns a tuple of (mediatype, compengine, engineopts).
       
   101         """
       
   102         # For now, if it isn't compressible in the old world, it's never
       
   103         # compressible. We can change this to send uncompressed 0.2 payloads
       
   104         # later.
       
   105         if not v1compressible:
       
   106             return HGTYPE, None, None
       
   107 
       
   108         # Determine the response media type and compression engine based
       
   109         # on the request parameters.
       
   110         protocaps = decodevaluefromheaders(self.req, 'X-HgProto').split(' ')
       
   111 
       
   112         if '0.2' in protocaps:
       
   113             # Default as defined by wire protocol spec.
       
   114             compformats = ['zlib', 'none']
       
   115             for cap in protocaps:
       
   116                 if cap.startswith('comp='):
       
   117                     compformats = cap[5:].split(',')
       
   118                     break
       
   119 
       
   120             # Now find an agreed upon compression format.
       
   121             for engine in wireproto.supportedcompengines(self.ui, self,
       
   122                                                          util.SERVERROLE):
       
   123                 if engine.wireprotosupport().name in compformats:
       
   124                     opts = {}
       
   125                     level = self.ui.configint('server',
       
   126                                               '%slevel' % engine.name())
       
   127                     if level is not None:
       
   128                         opts['level'] = level
       
   129 
       
   130                     return HGTYPE2, engine, opts
       
   131 
       
   132             # No mutually supported compression format. Fall back to the
       
   133             # legacy protocol.
       
   134 
       
   135         # Don't allow untrusted settings because disabling compression or
       
   136         # setting a very high compression level could lead to flooding
       
   137         # the server's network or CPU.
       
   138         opts = {'level': self.ui.configint('server', 'zliblevel', -1)}
       
   139         return HGTYPE, util.compengines['zlib'], opts
       
   140 
    99 def iscmd(cmd):
   141 def iscmd(cmd):
   100     return cmd in wireproto.commands
   142     return cmd in wireproto.commands
   101 
   143 
   102 def call(repo, req, cmd):
   144 def call(repo, req, cmd):
   103     p = webproto(req, repo.ui)
   145     p = webproto(req, repo.ui)
       
   146 
       
   147     def genversion2(gen, compress, engine, engineopts):
       
   148         # application/mercurial-0.2 always sends a payload header
       
   149         # identifying the compression engine.
       
   150         name = engine.wireprotosupport().name
       
   151         assert 0 < len(name) < 256
       
   152         yield struct.pack('B', len(name))
       
   153         yield name
       
   154 
       
   155         if compress:
       
   156             for chunk in engine.compressstream(gen, opts=engineopts):
       
   157                 yield chunk
       
   158         else:
       
   159             for chunk in gen:
       
   160                 yield chunk
       
   161 
   104     rsp = wireproto.dispatch(repo, p, cmd)
   162     rsp = wireproto.dispatch(repo, p, cmd)
   105     if isinstance(rsp, str):
   163     if isinstance(rsp, str):
   106         req.respond(HTTP_OK, HGTYPE, body=rsp)
   164         req.respond(HTTP_OK, HGTYPE, body=rsp)
   107         return []
   165         return []
   108     elif isinstance(rsp, wireproto.streamres):
   166     elif isinstance(rsp, wireproto.streamres):
   109         if rsp.reader:
   167         if rsp.reader:
   110             gen = iter(lambda: rsp.reader.read(32768), '')
   168             gen = iter(lambda: rsp.reader.read(32768), '')
   111         else:
   169         else:
   112             gen = rsp.gen
   170             gen = rsp.gen
   113 
   171 
   114         if rsp.v1compressible:
   172         # This code for compression should not be streamres specific. It
   115             gen = p.compresschunks(gen)
   173         # is here because we only compress streamres at the moment.
       
   174         mediatype, engine, engineopts = p.responsetype(rsp.v1compressible)
   116 
   175 
   117         req.respond(HTTP_OK, HGTYPE)
   176         if mediatype == HGTYPE and rsp.v1compressible:
       
   177             gen = engine.compressstream(gen, engineopts)
       
   178         elif mediatype == HGTYPE2:
       
   179             gen = genversion2(gen, rsp.v1compressible, engine, engineopts)
       
   180 
       
   181         req.respond(HTTP_OK, mediatype)
   118         return gen
   182         return gen
   119     elif isinstance(rsp, wireproto.pushres):
   183     elif isinstance(rsp, wireproto.pushres):
   120         val = p.restore()
   184         val = p.restore()
   121         rsp = '%d\n%s' % (rsp.res, val)
   185         rsp = '%d\n%s' % (rsp.res, val)
   122         req.respond(HTTP_OK, HGTYPE, body=rsp)
   186         req.respond(HTTP_OK, HGTYPE, body=rsp)