This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
mandaye/mandaye/dispatcher.py

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