1
|
|
2
|
import copy
|
3
|
import re
|
4
|
|
5
|
from urlparse import urlparse
|
6
|
from importlib import import_module
|
7
|
|
8
|
from mandaye import config
|
9
|
from mandaye.log import logger
|
10
|
from mandaye.mappers import default
|
11
|
from mandaye.response import _500, _302
|
12
|
from mandaye.exceptions import ImproperlyConfigured
|
13
|
|
14
|
# TODO: add an external url mapping
|
15
|
|
16
|
def import_mapping(name):
|
17
|
if not name:
|
18
|
return dict()
|
19
|
if not config.mappers.has_key(name):
|
20
|
logger.error("mapper %s not found" % name)
|
21
|
return dict()
|
22
|
module = config.mappers[name]
|
23
|
try:
|
24
|
mapper = import_module(module)
|
25
|
except ImportError, e:
|
26
|
raise ImproperlyConfigured('Error importing mapping %s: "%s"' % (module, e))
|
27
|
return mapper
|
28
|
|
29
|
|
30
|
class Dispatcher(object):
|
31
|
""" The dispatcher is the main class of Mandaye
|
32
|
It allows you to launch the right filter on the reqest and the response
|
33
|
"""
|
34
|
|
35
|
def __init__(self, env, target_url, mapper_name=None):
|
36
|
""" env: wsgi environ
|
37
|
target_url: the full url of your destination
|
38
|
mepper: python module with the mapper
|
39
|
"""
|
40
|
self.target = urlparse(target_url)
|
41
|
self.env = env
|
42
|
self.env['target'] = self.target
|
43
|
self.mapper_name = mapper_name
|
44
|
self.mapper = import_mapping(mapper_name)
|
45
|
auth_type = self.env['mandaye.config']['auth_type']
|
46
|
path = config.authentifications[auth_type]
|
47
|
i = path.rfind('.')
|
48
|
module, attr = path[:i], path[i+1:]
|
49
|
module = import_module(module)
|
50
|
Auth = getattr(module, attr)
|
51
|
self.auth = Auth(env, self.mapper)
|
52
|
mapping = []
|
53
|
mapping.extend(self.auth.get_default_mapping())
|
54
|
mapping.extend(default.mapping)
|
55
|
mapping.extend(self.mapper.mapping)
|
56
|
logger.debug('Dispatcher mapping : %r', mapping)
|
57
|
self.req_mapping = self._parse_mapping(mapping)
|
58
|
|
59
|
def __get_mappings_hooks(self, mapper, req_mapping):
|
60
|
""" fill the request mapping with the right hooks
|
61
|
return req_mapping
|
62
|
"""
|
63
|
if not mapper.has_key('method') or \
|
64
|
mapper['method'] == self.env['REQUEST_METHOD']:
|
65
|
for hookname in req_mapping:
|
66
|
if mapper.has_key(hookname):
|
67
|
if isinstance(req_mapping[hookname], list):
|
68
|
for entry in mapper[hookname]:
|
69
|
if entry.has_key('auth'):
|
70
|
entry['filter'] = getattr(self.auth, entry['auth'])
|
71
|
req_mapping[hookname].append(entry)
|
72
|
else:
|
73
|
if isinstance(mapper[hookname], dict) and \
|
74
|
mapper[hookname].has_key('auth'):
|
75
|
mapper[hookname]['filter'] = getattr(self.auth, mapper[hookname]['auth'])
|
76
|
req_mapping[hookname] = mapper[hookname]
|
77
|
return req_mapping
|
78
|
|
79
|
|
80
|
def _parse_mapping(self, mapping):
|
81
|
""" parse the mapping on every request
|
82
|
"""
|
83
|
req_mapping = {
|
84
|
'on_request': [],
|
85
|
'on_response': [],
|
86
|
'response': None,
|
87
|
'target': None,
|
88
|
'redirect': None,
|
89
|
'decompress': config.auto_decompress
|
90
|
}
|
91
|
|
92
|
if not mapping:
|
93
|
return req_mapping
|
94
|
for entry in mapping:
|
95
|
if entry.has_key('path'):
|
96
|
if isinstance(entry['path'], str):
|
97
|
if re.match(entry['path'], self.env['PATH_INFO']):
|
98
|
req_mapping = self.__get_mappings_hooks(entry, req_mapping)
|
99
|
else:
|
100
|
for path in entry['path']:
|
101
|
if re.match(path, self.env['PATH_INFO']):
|
102
|
req_mapping = self.__get_mappings_hooks(entry, req_mapping)
|
103
|
else:
|
104
|
logger.warning('Config error: you need to specify paths in your mapping')
|
105
|
return req_mapping
|
106
|
|
107
|
def _call_hook(self, hook, *args):
|
108
|
if hook and hook.has_key('filter'):
|
109
|
values = hook.get('values')
|
110
|
if not values:
|
111
|
values = dict()
|
112
|
return hook['filter'](self.env, values, *args)
|
113
|
else:
|
114
|
logger.warning("%s hook failed (no filter option)" % self.env['PATH_INFO'])
|
115
|
return None
|
116
|
|
117
|
def _is_cond_respected(self, hook, request, response):
|
118
|
if hook.has_key('condition') and \
|
119
|
not hook['condition'](self.env, request, response):
|
120
|
return False
|
121
|
return True
|
122
|
|
123
|
def set_request_target(self, request):
|
124
|
""" Add target url on not into the request
|
125
|
"""
|
126
|
request.target = self.target.geturl() + self.env['RAW_URI']
|
127
|
if self.req_mapping['target']:
|
128
|
if "//" in self.req_mapping['target']:
|
129
|
request.target = self.req_mapping['target']
|
130
|
else:
|
131
|
request.target = self.target.geturl() + self.req_mapping['target']
|
132
|
elif self.req_mapping['response'] and \
|
133
|
self._is_cond_respected(self.req_mapping['response'],
|
134
|
request, None):
|
135
|
request.target = None
|
136
|
elif self.req_mapping['redirect']:
|
137
|
request.target = None
|
138
|
return request
|
139
|
|
140
|
def get_response(self, request):
|
141
|
""" Called if you have a response hook for this request
|
142
|
"""
|
143
|
if self.req_mapping['redirect']:
|
144
|
return _302(self.req_mapping['redirect'])
|
145
|
elif self.req_mapping['response']:
|
146
|
logger.debug("Loading response hook(s)")
|
147
|
hook = self.req_mapping['response']
|
148
|
print hook
|
149
|
response = self._call_hook(hook, request, None)
|
150
|
if not response:
|
151
|
return _500(self.env["PATH_INFO"], "The response hook failed")
|
152
|
else:
|
153
|
return _500(self.env["PATH_INFO"], "no response and no target")
|
154
|
return response
|
155
|
|
156
|
def mod_request(self, request):
|
157
|
""" Modify the request
|
158
|
request: MandayeRequest object with cookies and headers
|
159
|
Return the request object """
|
160
|
# Calling hook function
|
161
|
for hook in self.req_mapping['on_request']:
|
162
|
if not self._is_cond_respected(hook, request, None):
|
163
|
continue
|
164
|
new_request = self._call_hook(hook, request)
|
165
|
if new_request:
|
166
|
request = new_request
|
167
|
else:
|
168
|
logger.warning("%s On_request hook %s failed (empty request)" % \
|
169
|
(self.env['PATH_INFO'], hook['filter']))
|
170
|
return request
|
171
|
|
172
|
def mod_response(self, request, response):
|
173
|
""" Modify the response. This will load on_response filters.
|
174
|
request: the Mandaye request
|
175
|
response: MandayeResponse object with cookies, headers and HTML
|
176
|
you can modify the cookies and the HTTP headers """
|
177
|
|
178
|
content_type = response.headers.getheader('content-type')
|
179
|
if content_type:
|
180
|
content_type = content_type.split(';')[0]
|
181
|
|
182
|
# Calling hook function
|
183
|
for hook in self.req_mapping['on_response']:
|
184
|
if not self._is_cond_respected(hook, request, response):
|
185
|
continue
|
186
|
if hook.has_key('content-types') and content_type:
|
187
|
if content_type not in hook['content-types']:
|
188
|
logger.debug("Don't load filter %s (content-type %s doesn't match)" % (hook['filter'], content_type))
|
189
|
continue
|
190
|
|
191
|
if self.req_mapping['decompress']:
|
192
|
response.decompress()
|
193
|
|
194
|
try:
|
195
|
new_response = self._call_hook(hook, request, response)
|
196
|
except Exception, e:
|
197
|
new_response = _500(self.env['PATH_INFO'],
|
198
|
"Hook %s failed with error %s" % (hook, e),
|
199
|
exception=e,
|
200
|
env=self.env)
|
201
|
if new_response:
|
202
|
response = new_response
|
203
|
else:
|
204
|
logger.warning("%s On_response hook %s failed (empty answer)",
|
205
|
self.env['PATH_INFO'], hook['filter'])
|
206
|
return response
|
207
|
|