Mercurial > moin > 1.9
changeset 4239:db15f98e3228
Split up wsgiapp.init and wsgiapp.run into smaller chunks
author | Florian Krupicka <florian.krupicka@googlemail.com> |
---|---|
date | Tue, 22 Jul 2008 20:35:16 +0200 |
parents | f36c84edb3fd |
children | 5dad898baf1f |
files | MoinMoin/web/contexts.py MoinMoin/wsgiapp.py |
diffstat | 2 files changed, 216 insertions(+), 194 deletions(-) [+] |
line wrap: on
line diff
--- a/MoinMoin/web/contexts.py Tue Jul 22 16:58:41 2008 +0200 +++ b/MoinMoin/web/contexts.py Tue Jul 22 20:35:16 2008 +0200 @@ -114,40 +114,16 @@ class LanguageMixin(object): """ Mixin for language attributes and methods. """ - def lang(self): - for key in ('moin.user.lang', 'moin.request.lang'): - if key in self.environ: - return self.environ[key] - - if i18n.languages is None: - i18n.i18n_init(self) - lang = None - - user = getattr(self, 'user') - if user and user.valid and user.language: - lang = user.language - self.environ['moin.user.lang'] = lang - else: - if i18n.languages and not self.cfg.language_ignore_browser: - for l in self.request.accept_languages: - if l in i18n.languages: - lang = l - break - - if lang is None and self.cfg.language_default in i18n.languages: - lang = self.cfg.language_default - else: - lang = 'en' - self.environ['moin.request.lang'] = lang - return lang - lang = property(lang) + lang = EnvironProxy('lang') def getText(self): lang = self.lang def _(text, i18n=i18n, request=self, lang=lang, **kw): return i18n.getText(text, request, lang, **kw) return _ - getText = EnvironProxy(getText) + + getText = property(getText) + _ = getText def content_lang(self): return self.cfg.language_default
--- a/MoinMoin/wsgiapp.py Tue Jul 22 16:58:41 2008 +0200 +++ b/MoinMoin/wsgiapp.py Tue Jul 22 20:35:16 2008 +0200 @@ -7,41 +7,180 @@ @license: GNU GPL, see COPYING for details. """ from werkzeug.http import HeaderSet -from werkzeug.utils import responder -from werkzeug.wrappers import Response -from werkzeug.exceptions import NotFound +from werkzeug.exceptions import HTTPException -from MoinMoin.web.contexts import HTTPContext, RenderContext, AllContext +from MoinMoin.web.contexts import AllContext, Context, XMLRPCContext from MoinMoin.web.request import Request -from MoinMoin.web.utils import check_spider, check_forbidden, check_setuid -from MoinMoin.web.utils import check_surge_protect -from MoinMoin.web.apps import HTTPExceptionsMiddleware +from MoinMoin.web.utils import check_forbidden, check_setuid, check_surge_protect from MoinMoin.Page import Page -from MoinMoin import config, wikiutil, user, caching, error +from MoinMoin import auth, i18n, user, wikiutil, xmlrpc from MoinMoin.action import get_names, get_available_actions -from MoinMoin.config import multiconfig -from MoinMoin.support.python_compatibility import set -from MoinMoin.util import IsWin9x -from MoinMoin.request import MoinMoinFinish, RemoteClosedConnection -from MoinMoin import auth from MoinMoin import log logging = log.getLogger(__name__) def init(request): - request = AllContext(request) - request.clock.start('total') - request.clock.start('base__init__') + """ + Wraps an incoming WSGI request in a Context object and initializes + several important attributes. + """ + context = AllContext(request) + context.clock.start('total') + context.clock.start('init') - request.session = request.cfg.session_service.get_session(request) + context.lang = setup_i18n_preauth(context) - # auth & user handling + context.session = context.cfg.session_service.get_session(request) + + userobj = setup_user(context, context.session) + userobj, olduser = check_setuid(context, userobj) + + if not userobj: + userobj = user.User(context, auth_method='request:invalid') + + context.user = userobj + context._setuid_realuser = olduser + + context.lang = setup_i18n_postauth(context) + + context.reset() + + context.clock.stop('init') + return context + +def run(context): + """ Run a context trough the application. """ + context.clock.start('run') + request = context.request + + # preliminary access checks (forbidden, bots, surge protection) + check_forbidden(context) + check_surge_protect(context) + + action_name = context.action + + # handle XMLRPC calls + if action_name == 'xmlrpc': + response = xmlrpc.xmlrpc(XMLRPCContext(request)) + elif action_name == 'xmlrpc2': + response = xmlrpc.xmlrpc2(XMLRPCContext(request)) + else: + response = dispatch(request, context, action_name) + context.cfg.session_service.finalize(context, context.session) + context.clock.stop('run') + return response + +def remove_prefix(path, prefix=None): + """ Remove an url prefix from the path info and return shortened path. """ + # we can have all action URLs like this: /action/ActionName/PageName?action=ActionName&... + # this is just for robots.txt being able to forbid them for crawlers + if prefix is not None: + prefix = '/%s/' % prefix # e.g. '/action/' + if path.startswith(prefix): + # remove prefix and action name + path = path[len(prefix):] + action, path = (path.split('/', 1) + ['', ''])[:2] + path = '/' + path + return path + +def dispatch(request, context, action_name='show'): + cfg = context.cfg + + # The last component in path_info is the page name, if any + path = remove_prefix(request.path, cfg.url_prefix_action) + + if path.startswith('/'): + pagename = wikiutil.normalize_pagename(path, cfg) + else: + pagename = None + + # need to inform caches that content changes based on: + # * cookie (even if we aren't sending one now) + # * User-Agent (because a bot might be denied and get no content) + # * Accept-Language (except if moin is told to ignore browser language) + hs = HeaderSet(('Cookie', 'User-Agent')) + if not cfg.language_ignore_browser: + hs.add('Accept-Language') + request.headers.add('Vary', str(hs)) + + # Handle request. We have these options: + # 1. jump to page where user left off + if not pagename and context.user.remember_last_visit and action_name == 'show': + response = handle_last_visit(context) + # 2. handle action + else: + response = handle_action(context, pagename, action_name) + if isinstance(response, Context): + response = response.request + return response + +def handle_action(context, pagename, action_name='show'): + """ Actual dispatcher function for non-XMLRPC actions. + + Also sets up the Page object for this request, normalizes and + redirects to canonical pagenames and checks for non-allowed + actions. + """ + _ = context.getText + cfg = context.cfg + + # pagename could be empty after normalization e.g. '///' -> '' + # Use localized FrontPage if pagename is empty + if not pagename: + context.page = wikiutil.getFrontPage(context) + else: + context.page = Page(context, pagename) + if '_' in pagename and not context.page.exists(): + pagename = pagename.replace('_', ' ') + page = Page(context, pagename) + if page.exists(): + url = page.url(context) + return abort(redirect(url)) + + msg = None + # Complain about unknown actions + if not action_name in get_names(cfg): + msg = _("Unknown action %(action_name)s.") % { + 'action_name': wikiutil.escape(action_name), } + + # Disallow non available actions + elif action_name[0].isupper() and not action_name in \ + get_available_actions(cfg, context.page, context.user): + msg = _("You are not allowed to do %(action_name)s on this page.") % { + 'action_name': wikiutil.escape(action_name), } + if not context.user.valid: + # Suggest non valid user to login + msg += " " + _("Login and try again.") + + if msg: + context.theme.add_msg(msg, "error") + context.page.send_page() + # Try action + else: + from MoinMoin import action + handler = action.getHandler(cfg, action_name) + if handler is None: + msg = _("You are not allowed to do %(action_name)s on this page.") % { + 'action_name': wikiutil.escape(action_name), } + if not context.user.valid: + # Suggest non valid user to login + msg += " " + _("Login and try again.") + context.theme.add_msg(msg, "error") + context.page.send_page() + else: + handler(context.page.page_name, context) + + return context + +def setup_user(context, session): + """ Try to retrieve a valid user object from the request, be it + either through the session or through a login. """ # first try setting up from session - userobj = auth.setup_from_session(request, request.session) + userobj = auth.setup_from_session(context, session) # then handle login/logout forms - form = request.values + form = context.request.values if 'login' in form: params = { @@ -51,161 +190,68 @@ 'openid_identifier': form.get('openid_identifier'), 'stage': form.get('stage') } - userobj = auth.handle_login(request, userobj, **params) + userobj = auth.handle_login(context, userobj, **params) elif 'logout' in form: - userobj = auth.handle_logout(request, userobj) + userobj = auth.handle_logout(context, userobj) else: - userobj = auth.handle_request(request, userobj) - - # check for setuid-handling of users - userobj, olduser = check_setuid(request, userobj) - - if not userobj: - userobj = user.User(request, auth_method='request:invalid') - - request.user = userobj - request._setuid_real_user = olduser - - # preliminary access control - # check against spiders, blacklists and request-spam - check_forbidden(request) - check_surge_protect(request) - - request.reset() - - request.clock.stop('base__init__') - return request - -def run(request): - - _ = request.getText - request.clock.start('run') - - action_name = request.action - if request.cfg.log_timing: - request.timing_log(True, action_name) - - # parse request data - try: - # The last component in path_info is the page name, if any - path = request.path - - # we can have all action URLs like this: /action/ActionName/PageName?action=ActionName&... - # this is just for robots.txt being able to forbid them for crawlers - prefix = request.cfg.url_prefix_action - if prefix is not None: - prefix = '/%s/' % prefix # e.g. '/action/' - if path.startswith(prefix): - # remove prefix and action name - path = path[len(prefix):] - action, path = (path.split('/', 1) + ['', ''])[:2] - path = '/' + path - - if path.startswith('/'): - pagename = wikiutil.normalize_pagename(path, request.cfg) - else: - pagename = None - - # need to inform caches that content changes based on: - # * cookie (even if we aren't sending one now) - # * User-Agent (because a bot might be denied and get no content) - # * Accept-Language (except if moin is told to ignore browser language) - hs = HeaderSet(('Cookie', 'User-Agent')) - if not request.cfg.language_ignore_browser: - hs.add('Accept-Language') - request.headers.add('Vary', str(hs)) + userobj = auth.handle_request(context, userobj) - # Handle request. We have these options: - # 1. jump to page where user left off - if not pagename and request.user.remember_last_visit and action_name == 'show': - pagetrail = request.user.getTrail() - if pagetrail: - # Redirect to last page visited - last_visited = pagetrail[-1] - wikiname, pagename = wikiutil.split_interwiki(last_visited) - if wikiname != 'Self': - wikitag, wikiurl, wikitail, error = wikiutil.resolve_interwiki(request, wikiname, pagename) - url = wikiurl + wikiutil.quoteWikinameURL(wikitail) - else: - url = Page(request, pagename).url(request) - else: - # Or to localized FrontPage - url = wikiutil.getFrontPage(request).url(request) - return abort(redirect(url)) - - # 2. handle action - else: - # pagename could be empty after normalization e.g. '///' -> '' - # Use localized FrontPage if pagename is empty - if not pagename: - request.page = wikiutil.getFrontPage(request) - else: - request.page = Page(request, pagename) - if '_' in pagename and not request.page.exists(): - pagename = pagename.replace('_', ' ') - page = Page(request, pagename) - if page.exists(): - url = page.url(request) - return abort(redirect(url)) - - msg = None - # Complain about unknown actions - if not action_name in get_names(request.cfg): - msg = _("Unknown action %(action_name)s.") % { - 'action_name': wikiutil.escape(action_name), } + return userobj - # Disallow non available actions - elif action_name[0].isupper() and not action_name in \ - get_available_actions(request.cfg, request.page, request.user): - msg = _("You are not allowed to do %(action_name)s on this page.") % { - 'action_name': wikiutil.escape(action_name), } - if not request.user.valid: - # Suggest non valid user to login - msg += " " + _("Login and try again.") +def setup_i18n_preauth(context): + """ Determine language for the request in absence of any user info. """ + if i18n.languages is None: + i18n.i18n_init(context) - if msg: - request.theme.add_msg(msg, "error") - request.page.send_page() - # Try action - else: - from MoinMoin import action - handler = action.getHandler(request.cfg, action_name) - if handler is None: - msg = _("You are not allowed to do %(action_name)s on this page.") % { - 'action_name': wikiutil.escape(action_name), } - if not request.user.valid: - # Suggest non valid user to login - msg += " " + _("Login and try again.") - request.theme.add_msg(msg, "error") - request.page.send_page() - else: - handler(request.page.page_name, request) - - # every action that didn't use to raise MoinMoinFinish must call this now: - # request.theme.send_closing_html() + cfg = context.cfg + lang = None + if i18n.languages and not cfg.language_ignore_browser: + for l in context.request.accept_languages: + if l in i18n.languages: + lang = l + break + if lang is None and cfg.language_default in i18n.languages: + lang = cfg.language_default + else: + lang = 'en' + return lang - except MoinMoinFinish: - pass - except RemoteClosedConnection: - # at least clean up - pass - except SystemExit: - raise # fcgi uses this to terminate a thread - - if request.cfg.log_timing: - request.timing_log(False, action_name) +def setup_i18n_postauth(context): + """ Determine language for the request after user-id is established. """ + user = context.user + if user and user.valid and user.language: + return user.language + else: + return context.lang - #return request.finish() - request.cfg.session_service.finalize(request, request.session) - return request +def handle_last_visit(request, context): + """ Redirect to last visited page (or frontpage) on missing pagename. """ + pagetrail = context.user.getTrail() + if pagetrail: + # Redirect to last page visited + last_visited = pagetrail[-1] + wikiname, pagename = wikiutil.split_interwiki(last_visited) + if wikiname != 'Self': + wikitag, wikiurl, wikitail, error = wikiutil.resolve_interwiki(context, wikiname, pagename) + url = wikiurl + wikiutil.quoteWikinameURL(wikitail) + else: + url = Page(context, pagename).url(context) + else: + # Or to localized FrontPage + url = wikiutil.getFrontPage(context).url(context) + return abort(redirect(url)) -def application(request): - run(init(request)) +def application(environ, start_response): + try: + request = Request(environ) + context = init(request) + response = run(context) + except HTTPException, e: + context.clock.stop('run') + context.clock.stop('total') + response = e - return request - -application = Request.application(application) -application = HTTPExceptionsMiddleware(application) + return response(environ, start_response) def run_server(config): from os import path