pyramid: make responses/exceptions from pyramid/pylons work
This commit is contained in:
parent
d6e940710d
commit
fdcf88bd53
6 changed files with 86 additions and 49 deletions
|
|
@ -25,13 +25,15 @@ import logging
|
|||
|
||||
from paste.registry import RegistryManager
|
||||
from paste.gzipper import make_gzip_middleware
|
||||
from pylons.middleware import ErrorHandler, StatusCodeRedirect
|
||||
from pylons.wsgiapp import PylonsApp
|
||||
from pyramid.authorization import ACLAuthorizationPolicy
|
||||
from pyramid.config import Configurator
|
||||
from pyramid.static import static_view
|
||||
from pyramid.settings import asbool, aslist
|
||||
from pyramid.wsgi import wsgiapp
|
||||
from pyramid.httpexceptions import HTTPError
|
||||
import pyramid.httpexceptions as httpexceptions
|
||||
from pyramid.renderers import render_to_response
|
||||
from routes.middleware import RoutesMiddleware
|
||||
import routes.util
|
||||
|
||||
|
|
@ -87,38 +89,15 @@ def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
|
|||
app = csrf.OriginChecker(app, expected_origin,
|
||||
skip_urls=[routes.util.url_for('api')])
|
||||
|
||||
# Add RoutesMiddleware. Currently we have two instances in the stack. This
|
||||
# is the lower one to make the StatusCodeRedirect middleware happy.
|
||||
# TODO: johbo: This is not optimal, search for a better solution.
|
||||
app = RoutesMiddleware(app, config['routes.map'])
|
||||
|
||||
# CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
|
||||
if asbool(config['pdebug']):
|
||||
from rhodecode.lib.profiler import ProfilingMiddleware
|
||||
app = ProfilingMiddleware(app)
|
||||
|
||||
# Protect from VCS Server error related pages when server is not available
|
||||
vcs_server_enabled = asbool(config.get('vcs.server.enable', 'true'))
|
||||
if not vcs_server_enabled:
|
||||
app = DisableVCSPagesWrapper(app)
|
||||
|
||||
if asbool(full_stack):
|
||||
|
||||
# Appenlight monitoring and error handler
|
||||
app, appenlight_client = wrap_in_appenlight_if_enabled(app, config)
|
||||
|
||||
# Handle Python exceptions
|
||||
app = ErrorHandler(app, global_conf, **config['pylons.errorware'])
|
||||
|
||||
# we want our low level middleware to get to the request ASAP. We don't
|
||||
# need any pylons stack middleware in them
|
||||
app = VCSMiddleware(app, config, appenlight_client)
|
||||
# Display error documents for 401, 403, 404 status codes (and
|
||||
# 500 when debug is disabled)
|
||||
if asbool(config['debug']):
|
||||
app = StatusCodeRedirect(app)
|
||||
else:
|
||||
app = StatusCodeRedirect(app, [400, 401, 403, 404, 500])
|
||||
|
||||
# enable https redirects based on HTTP_X_URL_SCHEME set by proxy
|
||||
app = HttpsFixup(app, config)
|
||||
|
|
@ -179,6 +158,36 @@ def add_pylons_compat_data(registry, global_config, settings):
|
|||
registry._pylons_compat_settings = settings
|
||||
|
||||
|
||||
def error_handler(exc, request):
|
||||
# TODO: dan: replace the old pylons error controller with this
|
||||
from rhodecode.model.settings import SettingsModel
|
||||
from rhodecode.lib.utils2 import AttributeDict
|
||||
|
||||
try:
|
||||
rc_config = SettingsModel().get_all_settings()
|
||||
except Exception:
|
||||
log.exception('failed to fetch settings')
|
||||
rc_config = {}
|
||||
|
||||
c = AttributeDict()
|
||||
c.error_message = exc.status
|
||||
c.error_explanation = exc.explanation or str(exc)
|
||||
c.visual = AttributeDict()
|
||||
|
||||
c.visual.rhodecode_support_url = (
|
||||
request.registry.settings.get('rhodecode_support_url') or
|
||||
request.route_url('rhodecode_support')
|
||||
)
|
||||
c.redirect_time = 0
|
||||
c.rhodecode_name = rc_config.get('rhodecode_title')
|
||||
if not c.rhodecode_name:
|
||||
c.rhodecode_name = 'Rhodecode'
|
||||
|
||||
response = render_to_response(
|
||||
'/errors/error_document.html', {'c': c}, request=request)
|
||||
return response
|
||||
|
||||
|
||||
def includeme(config):
|
||||
settings = config.registry.settings
|
||||
|
||||
|
|
@ -189,6 +198,8 @@ def includeme(config):
|
|||
config.include('rhodecode.login')
|
||||
config.include('rhodecode.tweens')
|
||||
config.include('rhodecode.api')
|
||||
config.add_route(
|
||||
'rhodecode_support', 'https://rhodecode.com/help/', static=True)
|
||||
|
||||
# Set the authorization policy.
|
||||
authz_policy = ACLAuthorizationPolicy()
|
||||
|
|
@ -207,17 +218,43 @@ def includeme(config):
|
|||
for inc in includes:
|
||||
config.include(inc)
|
||||
|
||||
# This is the glue which allows us to migrate in chunks. By registering the
|
||||
# pylons based application as the "Not Found" view in Pyramid, we will
|
||||
# fallback to the old application each time the new one does not yet know
|
||||
# how to handle a request.
|
||||
pylons_app = make_app(
|
||||
config.registry._pylons_compat_global_config,
|
||||
**config.registry._pylons_compat_settings)
|
||||
config.registry._pylons_compat_config = pylons_app.config
|
||||
pylons_app_as_view = wsgiapp(pylons_app)
|
||||
config.add_notfound_view(pylons_app_as_view)
|
||||
|
||||
pylons_app_as_view = wsgiapp(pylons_app)
|
||||
|
||||
# Protect from VCS Server error related pages when server is not available
|
||||
vcs_server_enabled = asbool(settings.get('vcs.server.enable', 'true'))
|
||||
if not vcs_server_enabled:
|
||||
pylons_app_as_view = DisableVCSPagesWrapper(pylons_app_as_view)
|
||||
|
||||
|
||||
def pylons_app_with_error_handler(context, request):
|
||||
"""
|
||||
Handle exceptions from rc pylons app:
|
||||
|
||||
- old webob type exceptions get converted to pyramid exceptions
|
||||
- pyramid exceptions are passed to the error handler view
|
||||
"""
|
||||
try:
|
||||
response = pylons_app_as_view(context, request)
|
||||
if 400 <= response.status_int <= 599: # webob type error responses
|
||||
ExcClass = httpexceptions.status_map[response.status_int]
|
||||
return error_handler(ExcClass(response.status), request)
|
||||
except HTTPError as e: # pyramid type exceptions
|
||||
return error_handler(e, request)
|
||||
|
||||
return response
|
||||
|
||||
# This is the glue which allows us to migrate in chunks. By registering the
|
||||
# pylons based application as the "Not Found" view in Pyramid, we will
|
||||
# fallback to the old application each time the new one does not yet know
|
||||
# how to handle a request.
|
||||
config.add_notfound_view(pylons_app_with_error_handler)
|
||||
|
||||
config.add_view(error_handler, context=HTTPError) # exceptions in rc pyramid
|
||||
|
||||
def includeme_last(config):
|
||||
"""
|
||||
|
|
@ -253,8 +290,7 @@ def wrap_app_in_wsgi_middlewares(pyramid_app, config):
|
|||
"""
|
||||
settings = config.registry.settings
|
||||
|
||||
# Add RoutesMiddleware. Currently we have two instances in the stack. This
|
||||
# is the upper one to support the pylons compatibility tween during
|
||||
# Add RoutesMiddleware to support the pylons compatibility tween during
|
||||
# migration to pyramid.
|
||||
pyramid_app = RoutesMiddleware(
|
||||
pyramid_app, config.registry._pylons_compat_config['routes.map'])
|
||||
|
|
|
|||
|
|
@ -166,10 +166,6 @@ def make_map(config):
|
|||
def check_int(environ, match_dict):
|
||||
return match_dict.get('id').isdigit()
|
||||
|
||||
# The ErrorController route (handles 404/500 error pages); it should
|
||||
# likely stay at the top, ensuring it can always be resolved
|
||||
rmap.connect('/error/{action}', controller='error')
|
||||
rmap.connect('/error/{action}/{id}', controller='error')
|
||||
|
||||
#==========================================================================
|
||||
# CUSTOM ROUTES HERE
|
||||
|
|
|
|||
|
|
@ -24,15 +24,21 @@ Disable VCS pages when VCS Server is not available
|
|||
|
||||
import logging
|
||||
import re
|
||||
|
||||
from pyramid.httpexceptions import HTTPBadGateway
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class VCSServerUnavailable(HTTPBadGateway):
|
||||
""" HTTP Exception class for when VCS Server is unavailable """
|
||||
code = 502
|
||||
title = 'VCS Server Required'
|
||||
explanation = 'A VCS Server is required for this action. There is currently no VCS Server configured.'
|
||||
|
||||
class DisableVCSPagesWrapper(object):
|
||||
"""
|
||||
Wrapper to disable all pages that require VCS Server to be running,
|
||||
avoiding that errors explode to the user.
|
||||
Pyramid view wrapper to disable all pages that require VCS Server to be
|
||||
running, avoiding that errors explode to the user.
|
||||
|
||||
This Wrapper should be enabled only in case VCS Server is not available
|
||||
for the instance.
|
||||
|
|
@ -60,11 +66,11 @@ class DisableVCSPagesWrapper(object):
|
|||
log.debug('accessing: `%s` with VCS Server disabled', path_info)
|
||||
return False
|
||||
|
||||
def __init__(self, app):
|
||||
self.application = app
|
||||
def __init__(self, handler):
|
||||
self.handler = handler
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
if not self._check_vcs_requirement(environ['PATH_INFO']):
|
||||
environ['PATH_INFO'] = '/error/vcs_unavailable'
|
||||
def __call__(self, context, request):
|
||||
if not self._check_vcs_requirement(request.environ['PATH_INFO']):
|
||||
raise VCSServerUnavailable('VCS Server is not available')
|
||||
|
||||
return self.application(environ, start_response)
|
||||
return self.handler(context, request)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import rhodecode
|
|||
from pylons.i18n.translation import _get_translator
|
||||
from pylons.util import ContextObj
|
||||
from routes.util import URLGenerator
|
||||
from pyramid.httpexceptions import HTTPInternalServerError, HTTPError, HTTPServiceUnavailable
|
||||
|
||||
from rhodecode.lib.base import attach_context_attributes, get_auth_user
|
||||
from rhodecode.model import meta
|
||||
|
|
@ -69,8 +70,8 @@ def pylons_compatibility_tween_factory(handler, registry):
|
|||
context.rhodecode_user = auth_user
|
||||
attach_context_attributes(context)
|
||||
pylons.tmpl_context._push_object(context)
|
||||
|
||||
return handler(request)
|
||||
response = handler(request)
|
||||
return response
|
||||
finally:
|
||||
# Dispose current database session and rollback uncommitted
|
||||
# transactions.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue