18 """CubicWeb web client application object""" |
18 """CubicWeb web client application object""" |
19 |
19 |
20 __docformat__ = "restructuredtext en" |
20 __docformat__ = "restructuredtext en" |
21 |
21 |
22 import contextlib |
22 import contextlib |
|
23 from functools import wraps |
23 import json |
24 import json |
24 import sys |
25 import sys |
25 from time import clock, time |
26 from time import clock, time |
26 from contextlib import contextmanager |
27 from contextlib import contextmanager |
27 from warnings import warn |
28 from warnings import warn |
28 |
29 |
29 from six import text_type, binary_type |
30 from six import PY2, text_type, binary_type |
30 from six.moves import http_client |
31 from six.moves import http_client |
31 |
32 |
32 from rql import BadRQLQuery |
33 from rql import BadRQLQuery |
33 |
34 |
34 from cubicweb import set_log_methods |
35 from cubicweb import set_log_methods |
43 from cubicweb.web.request import CubicWebRequestBase |
44 from cubicweb.web.request import CubicWebRequestBase |
44 |
45 |
45 # make session manager available through a global variable so the debug view can |
46 # make session manager available through a global variable so the debug view can |
46 # print information about web session |
47 # print information about web session |
47 SESSION_MANAGER = None |
48 SESSION_MANAGER = None |
|
49 |
|
50 |
|
51 def _deprecated_path_arg(func): |
|
52 @wraps(func) |
|
53 def wrapper(self, req, *args, **kwargs): |
|
54 if args or 'path' in kwargs: |
|
55 func_name = func.func_name if PY2 else func.__name__ |
|
56 warn('[3.24] path argument got removed from "%s" parameters' % func_name, |
|
57 DeprecationWarning) |
|
58 return func(self, req) |
|
59 return wrapper |
|
60 |
|
61 |
|
62 def _deprecated_req_path_swapped(func): |
|
63 @wraps(func) |
|
64 def wrapper(self, req, *args, **kwargs): |
|
65 if not isinstance(req, CubicWebRequestBase): |
|
66 warn('[3.15] Application entry point arguments are now (req, path) ' |
|
67 'not (path, req)', DeprecationWarning, 2) |
|
68 path = req |
|
69 req = args[0] if args else kwargs.pop('req') |
|
70 args = (path, ) + args[1:] |
|
71 return func(self, req, *args, **kwargs) |
|
72 return wrapper |
48 |
73 |
49 |
74 |
50 @contextmanager |
75 @contextmanager |
51 def anonymized_request(req): |
76 def anonymized_request(req): |
52 orig_cnx = req.cnx |
77 orig_cnx = req.cnx |
196 """ |
221 """ |
197 return self.session_handler.get_session(req) |
222 return self.session_handler.get_session(req) |
198 |
223 |
199 # publish methods ######################################################### |
224 # publish methods ######################################################### |
200 |
225 |
201 def log_handle_request(self, req, path): |
226 @_deprecated_path_arg |
|
227 def log_handle_request(self, req): |
202 """wrapper around _publish to log all queries executed for a given |
228 """wrapper around _publish to log all queries executed for a given |
203 accessed path |
229 accessed path |
204 """ |
230 """ |
205 def wrap_set_cnx(func): |
231 def wrap_set_cnx(func): |
206 |
232 |
238 self._query_log.write('\n'.join(result)) |
264 self._query_log.write('\n'.join(result)) |
239 self._query_log.flush() |
265 self._query_log.flush() |
240 except Exception: |
266 except Exception: |
241 self.exception('error while logging queries') |
267 self.exception('error while logging queries') |
242 |
268 |
243 def main_handle_request(self, req, path): |
269 @_deprecated_req_path_swapped |
244 """Process an http request |
270 @_deprecated_path_arg |
245 |
271 def main_handle_request(self, req): |
246 Arguments are: |
272 """Process an HTTP request `req` |
247 - a Request object |
273 |
248 - path of the request object |
274 :type req: `web.Request` |
|
275 :param req: the request object |
249 |
276 |
250 It returns the content of the http response. HTTP header and status are |
277 It returns the content of the http response. HTTP header and status are |
251 set on the Request object. |
278 set on the Request object. |
252 """ |
279 """ |
253 if not isinstance(req, CubicWebRequestBase): |
|
254 warn('[3.15] Application entry point arguments are now (req, path) ' |
|
255 'not (path, req)', DeprecationWarning, 2) |
|
256 req, path = path, req |
|
257 if req.authmode == 'http': |
280 if req.authmode == 'http': |
258 # activate realm-based auth |
281 # activate realm-based auth |
259 realm = self.vreg.config['realm'] |
282 realm = self.vreg.config['realm'] |
260 req.set_header('WWW-Authenticate', [('Basic', {'realm': realm})], raw=False) |
283 req.set_header('WWW-Authenticate', [('Basic', {'realm': realm})], raw=False) |
261 content = b'' |
284 content = b'' |
278 # nested try to allow LogOut to delegate logic to AuthenticationError |
301 # nested try to allow LogOut to delegate logic to AuthenticationError |
279 # handler |
302 # handler |
280 try: |
303 try: |
281 # Try to generate the actual request content |
304 # Try to generate the actual request content |
282 with cnx: |
305 with cnx: |
283 content = self.core_handle(req, path) |
306 content = self.core_handle(req) |
284 # Handle user log-out |
307 # Handle user log-out |
285 except LogOut as ex: |
308 except LogOut as ex: |
286 # When authentification is handled by cookie the code that |
309 # When authentification is handled by cookie the code that |
287 # raised LogOut must has invalidated the cookie. We can just |
310 # raised LogOut must has invalidated the cookie. We can just |
288 # reload the original url without authentification |
311 # reload the original url without authentification |
327 if not content: |
350 if not content: |
328 content = self.need_login_content(req) |
351 content = self.need_login_content(req) |
329 assert isinstance(content, binary_type) |
352 assert isinstance(content, binary_type) |
330 return content |
353 return content |
331 |
354 |
332 def core_handle(self, req, path): |
355 @_deprecated_path_arg |
333 """method called by the main publisher to process <path> |
356 def core_handle(self, req): |
|
357 """method called by the main publisher to process <req> relative path |
334 |
358 |
335 should return a string containing the resulting page or raise a |
359 should return a string containing the resulting page or raise a |
336 `NotFound` exception |
360 `NotFound` exception |
337 |
361 |
338 :type path: str |
|
339 :param path: the path part of the url to publish |
|
340 |
|
341 :type req: `web.Request` |
362 :type req: `web.Request` |
342 :param req: the request object |
363 :param req: the request object |
343 |
364 |
344 :rtype: str |
365 :rtype: str |
345 :return: the result of the pusblished url |
366 :return: the result of the pusblished url |
346 """ |
367 """ |
|
368 path = req.relative_path(False) |
347 # don't log form values they may contains sensitive information |
369 # don't log form values they may contains sensitive information |
348 self.debug('publish "%s" (%s, form params: %s)', |
370 self.debug('publish "%s" (%s, form params: %s)', path, |
349 path, req.session.sessionid, list(req.form)) |
371 req.session.sessionid, list(req.form)) |
350 # remove user callbacks on a new request (except for json controllers |
372 # remove user callbacks on a new request (except for json controllers |
351 # to avoid callbacks being unregistered before they could be called) |
373 # to avoid callbacks being unregistered before they could be called) |
352 tstart = clock() |
374 tstart = clock() |
353 commited = False |
375 commited = False |
354 try: |
376 try: |
421 try: |
443 try: |
422 req.cnx.rollback() |
444 req.cnx.rollback() |
423 except Exception: |
445 except Exception: |
424 pass # ignore rollback error at this point |
446 pass # ignore rollback error at this point |
425 self.add_undo_link_to_msg(req) |
447 self.add_undo_link_to_msg(req) |
426 self.debug('query %s executed in %s sec', req.relative_path(), clock() - tstart) |
448 self.debug('query %s executed in %s sec', path, clock() - tstart) |
427 return result |
449 return result |
428 |
450 |
429 # Error handlers |
451 # Error handlers |
430 |
452 |
431 def redirect_handler(self, req, ex): |
453 def redirect_handler(self, req, ex): |