[Pkg-privacy-commits] [pyptlib] 80/136: refactor Config API slightly, tests passed - add a unified Config.declareSupports() method for use in initialisation - remove most of the check* methods, instead do this directly in declareSupports() - move common logic from {client, server}.init() into declareSupports() - add a unified envvar validation system using composable first-class functions - exceptions thrown during validation automatic trigger config.writeEnvError()
Ximin Luo
infinity0 at moszumanska.debian.org
Sat Aug 22 13:25:12 UTC 2015
This is an automated email from the git hooks/post-receive script.
infinity0 pushed a commit to branch master
in repository pyptlib.
commit 6b6b3579a0620437582eafea197924ce8e2de50e
Author: Ximin Luo <infinity0 at gmx.com>
Date: Wed Aug 14 14:58:47 2013 +0100
refactor Config API slightly, tests passed
- add a unified Config.declareSupports() method for use in initialisation
- remove most of the check* methods, instead do this directly in declareSupports()
- move common logic from {client,server}.init() into declareSupports()
- add a unified envvar validation system using composable first-class functions
- exceptions thrown during validation automatic trigger config.writeEnvError()
---
pyptlib/client.py | 37 ++-----------
pyptlib/client_config.py | 16 ++----
pyptlib/config.py | 132 +++++++++++++++++++++--------------------------
pyptlib/server.py | 39 ++------------
pyptlib/server_config.py | 73 +++++++++++++-------------
5 files changed, 106 insertions(+), 191 deletions(-)
diff --git a/pyptlib/client.py b/pyptlib/client.py
index 59e4944..f8dc6d5 100644
--- a/pyptlib/client.py
+++ b/pyptlib/client.py
@@ -8,6 +8,7 @@ Public client-side pyptlib API.
from pyptlib.config import EnvError
from pyptlib.client_config import ClientConfig
+
def init(supported_transports):
"""
Bootstrap client-side managed-proxy mode.
@@ -27,21 +28,12 @@ def init(supported_transports):
:raises: :class:`pyptlib.config.EnvError` if environment was incomplete or corrupted.
"""
-
- supportedTransportVersion = '1'
-
config = ClientConfig()
- if config.checkManagedTransportVersion(supportedTransportVersion):
- config.writeVersion(supportedTransportVersion)
- else:
- config.writeVersionError()
- raise EnvError("Unsupported managed proxy protocol version (%s)" %
- str(config.getManagedTransportVersions()))
-
+ wanted = config.declareSupports(supported_transports)
retval = {}
retval['state_loc'] = config.getStateLocation()
- retval['transports'] = _getTransportsList(supported_transports, config)
+ retval['transports'] = wanted['transports']
return retval
@@ -85,26 +77,3 @@ def reportEnd():
config = ClientConfig()
config.writeMethodEnd()
-
-def _getTransportsList(supported_transports, config):
- """
- Figure out which transports the application should launch, based on
- the transports it supports and on the transports that Tor wants it
- to spawn.
-
- :param list supported_transports: Transports that the application supports.
- :param :class:`pyptlib.client_config.ClientConfig` config: Configuration of Tor.
-
- :returns: A list of transports that the application should launch.
- """
- transports = []
-
- if config.getAllTransportsEnabled():
- return supported_transports
-
- for transport in config.getClientTransports():
- if transport in supported_transports:
- transports.append(transport)
-
- return transports
-
diff --git a/pyptlib/client_config.py b/pyptlib/client_config.py
index 33e34a6..3099600 100644
--- a/pyptlib/client_config.py
+++ b/pyptlib/client_config.py
@@ -5,6 +5,8 @@
Low-level parts of pyptlib that are only useful to clients.
"""
+import sys
+
from pyptlib.config import Config
class ClientConfig(Config):
@@ -16,22 +18,11 @@ class ClientConfig(Config):
def __init__(self):
Config.__init__(self)
- self.transports = self.get('TOR_PT_CLIENT_TRANSPORTS').split(',')
+ self.transports = self.getEnv('TOR_PT_CLIENT_TRANSPORTS').split(',')
if '*' in self.transports:
self.allTransportsEnabled = True
self.transports.remove('*')
- def getClientTransports(self): # XXX why is this client-specific ???
- """
- Return a list of strings representing the client transports reported by Tor.
-
- If present, the wildcard transport, '*', is stripped from this list and used to set allTransportsEnabled to True.
-
- :returns: list of transports
- """
-
- return self.transports
-
def writeMethod(self, name, socksVersion, addrport, args=None, optArgs=None):
"""
Write a message to stdout announcing that a transport was
@@ -69,4 +60,3 @@ class ClientConfig(Config):
self.emit('CMETHODS DONE')
-
diff --git a/pyptlib/config.py b/pyptlib/config.py
index 0490d4d..af15404 100644
--- a/pyptlib/config.py
+++ b/pyptlib/config.py
@@ -7,6 +7,23 @@ Parts of pyptlib that are useful both to clients and servers.
import os, sys
+SUPPORTED_TRANSPORT_VERSIONS = ['1']
+
+def env_has_k(k, v):
+ """
+ A validator for Config.getEnv that returns the value of the envvar if it
+ was found, or throws ValueError if it was not.
+ """
+ if v is None: raise ValueError('Missing environment variable %s' % k)
+ return v
+
+def env_id(k, v):
+ """
+ A validator for Config.getEnv that returns the value of the envvar if it
+ was found, or None if it was not.
+ """
+ return v
+
class Config(object):
"""
pyptlib's configuration.
@@ -19,23 +36,10 @@ class Config(object):
:raises: :class:`pyptlib.config.EnvError` if environment was incomplete or corrupted.
"""
- stateLocation = None # TOR_PT_STATE_LOCATION
- managedTransportVer = [] # TOR_PT_MANAGED_TRANSPORT_VER
- transports = [] # TOR_PT_SERVER_TRANSPORTS or TOR_PT_CLIENT_TRANSPORTS
- allTransportsEnabled = False
-
def __init__(self):
- self.stateLocation = self.get('TOR_PT_STATE_LOCATION')
- self.managedTransportVer = self.get('TOR_PT_MANAGED_TRANSPORT_VER').split(',')
-
- def checkClientMode(self):
- """
- Check whether Tor wants us to run as a client or as a server.
-
- :returns: bool -- True if Tor wants us to run as a client.
- """
-
- return self.check('TOR_PT_CLIENT_TRANSPORTS')
+ self.stateLocation = self.getEnv('TOR_PT_STATE_LOCATION')
+ self.managedTransportVer = self.getEnv('TOR_PT_MANAGED_TRANSPORT_VER').split(',')
+ self.allTransportsEnabled = False
def getStateLocation(self):
"""
@@ -51,17 +55,6 @@ class Config(object):
return self.managedTransportVer
- def checkManagedTransportVersion(self, version):
- """
- Check if Tor supports a specific managed-proxy protocol version.
-
- :param string version: A managed-proxy protocol version.
-
- :returns: bool -- True if version is supported.
- """
-
- return version in self.managedTransportVer
-
def getAllTransportsEnabled(self):
"""
Check if Tor wants the application to spawn all its transpotrs.
@@ -71,71 +64,66 @@ class Config(object):
return self.allTransportsEnabled
- def checkTransportEnabled(self, transport):
- """
- Check if Tor wants the application to spawn a specific transport.
-
- :param string transport: The name of a pluggable transport.
-
- :returns: bool -- True if Tor wants the application to spawn that transport.
- """
-
- return self.allTransportsEnabled or transport in self.transports
-
- def writeEnvError(self, message): # ENV-ERROR
- """
- Announce that an error occured while parsing the environment.
-
- :param str message: Error message.
- """
-
- self.emit('ENV-ERROR %s' % message)
-
- def writeVersion(self, version): # VERSION
+ def declareSupports(self, transports):
"""
- Announce that a specific managed-proxy protocol version is supported.
+ Declare to Tor the versions and transports that this PT supports.
- :param str version: A managed-proxy protocol version.
- """
-
- self.emit('VERSION %s' % version)
+ :param list transports: List of transport methods this PT supports.
- def writeVersionError(self): # VERSION-ERROR
- """
- Announce that we could not find a supported managed-proxy
- protocol version.
+ :returns: {"transports": wanted_transports} -- The subset of the
+ declared inputs that were actually wanted by Tor.
"""
+ versions = SUPPORTED_TRANSPORT_VERSIONS
+ if type(transports) == str:
+ transports = [transports]
- self.emit('VERSION-ERROR no-version')
+ wanted_versions = [v for v in versions if v in self.managedTransportVer]
+ if not wanted_versions:
+ self.emit('VERSION-ERROR no-version')
+ raise EnvError("Unsupported managed proxy protocol version (%s)" %
+ self.managedTransportVer)
+ else:
+ self.emit('VERSION %s' % wanted_versions[0])
- def check(self, key):
- """
- Check the environment for a specific environment variable.
+ if self.allTransportsEnabled:
+ wanted_transports = transports.keys()
+ unwanted_transports = []
+ else:
+ # return able in priority-order determined by plugin
+ wanted_transports = [t for t in transports if t in self.transports]
+ # return unable in priority-order as requested by Tor
+ unwanted_transports = [t for t in self.transports if t not in transports]
- :param str key: Environment variable key.
+ for t in unwanted_transports:
+ self.writeMethodError(t, 'unsupported transport')
- :returns: bool -- True if the environment variable is set.
- """
+ return { 'transports': wanted_transports }
- return key in os.environ
+ def writeMethodError(self, transportName, message):
+ raise NotImplementedError
- def get(self, key):
+ def getEnv(self, key, validate=env_has_k):
"""
Get the value of an environment variable.
:param str key: Environment variable key.
+ :param f validate: Function that takes a var and a value and returns
+ a transformed value if it is valid, or throws an exception.
+ If the environment does not define var, value is None. By default,
+ we return the value if the environment has the variable, otherwise
+ we raise a ValueError.
:returns: str -- The value of the envrionment variable.
:raises: :class:`pyptlib.config.EnvError` if environment
variable could not be found.
"""
-
- if key in os.environ:
- return os.environ[key]
- else:
- message = 'Missing environment variable %s' % key
- self.writeEnvError(message)
+ try:
+ return validate(key, os.getenv(key))
+ except Exception, e:
+ message = 'ENV-ERROR %s' % e.message
+ print message
+ sys.stdout.flush()
raise EnvError(message)
def emit(self, msg):
diff --git a/pyptlib/server.py b/pyptlib/server.py
index ee075ec..15e5df8 100644
--- a/pyptlib/server.py
+++ b/pyptlib/server.py
@@ -31,23 +31,15 @@ def init(supported_transports):
:raises: :class:`pyptlib.config.EnvError` if environment was incomplete or corrupted.
"""
-
- supportedTransportVersion = '1'
-
config = ServerConfig()
-
- if config.checkManagedTransportVersion(supportedTransportVersion):
- config.writeVersion(supportedTransportVersion)
- else:
- config.writeVersionError()
- raise EnvError("Unsupported managed proxy protocol version (%s)" %
- str(config.getManagedTransportVersions()))
-
+ wanted = config.declareSupports(supported_transports)
+ transports = dict(((k, v) for k, v in config.getServerBindAddresses().items()
+ if k in wanted['transports']))
retval = {}
retval['state_loc'] = config.getStateLocation()
retval['orport'] = config.getORPort()
retval['ext_orport'] = config.getExtendedORPort()
- retval['transports'] = _getTransportsDict(supported_transports, config)
+ retval['transports'] = transports
retval['auth_cookie_file'] = config.getAuthCookieFile()
return retval
@@ -90,26 +82,3 @@ def reportEnd():
config = ServerConfig()
config.writeMethodEnd()
-
-def _getTransportsDict(supported_transports, config):
- """
- Figure out which transports the application should launch, based on
- the transports it supports and on the transports that Tor wants it
- to spawn.
-
- :param list supported_transports: Transports that the application supports.
- :param :class:`pyptlib.client_config.ClientConfig` config: Configuration of Tor.
-
- :returns: A dictionary of 'transport => bind address' of transports that the application should launch.
- """
- transports = {}
-
- if config.getAllTransportsEnabled():
- return config.getServerBindAddresses()
-
- for transport in config.getServerTransports():
- if transport in supported_transports:
- assert(transport in config.getServerBindAddresses())
- transports[transport] = config.getServerBindAddresses()[transport]
-
- return transports
diff --git a/pyptlib/server_config.py b/pyptlib/server_config.py
index ba0fa63..933f856 100644
--- a/pyptlib/server_config.py
+++ b/pyptlib/server_config.py
@@ -7,6 +7,9 @@ Low-level parts of pyptlib that are only useful to servers.
import pyptlib.config as config
import pyptlib.util as util
+import sys
+
+from pyptlib.config import env_has_k, env_id
class ServerConfig(config.Config):
"""
@@ -20,63 +23,59 @@ class ServerConfig(config.Config):
:raises: :class:`pyptlib.config.EnvError` if environment was incomplete or corrupted.
"""
def __init__(self):
- config.Config.__init__(self)
-
"""
TOR_PT_EXTENDED_SERVER_PORT is optional; tor uses the empty
string as its value if it does not support the Extended
ORPort.
"""
- ext_orport_tmp = self.get('TOR_PT_EXTENDED_SERVER_PORT')
- if ext_orport_tmp == '':
- self.extendedORPort = None
- else:
- self.extendedORPort = self.get_addrport('TOR_PT_EXTENDED_SERVER_PORT')
+ config.Config.__init__(self)
- if self.check('TOR_PT_AUTH_COOKIE_FILE'):
- self.authCookieFile = self.get('TOR_PT_AUTH_COOKIE_FILE')
- else:
- self.authCookieFile = None
+ def empty_or_valid_addr(k, v):
+ v = env_has_k(k, v)
+ if v == '': return None
+ return util.parse_addr_spec(v)
+
+ self.extendedORPort = self.getEnv('TOR_PT_EXTENDED_SERVER_PORT', empty_or_valid_addr)
# Check that either both Extended ORPort and the Extended
# ORPort Authentication Cookie are present, or neither.
- if self.extendedORPort and not self.authCookieFile:
- err = "Extended ORPort address provided, but no cookie file."
- self.writeEnvError(err)
- raise config.EnvError(err)
- elif self.authCookieFile and not self.extendedORPort:
- err = "Extended ORPort Authentication cookie file provided, but no Extended ORPort address."
- self.writeEnvError(err)
- raise config.EnvError(err)
+ if self.extendedORPort:
+ def validate_authcookie(k, v):
+ if v is None: raise ValueError("Extended ORPort address provided, but no cookie file.")
+ return v
+ else:
+ def validate_authcookie(k, v):
+ if v is not None: raise ValueError("Extended ORPort Authentication cookie file provided, but no Extended ORPort address.")
+ return v
+ self.authCookieFile = self.getEnv('TOR_PT_AUTH_COOKIE_FILE', validate_authcookie)
# Get ORPort.
- self.ORPort = self.get_addrport('TOR_PT_ORPORT')
+ self.ORPort = self.getEnv('TOR_PT_ORPORT', empty_or_valid_addr)
# Get bind addresses.
- self.serverBindAddr = {}
- bindaddrs = self.get('TOR_PT_SERVER_BINDADDR').split(',')
- for bindaddr in bindaddrs:
- (transport_name, addrport) = bindaddr.split('-')
-
- try:
+ def validate_sever_bindaddr(k, bindaddrs):
+ serverBindAddr = {}
+ bindaddrs = env_has_k(k, bindaddrs).split(',')
+ for bindaddr in bindaddrs:
+ (transport_name, addrport) = bindaddr.split('-')
(addr, port) = util.parse_addr_spec(addrport)
- except ValueError, err:
- self.writeEnvError(err)
- raise config.EnvError(err)
-
- self.serverBindAddr[transport_name] = (addr, port)
+ serverBindAddr[transport_name] = (addr, port)
+ return serverBindAddr
+ self.serverBindAddr = self.getEnv('TOR_PT_SERVER_BINDADDR', validate_sever_bindaddr)
# Get transports.
- self.transports = self.get('TOR_PT_SERVER_TRANSPORTS').split(',')
+ def validate_transports(k, transports):
+ transports = env_has_k(k, transports).split(',')
+ t = sorted(transports)
+ b = sorted(self.serverBindAddr.keys())
+ if t != b:
+ raise ValueError("Can't match transports with bind addresses (%s, %s)" % (t, b))
+ return transports
+ self.transports = self.getEnv('TOR_PT_SERVER_TRANSPORTS', validate_transports)
if '*' in self.transports:
self.allTransportsEnabled = True
self.transports.remove('*')
- if sorted(self.transports) != sorted(self.serverBindAddr.keys()):
- err = "Can't match transports with bind addresses (%s, %s)" % (self.transports, self.serverBindAddr.keys())
- self.writeEnvError(err)
- raise config.EnvError(err)
-
def getExtendedORPort(self):
"""
:returns: :attr:`pyptlib.server_config.ServerConfig.extendedORPort`
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-privacy/packages/pyptlib.git
More information about the Pkg-privacy-commits
mailing list