Patchwork D2019: wireprotoserver: move protocol parsing and dispatch out of hgweb

login
register
mail settings
Submitter phabricator
Date Feb. 6, 2018, 9:04 p.m.
Message ID <58a7f5ddebd4b89fb18c4da9f346f867@localhost.localdomain>
Download mbox | patch
Permalink /patch/27386/
State Not Applicable
Headers show

Comments

phabricator - Feb. 6, 2018, 9:04 p.m.
indygreg updated this revision to Diff 5259.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2019?vs=5173&id=5259

REVISION DETAIL
  https://phab.mercurial-scm.org/D2019

AFFECTED FILES
  mercurial/hgweb/hgweb_mod.py
  mercurial/wireprotoserver.py

CHANGE DETAILS




To: indygreg, #hg-reviewers
Cc: mercurial-devel

Patch

diff --git a/mercurial/wireprotoserver.py b/mercurial/wireprotoserver.py
--- a/mercurial/wireprotoserver.py
+++ b/mercurial/wireprotoserver.py
@@ -202,9 +202,43 @@ 
 def iscmd(cmd):
     return cmd in wireproto.commands
 
-def callhttp(repo, req, cmd):
+def parsehttprequest(repo, req, query):
+    """Parse the HTTP request for a wire protocol request.
+
+    If the current request appears to be a wire protocol request, this
+    function returns a dict with details about that request, including
+    an ``abstractprotocolserver`` instance suitable for handling the
+    request. Otherwise, ``None`` is returned.
+
+    ``req`` is a ``wsgirequest`` instance.
+    """
+    # 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.
+    if r'cmd' not in req.form:
+        return None
+
+    cmd = pycompat.sysbytes(req.form[r'cmd'][0])
+
+    # 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.
+    if cmd not in wireproto.commands:
+        return None
+
     proto = webproto(req, repo.ui)
 
+    return {
+        'cmd': cmd,
+        'proto': proto,
+        'dispatch': lambda: _callhttp(repo, req, proto, cmd),
+    }
+
+def _callhttp(repo, req, proto, cmd):
     def genversion2(gen, engine, engineopts):
         # application/mercurial-0.2 always sends a payload header
         # identifying the compression engine.
diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -357,26 +357,18 @@ 
             query = req.env[r'QUERY_STRING'].partition(r'&')[0]
             query = query.partition(r';')[0]
 
-        # The ``cmd`` request parameter is used by both the wire protocol
-        # and hgweb. We route all known wire protocol commands to the
-        # wire protocol handler, even if the command isn't available for
-        # this transport. That's better for machine clients in the case
-        # of an errant request to an unavailable protocol command. And it
-        # prevents hgweb from accidentally using ``cmd`` values used by
-        # the wire protocol.
+        # Route it to a wire protocol handler if it looks like a wire protocol
+        # request.
+        protohandler = wireprotoserver.parsehttprequest(rctx.repo, req, query)
 
-        # process this if it's a protocol request
-        # protocol bits don't need to create any URLs
-        # and the clients always use the old URL structure
-
-        cmd = pycompat.sysbytes(req.form.get(r'cmd', [r''])[0])
-        if wireprotoserver.iscmd(cmd):
+        if protohandler:
+            cmd = protohandler['cmd']
             try:
                 if query:
                     raise ErrorResponse(HTTP_NOT_FOUND)
                 if cmd in perms:
                     self.check_perm(rctx, req, perms[cmd])
-                return wireprotoserver.callhttp(rctx.repo, req, cmd)
+                return protohandler['dispatch']()
             except ErrorResponse as inst:
                 # A client that sends unbundle without 100-continue will
                 # break if we respond early.
@@ -425,6 +417,8 @@ 
                     if fn.endswith(ext):
                         req.form['node'] = [fn[:-len(ext)]]
                         req.form['type'] = [type_]
+        else:
+            cmd = pycompat.sysbytes(req.form.get(r'cmd', [r''])[0])
 
         # process the web interface request