diff mercurial/wireprototypes.py @ 37783:9d818539abfa

wireproto: move supportedcompengines out of wireproto This function is used by both version 1 and version 2. It belongs in a common module. "wireprototypes" may not be the best module name. I may rename it... Differential Revision: https://phab.mercurial-scm.org/D3398
author Gregory Szorc <gregory.szorc@gmail.com>
date Mon, 16 Apr 2018 22:08:13 -0700
parents 352932a11905
children 856f381ad74b
line wrap: on
line diff
--- a/mercurial/wireprototypes.py	Mon Apr 16 22:00:52 2018 -0700
+++ b/mercurial/wireprototypes.py	Mon Apr 16 22:08:13 2018 -0700
@@ -12,6 +12,11 @@
 from .thirdparty.zope import (
     interface as zi,
 )
+from .i18n import _
+from . import (
+    error,
+    util,
+)
 
 # Names of the SSH protocol implementations.
 SSHV1 = 'ssh-v1'
@@ -319,3 +324,52 @@
             return False
 
         return True
+
+def supportedcompengines(ui, role):
+    """Obtain the list of supported compression engines for a request."""
+    assert role in (util.CLIENTROLE, util.SERVERROLE)
+
+    compengines = util.compengines.supportedwireengines(role)
+
+    # Allow config to override default list and ordering.
+    if role == util.SERVERROLE:
+        configengines = ui.configlist('server', 'compressionengines')
+        config = 'server.compressionengines'
+    else:
+        # This is currently implemented mainly to facilitate testing. In most
+        # cases, the server should be in charge of choosing a compression engine
+        # because a server has the most to lose from a sub-optimal choice. (e.g.
+        # CPU DoS due to an expensive engine or a network DoS due to poor
+        # compression ratio).
+        configengines = ui.configlist('experimental',
+                                      'clientcompressionengines')
+        config = 'experimental.clientcompressionengines'
+
+    # No explicit config. Filter out the ones that aren't supposed to be
+    # advertised and return default ordering.
+    if not configengines:
+        attr = 'serverpriority' if role == util.SERVERROLE else 'clientpriority'
+        return [e for e in compengines
+                if getattr(e.wireprotosupport(), attr) > 0]
+
+    # If compression engines are listed in the config, assume there is a good
+    # reason for it (like server operators wanting to achieve specific
+    # performance characteristics). So fail fast if the config references
+    # unusable compression engines.
+    validnames = set(e.name() for e in compengines)
+    invalidnames = set(e for e in configengines if e not in validnames)
+    if invalidnames:
+        raise error.Abort(_('invalid compression engine defined in %s: %s') %
+                          (config, ', '.join(sorted(invalidnames))))
+
+    compengines = [e for e in compengines if e.name() in configengines]
+    compengines = sorted(compengines,
+                         key=lambda e: configengines.index(e.name()))
+
+    if not compengines:
+        raise error.Abort(_('%s config option does not specify any known '
+                            'compression engines') % config,
+                          hint=_('usable compression engines: %s') %
+                          ', '.sorted(validnames))
+
+    return compengines