179 lines
6.4 KiB
Python
179 lines
6.4 KiB
Python
import re
|
|
|
|
from urlparse import urlparse
|
|
from importlib import import_module
|
|
|
|
from mandaye.log import logger
|
|
from mandaye.filters.default import MandayeFilter
|
|
from mandaye.response import _500, _302
|
|
from mandaye.exceptions import ImproperlyConfigured
|
|
|
|
# TODO: add an external url mapping
|
|
|
|
def import_mapping(path):
|
|
if not path or not '.' in path:
|
|
return path
|
|
i = path.rfind('.')
|
|
module, attr = path[:i], path[i+1:]
|
|
|
|
try:
|
|
mod = import_module(module)
|
|
except ImportError, e:
|
|
raise ImproperlyConfigured('Error importing mapping %s: "%s"' % (path, e))
|
|
try:
|
|
mapping = getattr(mod, attr)
|
|
except AttributeError:
|
|
raise ImproperlyConfigured('Module "%s" does not define a "%s" mapping' % (module, attr))
|
|
return mapping
|
|
|
|
|
|
class Dispatcher(object):
|
|
""" The dispatcher is the main class of Mandaye
|
|
It allows you to launch the right filter on the reqest and the response
|
|
"""
|
|
|
|
def __init__(self, env, target_url, mapping=dict()):
|
|
""" env: wsgi environ
|
|
target_url: the full url of your destination
|
|
mapping: dict with the site mapping
|
|
"""
|
|
self.target = urlparse(target_url)
|
|
self.env = env
|
|
self.env['target'] = self.target
|
|
self.mapping = import_mapping(mapping)
|
|
self.filter = MandayeFilter(self.env, self.target)
|
|
self.req_mapping = self._parse_mapping(self.mapping)
|
|
|
|
def __get_mappings_hooks(self, mapper, req_mapping):
|
|
""" fill the request mapping with the right hooks
|
|
return req_mapping
|
|
"""
|
|
if not mapper.has_key('method') or \
|
|
mapper['method'] == self.env['REQUEST_METHOD']:
|
|
for hookname in req_mapping:
|
|
if mapper.has_key(hookname):
|
|
if isinstance(req_mapping[hookname], list):
|
|
req_mapping[hookname].extend(mapper[hookname])
|
|
else:
|
|
req_mapping[hookname] = mapper[hookname]
|
|
return req_mapping
|
|
|
|
|
|
def _parse_mapping(self, mapping):
|
|
""" parse the mapping on every request
|
|
"""
|
|
if not mapping:
|
|
return req_mapping
|
|
|
|
req_mapping = {
|
|
'on_request': [],
|
|
'on_response': [],
|
|
'response': None,
|
|
'target': None,
|
|
'redirect': None,
|
|
}
|
|
|
|
for mapper in mapping:
|
|
if mapper.has_key('path'):
|
|
if isinstance(mapper['path'], str):
|
|
if re.match(mapper['path'], self.env['PATH_INFO']):
|
|
req_mapping = self.__get_mappings_hooks(mapper, req_mapping)
|
|
else:
|
|
for path in mapper['path']:
|
|
if re.match(path, self.env['PATH_INFO']):
|
|
req_mapping = self.__get_mappings_hooks(mapper, req_mapping)
|
|
else:
|
|
logger.warning('Config error: you need to specify paths in your mapping')
|
|
return req_mapping
|
|
|
|
def _call_hook(self, hook, *args):
|
|
if hook and hook.has_key('filter'):
|
|
values = hook.get('values')
|
|
if not values:
|
|
values = dict()
|
|
if hook.has_key('condition'):
|
|
return hook['filter'](self.env, values, hook['condition'], *args)
|
|
else:
|
|
return hook['filter'](self.env, values, *args)
|
|
else:
|
|
logger.warning("%s hook failed (no filter option)" % self.env['PATH_INFO'])
|
|
return None
|
|
|
|
def get_target_url(self):
|
|
""" Return the destination url
|
|
"""
|
|
# Disable reverse proxy if we have a static response
|
|
if self.req_mapping['response'] or self.req_mapping['redirect']:
|
|
return None
|
|
if self.req_mapping['target']:
|
|
if re.match("http://|https://", self.req_mapping['target']):
|
|
url = self.req_mapping['target']
|
|
else:
|
|
url = self.target.geturl() + self.req_mapping['target']
|
|
else:
|
|
url = self.target.geturl() + self.env['PATH_INFO']
|
|
|
|
if self.env['QUERY_STRING']:
|
|
return url + "?" + self.env['QUERY_STRING']
|
|
else:
|
|
return url
|
|
return None
|
|
|
|
|
|
def get_response(self, request):
|
|
""" Called if you have a response hook for this request
|
|
"""
|
|
if self.req_mapping['redirect']:
|
|
return _302(self.req_mapping['redirect'])
|
|
response = None
|
|
logger.debug("Loading response hook(s)")
|
|
for hook in self.req_mapping['response']:
|
|
new_response = self._call_hook(hook, request, response)
|
|
if new_response:
|
|
response = new_response
|
|
else:
|
|
logger.warning("%s Response hook %s failed" % (self.env['PATH_INFO'],
|
|
self.req_mapping['response']['filter']))
|
|
if not response:
|
|
return _500(self.env["PATH_INFO"], "The response hook failed")
|
|
# loading defaults filters
|
|
response = self.filter.on_response(response)
|
|
return response
|
|
|
|
def mod_request(self, request):
|
|
""" Modify the request
|
|
request: MandayeRequest object with cookies and headers
|
|
Return the request object """
|
|
# Loading defaults filters
|
|
request = self.filter.on_request(request)
|
|
|
|
# Calling hook function
|
|
for hook in self.req_mapping['on_request']:
|
|
new_request = self._call_hook(hook, request)
|
|
if new_request:
|
|
request = new_request
|
|
else:
|
|
logger.warning("%s On_request hook %s failed (empty request)" % \
|
|
(self.env['PATH_INFO'], hook['filter']))
|
|
return request
|
|
|
|
def mod_response(self, request, response):
|
|
""" Modify the response
|
|
request: the Mandaye request
|
|
response: MandayeResponse object with cookies, headers and HTML
|
|
you can modify the cookies and the HTTP headers """
|
|
|
|
# loading defaults filters
|
|
response = self.filter.on_response(response)
|
|
|
|
# Calling hook function
|
|
for hook in self.req_mapping['on_response']:
|
|
new_response = self._call_hook(hook, request, response)
|
|
if new_response:
|
|
response = new_response
|
|
else:
|
|
logger.warning("%s On_response hook %s failed (empty answer)" % \
|
|
(self.env['PATH_INFO'], hook['filter']))
|
|
return response
|
|
|