226 lines
8.9 KiB
Python
226 lines
8.9 KiB
Python
|
|
import copy
|
|
import re
|
|
|
|
from urlparse import urlparse
|
|
from importlib import import_module
|
|
|
|
from mandaye import config
|
|
from mandaye.filters.default import MandayeFilter
|
|
from mandaye.http import HTTPRequest, HTTPResponse
|
|
from mandaye.log import logger
|
|
from mandaye.mappers import default
|
|
from mandaye.response import _500, _302
|
|
from mandaye.exceptions import ImproperlyConfigured
|
|
|
|
# TODO: add an external url mapping
|
|
|
|
def import_mapping(name):
|
|
if not name:
|
|
return dict()
|
|
if not config.mappers.has_key(name):
|
|
logger.error("mapper %s not found" % name)
|
|
return dict()
|
|
module = config.mappers[name]
|
|
try:
|
|
mapper = import_module(module)
|
|
except ImportError, e:
|
|
raise ImproperlyConfigured('Error importing mapping %s: "%s"' % (module, e))
|
|
return mapper
|
|
|
|
|
|
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, mapper_name=None):
|
|
""" env: wsgi environ
|
|
target_url: the full url of your destination
|
|
mepper: python module with the mapper
|
|
"""
|
|
self.env = env
|
|
self.target = urlparse(target_url)
|
|
self.env['target'] = self.target
|
|
self.mapper_name = mapper_name
|
|
self.mapper = import_mapping(mapper_name)
|
|
self.env['urls'] = self.mapper.urls
|
|
auth_type = self.env['mandaye.config']['auth_type']
|
|
path = config.authentifications[auth_type]
|
|
i = path.rfind('.')
|
|
module, attr = path[:i], path[i+1:]
|
|
module = import_module(module)
|
|
Auth = getattr(module, attr)
|
|
self.auth = Auth(env, self.mapper)
|
|
self.env['mandaye.auth'] = self.auth
|
|
self.env['mandaye.mapper'] = self.mapper
|
|
mapping = []
|
|
mapping.extend(self.auth.get_default_mapping())
|
|
mapping.extend(default.mapping)
|
|
mapping.extend(self.mapper.mapping)
|
|
logger.debug('Dispatcher mapping : %r', mapping)
|
|
self.req_mapping = self._parse_mapping(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']:
|
|
if mapper.has_key('scripts'):
|
|
req_mapping["on_response"].append(
|
|
{
|
|
'filter': MandayeFilter.add_js_header,
|
|
'values': {'scripts': mapper['scripts']},
|
|
'content-types': ['text/html']
|
|
}
|
|
)
|
|
for hookname in req_mapping:
|
|
if mapper.has_key(hookname):
|
|
if isinstance(req_mapping[hookname], list):
|
|
for entry in mapper[hookname]:
|
|
if entry.has_key('auth'):
|
|
entry['filter'] = getattr(self.auth, entry['auth'])
|
|
req_mapping[hookname].append(entry)
|
|
else:
|
|
if isinstance(mapper[hookname], dict) and \
|
|
mapper[hookname].has_key('auth'):
|
|
mapper[hookname]['filter'] = getattr(self.auth, mapper[hookname]['auth'])
|
|
req_mapping[hookname] = mapper[hookname]
|
|
return req_mapping
|
|
|
|
|
|
def _parse_mapping(self, mapping):
|
|
""" parse the mapping on every request
|
|
"""
|
|
req_mapping = {
|
|
'on_request': [],
|
|
'on_response': [],
|
|
'response': None,
|
|
'target': None,
|
|
'redirect': None,
|
|
'decompress': config.auto_decompress
|
|
}
|
|
|
|
if not mapping:
|
|
return req_mapping
|
|
for entry in mapping:
|
|
if entry.has_key('path'):
|
|
if isinstance(entry['path'], str):
|
|
if re.match(entry['path'], self.env['PATH_INFO'],
|
|
re.IGNORECASE):
|
|
req_mapping = self.__get_mappings_hooks(entry, req_mapping)
|
|
else:
|
|
for path in entry['path']:
|
|
if re.match(path, self.env['PATH_INFO'],
|
|
re.IGNORECASE):
|
|
req_mapping = self.__get_mappings_hooks(entry, 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()
|
|
return hook['filter'](self.env, values, *args)
|
|
else:
|
|
logger.warning("%s hook failed (no filter option)" % self.env['PATH_INFO'])
|
|
return None
|
|
|
|
def _is_cond_respected(self, hook, request, response):
|
|
if hook.has_key('condition') and \
|
|
not hook['condition'](self.env, request, response):
|
|
return False
|
|
return True
|
|
|
|
def set_request_target(self, request):
|
|
""" Add target url on not into the request
|
|
"""
|
|
request.target = self.target.geturl() + self.env['RAW_URI']
|
|
if self.req_mapping['target']:
|
|
if "//" in self.req_mapping['target']:
|
|
request.target = self.req_mapping['target']
|
|
else:
|
|
request.target = self.target.geturl() + self.req_mapping['target']
|
|
elif self.req_mapping['response'] and \
|
|
self._is_cond_respected(self.req_mapping['response'],
|
|
request, None):
|
|
request.target = None
|
|
elif self.req_mapping['redirect']:
|
|
request.target = None
|
|
return request
|
|
|
|
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'])
|
|
elif self.req_mapping['response']:
|
|
logger.debug("Loading response hook(s)")
|
|
hook = self.req_mapping['response']
|
|
logger.debug('Using hook: %r', hook)
|
|
response = self._call_hook(hook, request, None)
|
|
if not response:
|
|
return _500(self.env["PATH_INFO"], "The response hook failed")
|
|
else:
|
|
return _500(self.env["PATH_INFO"], "no response and no target")
|
|
return response
|
|
|
|
def mod_request(self, request):
|
|
""" Modify the request
|
|
request: MandayeRequest object with cookies and headers
|
|
Return the request object """
|
|
# Calling hook function
|
|
for hook in self.req_mapping['on_request']:
|
|
if not self._is_cond_respected(hook, request, None):
|
|
continue
|
|
response = self._call_hook(hook, request)
|
|
if response and isinstance(response, HTTPResponse):
|
|
return response
|
|
elif response and isinstance(response, HTTPRequest):
|
|
logger.warning('DEPRECATED hook %s: a request hook must return None or an HTTPResponse nothing else',
|
|
hook['filter'])
|
|
elif response:
|
|
logger.warning("%s on_request hook (%s) must return None or an HTTPResponse nothing else",
|
|
self.env['PATH_INFO'], hook['filter'])
|
|
return
|
|
|
|
def mod_response(self, request, response):
|
|
""" Modify the response. This will load on_response filters.
|
|
request: the Mandaye request
|
|
response: MandayeResponse object with cookies, headers and HTML
|
|
you can modify the cookies and the HTTP headers """
|
|
|
|
content_type = response.headers.getheader('content-type')
|
|
if content_type:
|
|
content_type = content_type.split(';')[0]
|
|
|
|
# Calling hook function
|
|
for hook in self.req_mapping['on_response']:
|
|
if not self._is_cond_respected(hook, request, response):
|
|
continue
|
|
if hook.has_key('content-types') and content_type:
|
|
if content_type not in hook['content-types']:
|
|
logger.debug("Don't load filter %s (content-type %s doesn't match)" % (hook['filter'], content_type))
|
|
continue
|
|
|
|
if self.req_mapping['decompress']:
|
|
response.decompress()
|
|
|
|
try:
|
|
new_response = self._call_hook(hook, request, response)
|
|
except Exception, e:
|
|
new_response = _500(self.env['PATH_INFO'],
|
|
"Hook %s failed with error %s" % (hook, e),
|
|
exception=e,
|
|
env=self.env)
|
|
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
|
|
|