##// END OF EJS Templates
error-view: expose traceback variable in error view.
marcink -
r1915:33fd40f0 default
parent child Browse files
Show More
@@ -1,529 +1,527 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Pylons middleware initialization
22 Pylons middleware initialization
23 """
23 """
24 import logging
24 import logging
25 import traceback
25 from collections import OrderedDict
26 from collections import OrderedDict
26
27
27 from paste.registry import RegistryManager
28 from paste.registry import RegistryManager
28 from paste.gzipper import make_gzip_middleware
29 from paste.gzipper import make_gzip_middleware
29 from pylons.wsgiapp import PylonsApp
30 from pylons.wsgiapp import PylonsApp
30 from pyramid.authorization import ACLAuthorizationPolicy
31 from pyramid.authorization import ACLAuthorizationPolicy
31 from pyramid.config import Configurator
32 from pyramid.config import Configurator
32 from pyramid.settings import asbool, aslist
33 from pyramid.settings import asbool, aslist
33 from pyramid.wsgi import wsgiapp
34 from pyramid.wsgi import wsgiapp
34 from pyramid.httpexceptions import (
35 from pyramid.httpexceptions import (
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound)
36 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound)
36 from pyramid.events import ApplicationCreated
37 from pyramid.events import ApplicationCreated
37 from pyramid.renderers import render_to_response
38 from pyramid.renderers import render_to_response
38 from routes.middleware import RoutesMiddleware
39 from routes.middleware import RoutesMiddleware
39 import routes.util
40
41 import rhodecode
40 import rhodecode
42
41
43 from rhodecode.model import meta
42 from rhodecode.model import meta
44 from rhodecode.config import patches
43 from rhodecode.config import patches
45 from rhodecode.config.routing import STATIC_FILE_PREFIX
44 from rhodecode.config.routing import STATIC_FILE_PREFIX
46 from rhodecode.config.environment import (
45 from rhodecode.config.environment import (
47 load_environment, load_pyramid_environment)
46 load_environment, load_pyramid_environment)
48
47
49 from rhodecode.lib.vcs import VCSCommunicationError
48 from rhodecode.lib.vcs import VCSCommunicationError
50 from rhodecode.lib.exceptions import VCSServerUnavailable
49 from rhodecode.lib.exceptions import VCSServerUnavailable
51 from rhodecode.lib.middleware import csrf
52 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
50 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
53 from rhodecode.lib.middleware.error_handling import (
51 from rhodecode.lib.middleware.error_handling import (
54 PylonsErrorHandlingMiddleware)
52 PylonsErrorHandlingMiddleware)
55 from rhodecode.lib.middleware.https_fixup import HttpsFixup
53 from rhodecode.lib.middleware.https_fixup import HttpsFixup
56 from rhodecode.lib.middleware.vcs import VCSMiddleware
54 from rhodecode.lib.middleware.vcs import VCSMiddleware
57 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
55 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
58 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
56 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
59 from rhodecode.subscribers import (
57 from rhodecode.subscribers import (
60 scan_repositories_if_enabled, write_js_routes_if_enabled,
58 scan_repositories_if_enabled, write_js_routes_if_enabled,
61 write_metadata_if_needed)
59 write_metadata_if_needed)
62
60
63
61
64 log = logging.getLogger(__name__)
62 log = logging.getLogger(__name__)
65
63
66
64
67 # this is used to avoid avoid the route lookup overhead in routesmiddleware
65 # this is used to avoid avoid the route lookup overhead in routesmiddleware
68 # for certain routes which won't go to pylons to - eg. static files, debugger
66 # for certain routes which won't go to pylons to - eg. static files, debugger
69 # it is only needed for the pylons migration and can be removed once complete
67 # it is only needed for the pylons migration and can be removed once complete
70 class SkippableRoutesMiddleware(RoutesMiddleware):
68 class SkippableRoutesMiddleware(RoutesMiddleware):
71 """ Routes middleware that allows you to skip prefixes """
69 """ Routes middleware that allows you to skip prefixes """
72
70
73 def __init__(self, *args, **kw):
71 def __init__(self, *args, **kw):
74 self.skip_prefixes = kw.pop('skip_prefixes', [])
72 self.skip_prefixes = kw.pop('skip_prefixes', [])
75 super(SkippableRoutesMiddleware, self).__init__(*args, **kw)
73 super(SkippableRoutesMiddleware, self).__init__(*args, **kw)
76
74
77 def __call__(self, environ, start_response):
75 def __call__(self, environ, start_response):
78 for prefix in self.skip_prefixes:
76 for prefix in self.skip_prefixes:
79 if environ['PATH_INFO'].startswith(prefix):
77 if environ['PATH_INFO'].startswith(prefix):
80 # added to avoid the case when a missing /_static route falls
78 # added to avoid the case when a missing /_static route falls
81 # through to pylons and causes an exception as pylons is
79 # through to pylons and causes an exception as pylons is
82 # expecting wsgiorg.routingargs to be set in the environ
80 # expecting wsgiorg.routingargs to be set in the environ
83 # by RoutesMiddleware.
81 # by RoutesMiddleware.
84 if 'wsgiorg.routing_args' not in environ:
82 if 'wsgiorg.routing_args' not in environ:
85 environ['wsgiorg.routing_args'] = (None, {})
83 environ['wsgiorg.routing_args'] = (None, {})
86 return self.app(environ, start_response)
84 return self.app(environ, start_response)
87
85
88 return super(SkippableRoutesMiddleware, self).__call__(
86 return super(SkippableRoutesMiddleware, self).__call__(
89 environ, start_response)
87 environ, start_response)
90
88
91
89
92 def make_app(global_conf, static_files=True, **app_conf):
90 def make_app(global_conf, static_files=True, **app_conf):
93 """Create a Pylons WSGI application and return it
91 """Create a Pylons WSGI application and return it
94
92
95 ``global_conf``
93 ``global_conf``
96 The inherited configuration for this application. Normally from
94 The inherited configuration for this application. Normally from
97 the [DEFAULT] section of the Paste ini file.
95 the [DEFAULT] section of the Paste ini file.
98
96
99 ``app_conf``
97 ``app_conf``
100 The application's local configuration. Normally specified in
98 The application's local configuration. Normally specified in
101 the [app:<name>] section of the Paste ini file (where <name>
99 the [app:<name>] section of the Paste ini file (where <name>
102 defaults to main).
100 defaults to main).
103
101
104 """
102 """
105 # Apply compatibility patches
103 # Apply compatibility patches
106 patches.kombu_1_5_1_python_2_7_11()
104 patches.kombu_1_5_1_python_2_7_11()
107 patches.inspect_getargspec()
105 patches.inspect_getargspec()
108
106
109 # Configure the Pylons environment
107 # Configure the Pylons environment
110 config = load_environment(global_conf, app_conf)
108 config = load_environment(global_conf, app_conf)
111
109
112 # The Pylons WSGI app
110 # The Pylons WSGI app
113 app = PylonsApp(config=config)
111 app = PylonsApp(config=config)
114
112
115 # Establish the Registry for this application
113 # Establish the Registry for this application
116 app = RegistryManager(app)
114 app = RegistryManager(app)
117
115
118 app.config = config
116 app.config = config
119
117
120 return app
118 return app
121
119
122
120
123 def make_pyramid_app(global_config, **settings):
121 def make_pyramid_app(global_config, **settings):
124 """
122 """
125 Constructs the WSGI application based on Pyramid and wraps the Pylons based
123 Constructs the WSGI application based on Pyramid and wraps the Pylons based
126 application.
124 application.
127
125
128 Specials:
126 Specials:
129
127
130 * We migrate from Pylons to Pyramid. While doing this, we keep both
128 * We migrate from Pylons to Pyramid. While doing this, we keep both
131 frameworks functional. This involves moving some WSGI middlewares around
129 frameworks functional. This involves moving some WSGI middlewares around
132 and providing access to some data internals, so that the old code is
130 and providing access to some data internals, so that the old code is
133 still functional.
131 still functional.
134
132
135 * The application can also be integrated like a plugin via the call to
133 * The application can also be integrated like a plugin via the call to
136 `includeme`. This is accompanied with the other utility functions which
134 `includeme`. This is accompanied with the other utility functions which
137 are called. Changing this should be done with great care to not break
135 are called. Changing this should be done with great care to not break
138 cases when these fragments are assembled from another place.
136 cases when these fragments are assembled from another place.
139
137
140 """
138 """
141 # The edition string should be available in pylons too, so we add it here
139 # The edition string should be available in pylons too, so we add it here
142 # before copying the settings.
140 # before copying the settings.
143 settings.setdefault('rhodecode.edition', 'Community Edition')
141 settings.setdefault('rhodecode.edition', 'Community Edition')
144
142
145 # As long as our Pylons application does expect "unprepared" settings, make
143 # As long as our Pylons application does expect "unprepared" settings, make
146 # sure that we keep an unmodified copy. This avoids unintentional change of
144 # sure that we keep an unmodified copy. This avoids unintentional change of
147 # behavior in the old application.
145 # behavior in the old application.
148 settings_pylons = settings.copy()
146 settings_pylons = settings.copy()
149
147
150 sanitize_settings_and_apply_defaults(settings)
148 sanitize_settings_and_apply_defaults(settings)
151 config = Configurator(settings=settings)
149 config = Configurator(settings=settings)
152 add_pylons_compat_data(config.registry, global_config, settings_pylons)
150 add_pylons_compat_data(config.registry, global_config, settings_pylons)
153
151
154 load_pyramid_environment(global_config, settings)
152 load_pyramid_environment(global_config, settings)
155
153
156 includeme_first(config)
154 includeme_first(config)
157 includeme(config)
155 includeme(config)
158 pyramid_app = config.make_wsgi_app()
156 pyramid_app = config.make_wsgi_app()
159 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
157 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
160 pyramid_app.config = config
158 pyramid_app.config = config
161
159
162 # creating the app uses a connection - return it after we are done
160 # creating the app uses a connection - return it after we are done
163 meta.Session.remove()
161 meta.Session.remove()
164
162
165 return pyramid_app
163 return pyramid_app
166
164
167
165
168 def make_not_found_view(config):
166 def make_not_found_view(config):
169 """
167 """
170 This creates the view which should be registered as not-found-view to
168 This creates the view which should be registered as not-found-view to
171 pyramid. Basically it contains of the old pylons app, converted to a view.
169 pyramid. Basically it contains of the old pylons app, converted to a view.
172 Additionally it is wrapped by some other middlewares.
170 Additionally it is wrapped by some other middlewares.
173 """
171 """
174 settings = config.registry.settings
172 settings = config.registry.settings
175 vcs_server_enabled = settings['vcs.server.enable']
173 vcs_server_enabled = settings['vcs.server.enable']
176
174
177 # Make pylons app from unprepared settings.
175 # Make pylons app from unprepared settings.
178 pylons_app = make_app(
176 pylons_app = make_app(
179 config.registry._pylons_compat_global_config,
177 config.registry._pylons_compat_global_config,
180 **config.registry._pylons_compat_settings)
178 **config.registry._pylons_compat_settings)
181 config.registry._pylons_compat_config = pylons_app.config
179 config.registry._pylons_compat_config = pylons_app.config
182
180
183 # Appenlight monitoring.
181 # Appenlight monitoring.
184 pylons_app, appenlight_client = wrap_in_appenlight_if_enabled(
182 pylons_app, appenlight_client = wrap_in_appenlight_if_enabled(
185 pylons_app, settings)
183 pylons_app, settings)
186
184
187 # The pylons app is executed inside of the pyramid 404 exception handler.
185 # The pylons app is executed inside of the pyramid 404 exception handler.
188 # Exceptions which are raised inside of it are not handled by pyramid
186 # Exceptions which are raised inside of it are not handled by pyramid
189 # again. Therefore we add a middleware that invokes the error handler in
187 # again. Therefore we add a middleware that invokes the error handler in
190 # case of an exception or error response. This way we return proper error
188 # case of an exception or error response. This way we return proper error
191 # HTML pages in case of an error.
189 # HTML pages in case of an error.
192 reraise = (settings.get('debugtoolbar.enabled', False) or
190 reraise = (settings.get('debugtoolbar.enabled', False) or
193 rhodecode.disable_error_handler)
191 rhodecode.disable_error_handler)
194 pylons_app = PylonsErrorHandlingMiddleware(
192 pylons_app = PylonsErrorHandlingMiddleware(
195 pylons_app, error_handler, reraise)
193 pylons_app, error_handler, reraise)
196
194
197 # The VCSMiddleware shall operate like a fallback if pyramid doesn't find a
195 # The VCSMiddleware shall operate like a fallback if pyramid doesn't find a
198 # view to handle the request. Therefore it is wrapped around the pylons
196 # view to handle the request. Therefore it is wrapped around the pylons
199 # app. It has to be outside of the error handling otherwise error responses
197 # app. It has to be outside of the error handling otherwise error responses
200 # from the vcsserver are converted to HTML error pages. This confuses the
198 # from the vcsserver are converted to HTML error pages. This confuses the
201 # command line tools and the user won't get a meaningful error message.
199 # command line tools and the user won't get a meaningful error message.
202 if vcs_server_enabled:
200 if vcs_server_enabled:
203 pylons_app = VCSMiddleware(
201 pylons_app = VCSMiddleware(
204 pylons_app, settings, appenlight_client, registry=config.registry)
202 pylons_app, settings, appenlight_client, registry=config.registry)
205
203
206 # Convert WSGI app to pyramid view and return it.
204 # Convert WSGI app to pyramid view and return it.
207 return wsgiapp(pylons_app)
205 return wsgiapp(pylons_app)
208
206
209
207
210 def add_pylons_compat_data(registry, global_config, settings):
208 def add_pylons_compat_data(registry, global_config, settings):
211 """
209 """
212 Attach data to the registry to support the Pylons integration.
210 Attach data to the registry to support the Pylons integration.
213 """
211 """
214 registry._pylons_compat_global_config = global_config
212 registry._pylons_compat_global_config = global_config
215 registry._pylons_compat_settings = settings
213 registry._pylons_compat_settings = settings
216
214
217
215
218 def error_handler(exception, request):
216 def error_handler(exception, request):
219 import rhodecode
217 import rhodecode
220 from rhodecode.lib import helpers
218 from rhodecode.lib import helpers
221
219
222 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
220 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
223
221
224 base_response = HTTPInternalServerError()
222 base_response = HTTPInternalServerError()
225 # prefer original exception for the response since it may have headers set
223 # prefer original exception for the response since it may have headers set
226 if isinstance(exception, HTTPException):
224 if isinstance(exception, HTTPException):
227 base_response = exception
225 base_response = exception
228 elif isinstance(exception, VCSCommunicationError):
226 elif isinstance(exception, VCSCommunicationError):
229 base_response = VCSServerUnavailable()
227 base_response = VCSServerUnavailable()
230
228
231 def is_http_error(response):
229 def is_http_error(response):
232 # error which should have traceback
230 # error which should have traceback
233 return response.status_code > 499
231 return response.status_code > 499
234
232
235 if is_http_error(base_response):
233 if is_http_error(base_response):
236 log.exception(
234 log.exception(
237 'error occurred handling this request for path: %s', request.path)
235 'error occurred handling this request for path: %s', request.path)
238
236
239 c = AttributeDict()
237 c = AttributeDict()
240 c.error_message = base_response.status
238 c.error_message = base_response.status
241 c.error_explanation = base_response.explanation or str(base_response)
239 c.error_explanation = base_response.explanation or str(base_response)
242 c.visual = AttributeDict()
240 c.visual = AttributeDict()
243
241
244 c.visual.rhodecode_support_url = (
242 c.visual.rhodecode_support_url = (
245 request.registry.settings.get('rhodecode_support_url') or
243 request.registry.settings.get('rhodecode_support_url') or
246 request.route_url('rhodecode_support')
244 request.route_url('rhodecode_support')
247 )
245 )
248 c.redirect_time = 0
246 c.redirect_time = 0
249 c.rhodecode_name = rhodecode_title
247 c.rhodecode_name = rhodecode_title
250 if not c.rhodecode_name:
248 if not c.rhodecode_name:
251 c.rhodecode_name = 'Rhodecode'
249 c.rhodecode_name = 'Rhodecode'
252
250
253 c.causes = []
251 c.causes = []
254 if hasattr(base_response, 'causes'):
252 if hasattr(base_response, 'causes'):
255 c.causes = base_response.causes
253 c.causes = base_response.causes
256 c.messages = helpers.flash.pop_messages(request=request)
254 c.messages = helpers.flash.pop_messages(request=request)
257
255 c.traceback = traceback.format_exc()
258 response = render_to_response(
256 response = render_to_response(
259 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
257 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
260 response=base_response)
258 response=base_response)
261
259
262 return response
260 return response
263
261
264
262
265 def includeme(config):
263 def includeme(config):
266 settings = config.registry.settings
264 settings = config.registry.settings
267
265
268 # plugin information
266 # plugin information
269 config.registry.rhodecode_plugins = OrderedDict()
267 config.registry.rhodecode_plugins = OrderedDict()
270
268
271 config.add_directive(
269 config.add_directive(
272 'register_rhodecode_plugin', register_rhodecode_plugin)
270 'register_rhodecode_plugin', register_rhodecode_plugin)
273
271
274 if asbool(settings.get('appenlight', 'false')):
272 if asbool(settings.get('appenlight', 'false')):
275 config.include('appenlight_client.ext.pyramid_tween')
273 config.include('appenlight_client.ext.pyramid_tween')
276
274
277 # Includes which are required. The application would fail without them.
275 # Includes which are required. The application would fail without them.
278 config.include('pyramid_mako')
276 config.include('pyramid_mako')
279 config.include('pyramid_beaker')
277 config.include('pyramid_beaker')
280
278
281 config.include('rhodecode.authentication')
279 config.include('rhodecode.authentication')
282 config.include('rhodecode.integrations')
280 config.include('rhodecode.integrations')
283
281
284 # apps
282 # apps
285 config.include('rhodecode.apps._base')
283 config.include('rhodecode.apps._base')
286 config.include('rhodecode.apps.ops')
284 config.include('rhodecode.apps.ops')
287
285
288 config.include('rhodecode.apps.admin')
286 config.include('rhodecode.apps.admin')
289 config.include('rhodecode.apps.channelstream')
287 config.include('rhodecode.apps.channelstream')
290 config.include('rhodecode.apps.login')
288 config.include('rhodecode.apps.login')
291 config.include('rhodecode.apps.home')
289 config.include('rhodecode.apps.home')
292 config.include('rhodecode.apps.repository')
290 config.include('rhodecode.apps.repository')
293 config.include('rhodecode.apps.repo_group')
291 config.include('rhodecode.apps.repo_group')
294 config.include('rhodecode.apps.search')
292 config.include('rhodecode.apps.search')
295 config.include('rhodecode.apps.user_profile')
293 config.include('rhodecode.apps.user_profile')
296 config.include('rhodecode.apps.my_account')
294 config.include('rhodecode.apps.my_account')
297 config.include('rhodecode.apps.svn_support')
295 config.include('rhodecode.apps.svn_support')
298 config.include('rhodecode.apps.gist')
296 config.include('rhodecode.apps.gist')
299
297
300 config.include('rhodecode.apps.debug_style')
298 config.include('rhodecode.apps.debug_style')
301 config.include('rhodecode.tweens')
299 config.include('rhodecode.tweens')
302 config.include('rhodecode.api')
300 config.include('rhodecode.api')
303
301
304 config.add_route(
302 config.add_route(
305 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
303 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
306
304
307 config.add_translation_dirs('rhodecode:i18n/')
305 config.add_translation_dirs('rhodecode:i18n/')
308 settings['default_locale_name'] = settings.get('lang', 'en')
306 settings['default_locale_name'] = settings.get('lang', 'en')
309
307
310 # Add subscribers.
308 # Add subscribers.
311 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
309 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
312 config.add_subscriber(write_metadata_if_needed, ApplicationCreated)
310 config.add_subscriber(write_metadata_if_needed, ApplicationCreated)
313 config.add_subscriber(write_js_routes_if_enabled, ApplicationCreated)
311 config.add_subscriber(write_js_routes_if_enabled, ApplicationCreated)
314
312
315 config.add_request_method(
313 config.add_request_method(
316 'rhodecode.lib.partial_renderer.get_partial_renderer',
314 'rhodecode.lib.partial_renderer.get_partial_renderer',
317 'get_partial_renderer')
315 'get_partial_renderer')
318
316
319 # events
317 # events
320 # TODO(marcink): this should be done when pyramid migration is finished
318 # TODO(marcink): this should be done when pyramid migration is finished
321 # config.add_subscriber(
319 # config.add_subscriber(
322 # 'rhodecode.integrations.integrations_event_handler',
320 # 'rhodecode.integrations.integrations_event_handler',
323 # 'rhodecode.events.RhodecodeEvent')
321 # 'rhodecode.events.RhodecodeEvent')
324
322
325 # Set the authorization policy.
323 # Set the authorization policy.
326 authz_policy = ACLAuthorizationPolicy()
324 authz_policy = ACLAuthorizationPolicy()
327 config.set_authorization_policy(authz_policy)
325 config.set_authorization_policy(authz_policy)
328
326
329 # Set the default renderer for HTML templates to mako.
327 # Set the default renderer for HTML templates to mako.
330 config.add_mako_renderer('.html')
328 config.add_mako_renderer('.html')
331
329
332 config.add_renderer(
330 config.add_renderer(
333 name='json_ext',
331 name='json_ext',
334 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
332 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
335
333
336 # include RhodeCode plugins
334 # include RhodeCode plugins
337 includes = aslist(settings.get('rhodecode.includes', []))
335 includes = aslist(settings.get('rhodecode.includes', []))
338 for inc in includes:
336 for inc in includes:
339 config.include(inc)
337 config.include(inc)
340
338
341 # This is the glue which allows us to migrate in chunks. By registering the
339 # This is the glue which allows us to migrate in chunks. By registering the
342 # pylons based application as the "Not Found" view in Pyramid, we will
340 # pylons based application as the "Not Found" view in Pyramid, we will
343 # fallback to the old application each time the new one does not yet know
341 # fallback to the old application each time the new one does not yet know
344 # how to handle a request.
342 # how to handle a request.
345 config.add_notfound_view(make_not_found_view(config))
343 config.add_notfound_view(make_not_found_view(config))
346
344
347 if not settings.get('debugtoolbar.enabled', False):
345 if not settings.get('debugtoolbar.enabled', False):
348 # disabled debugtoolbar handle all exceptions via the error_handlers
346 # disabled debugtoolbar handle all exceptions via the error_handlers
349 config.add_view(error_handler, context=Exception)
347 config.add_view(error_handler, context=Exception)
350
348
351 config.add_view(error_handler, context=HTTPError)
349 config.add_view(error_handler, context=HTTPError)
352
350
353
351
354 def includeme_first(config):
352 def includeme_first(config):
355 # redirect automatic browser favicon.ico requests to correct place
353 # redirect automatic browser favicon.ico requests to correct place
356 def favicon_redirect(context, request):
354 def favicon_redirect(context, request):
357 return HTTPFound(
355 return HTTPFound(
358 request.static_path('rhodecode:public/images/favicon.ico'))
356 request.static_path('rhodecode:public/images/favicon.ico'))
359
357
360 config.add_view(favicon_redirect, route_name='favicon')
358 config.add_view(favicon_redirect, route_name='favicon')
361 config.add_route('favicon', '/favicon.ico')
359 config.add_route('favicon', '/favicon.ico')
362
360
363 def robots_redirect(context, request):
361 def robots_redirect(context, request):
364 return HTTPFound(
362 return HTTPFound(
365 request.static_path('rhodecode:public/robots.txt'))
363 request.static_path('rhodecode:public/robots.txt'))
366
364
367 config.add_view(robots_redirect, route_name='robots')
365 config.add_view(robots_redirect, route_name='robots')
368 config.add_route('robots', '/robots.txt')
366 config.add_route('robots', '/robots.txt')
369
367
370 config.add_static_view(
368 config.add_static_view(
371 '_static/deform', 'deform:static')
369 '_static/deform', 'deform:static')
372 config.add_static_view(
370 config.add_static_view(
373 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
371 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
374
372
375
373
376 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
374 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
377 """
375 """
378 Apply outer WSGI middlewares around the application.
376 Apply outer WSGI middlewares around the application.
379
377
380 Part of this has been moved up from the Pylons layer, so that the
378 Part of this has been moved up from the Pylons layer, so that the
381 data is also available if old Pylons code is hit through an already ported
379 data is also available if old Pylons code is hit through an already ported
382 view.
380 view.
383 """
381 """
384 settings = config.registry.settings
382 settings = config.registry.settings
385
383
386 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
384 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
387 pyramid_app = HttpsFixup(pyramid_app, settings)
385 pyramid_app = HttpsFixup(pyramid_app, settings)
388
386
389 # Add RoutesMiddleware to support the pylons compatibility tween during
387 # Add RoutesMiddleware to support the pylons compatibility tween during
390 # migration to pyramid.
388 # migration to pyramid.
391
389
392 # TODO(marcink): remove after migration to pyramid
390 # TODO(marcink): remove after migration to pyramid
393 if hasattr(config.registry, '_pylons_compat_config'):
391 if hasattr(config.registry, '_pylons_compat_config'):
394 routes_map = config.registry._pylons_compat_config['routes.map']
392 routes_map = config.registry._pylons_compat_config['routes.map']
395 pyramid_app = SkippableRoutesMiddleware(
393 pyramid_app = SkippableRoutesMiddleware(
396 pyramid_app, routes_map,
394 pyramid_app, routes_map,
397 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
395 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
398
396
399 pyramid_app, _ = wrap_in_appenlight_if_enabled(pyramid_app, settings)
397 pyramid_app, _ = wrap_in_appenlight_if_enabled(pyramid_app, settings)
400
398
401 if settings['gzip_responses']:
399 if settings['gzip_responses']:
402 pyramid_app = make_gzip_middleware(
400 pyramid_app = make_gzip_middleware(
403 pyramid_app, settings, compress_level=1)
401 pyramid_app, settings, compress_level=1)
404
402
405 # this should be the outer most middleware in the wsgi stack since
403 # this should be the outer most middleware in the wsgi stack since
406 # middleware like Routes make database calls
404 # middleware like Routes make database calls
407 def pyramid_app_with_cleanup(environ, start_response):
405 def pyramid_app_with_cleanup(environ, start_response):
408 try:
406 try:
409 return pyramid_app(environ, start_response)
407 return pyramid_app(environ, start_response)
410 finally:
408 finally:
411 # Dispose current database session and rollback uncommitted
409 # Dispose current database session and rollback uncommitted
412 # transactions.
410 # transactions.
413 meta.Session.remove()
411 meta.Session.remove()
414
412
415 # In a single threaded mode server, on non sqlite db we should have
413 # In a single threaded mode server, on non sqlite db we should have
416 # '0 Current Checked out connections' at the end of a request,
414 # '0 Current Checked out connections' at the end of a request,
417 # if not, then something, somewhere is leaving a connection open
415 # if not, then something, somewhere is leaving a connection open
418 pool = meta.Base.metadata.bind.engine.pool
416 pool = meta.Base.metadata.bind.engine.pool
419 log.debug('sa pool status: %s', pool.status())
417 log.debug('sa pool status: %s', pool.status())
420
418
421 return pyramid_app_with_cleanup
419 return pyramid_app_with_cleanup
422
420
423
421
424 def sanitize_settings_and_apply_defaults(settings):
422 def sanitize_settings_and_apply_defaults(settings):
425 """
423 """
426 Applies settings defaults and does all type conversion.
424 Applies settings defaults and does all type conversion.
427
425
428 We would move all settings parsing and preparation into this place, so that
426 We would move all settings parsing and preparation into this place, so that
429 we have only one place left which deals with this part. The remaining parts
427 we have only one place left which deals with this part. The remaining parts
430 of the application would start to rely fully on well prepared settings.
428 of the application would start to rely fully on well prepared settings.
431
429
432 This piece would later be split up per topic to avoid a big fat monster
430 This piece would later be split up per topic to avoid a big fat monster
433 function.
431 function.
434 """
432 """
435
433
436 # Pyramid's mako renderer has to search in the templates folder so that the
434 # Pyramid's mako renderer has to search in the templates folder so that the
437 # old templates still work. Ported and new templates are expected to use
435 # old templates still work. Ported and new templates are expected to use
438 # real asset specifications for the includes.
436 # real asset specifications for the includes.
439 mako_directories = settings.setdefault('mako.directories', [
437 mako_directories = settings.setdefault('mako.directories', [
440 # Base templates of the original Pylons application
438 # Base templates of the original Pylons application
441 'rhodecode:templates',
439 'rhodecode:templates',
442 ])
440 ])
443 log.debug(
441 log.debug(
444 "Using the following Mako template directories: %s",
442 "Using the following Mako template directories: %s",
445 mako_directories)
443 mako_directories)
446
444
447 # Default includes, possible to change as a user
445 # Default includes, possible to change as a user
448 pyramid_includes = settings.setdefault('pyramid.includes', [
446 pyramid_includes = settings.setdefault('pyramid.includes', [
449 'rhodecode.lib.middleware.request_wrapper',
447 'rhodecode.lib.middleware.request_wrapper',
450 ])
448 ])
451 log.debug(
449 log.debug(
452 "Using the following pyramid.includes: %s",
450 "Using the following pyramid.includes: %s",
453 pyramid_includes)
451 pyramid_includes)
454
452
455 # TODO: johbo: Re-think this, usually the call to config.include
453 # TODO: johbo: Re-think this, usually the call to config.include
456 # should allow to pass in a prefix.
454 # should allow to pass in a prefix.
457 settings.setdefault('rhodecode.api.url', '/_admin/api')
455 settings.setdefault('rhodecode.api.url', '/_admin/api')
458
456
459 # Sanitize generic settings.
457 # Sanitize generic settings.
460 _list_setting(settings, 'default_encoding', 'UTF-8')
458 _list_setting(settings, 'default_encoding', 'UTF-8')
461 _bool_setting(settings, 'is_test', 'false')
459 _bool_setting(settings, 'is_test', 'false')
462 _bool_setting(settings, 'gzip_responses', 'false')
460 _bool_setting(settings, 'gzip_responses', 'false')
463
461
464 # Call split out functions that sanitize settings for each topic.
462 # Call split out functions that sanitize settings for each topic.
465 _sanitize_appenlight_settings(settings)
463 _sanitize_appenlight_settings(settings)
466 _sanitize_vcs_settings(settings)
464 _sanitize_vcs_settings(settings)
467
465
468 return settings
466 return settings
469
467
470
468
471 def _sanitize_appenlight_settings(settings):
469 def _sanitize_appenlight_settings(settings):
472 _bool_setting(settings, 'appenlight', 'false')
470 _bool_setting(settings, 'appenlight', 'false')
473
471
474
472
475 def _sanitize_vcs_settings(settings):
473 def _sanitize_vcs_settings(settings):
476 """
474 """
477 Applies settings defaults and does type conversion for all VCS related
475 Applies settings defaults and does type conversion for all VCS related
478 settings.
476 settings.
479 """
477 """
480 _string_setting(settings, 'vcs.svn.compatible_version', '')
478 _string_setting(settings, 'vcs.svn.compatible_version', '')
481 _string_setting(settings, 'git_rev_filter', '--all')
479 _string_setting(settings, 'git_rev_filter', '--all')
482 _string_setting(settings, 'vcs.hooks.protocol', 'http')
480 _string_setting(settings, 'vcs.hooks.protocol', 'http')
483 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
481 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
484 _string_setting(settings, 'vcs.server', '')
482 _string_setting(settings, 'vcs.server', '')
485 _string_setting(settings, 'vcs.server.log_level', 'debug')
483 _string_setting(settings, 'vcs.server.log_level', 'debug')
486 _string_setting(settings, 'vcs.server.protocol', 'http')
484 _string_setting(settings, 'vcs.server.protocol', 'http')
487 _bool_setting(settings, 'startup.import_repos', 'false')
485 _bool_setting(settings, 'startup.import_repos', 'false')
488 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
486 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
489 _bool_setting(settings, 'vcs.server.enable', 'true')
487 _bool_setting(settings, 'vcs.server.enable', 'true')
490 _bool_setting(settings, 'vcs.start_server', 'false')
488 _bool_setting(settings, 'vcs.start_server', 'false')
491 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
489 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
492 _int_setting(settings, 'vcs.connection_timeout', 3600)
490 _int_setting(settings, 'vcs.connection_timeout', 3600)
493
491
494 # Support legacy values of vcs.scm_app_implementation. Legacy
492 # Support legacy values of vcs.scm_app_implementation. Legacy
495 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http'
493 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http'
496 # which is now mapped to 'http'.
494 # which is now mapped to 'http'.
497 scm_app_impl = settings['vcs.scm_app_implementation']
495 scm_app_impl = settings['vcs.scm_app_implementation']
498 if scm_app_impl == 'rhodecode.lib.middleware.utils.scm_app_http':
496 if scm_app_impl == 'rhodecode.lib.middleware.utils.scm_app_http':
499 settings['vcs.scm_app_implementation'] = 'http'
497 settings['vcs.scm_app_implementation'] = 'http'
500
498
501
499
502 def _int_setting(settings, name, default):
500 def _int_setting(settings, name, default):
503 settings[name] = int(settings.get(name, default))
501 settings[name] = int(settings.get(name, default))
504
502
505
503
506 def _bool_setting(settings, name, default):
504 def _bool_setting(settings, name, default):
507 input = settings.get(name, default)
505 input = settings.get(name, default)
508 if isinstance(input, unicode):
506 if isinstance(input, unicode):
509 input = input.encode('utf8')
507 input = input.encode('utf8')
510 settings[name] = asbool(input)
508 settings[name] = asbool(input)
511
509
512
510
513 def _list_setting(settings, name, default):
511 def _list_setting(settings, name, default):
514 raw_value = settings.get(name, default)
512 raw_value = settings.get(name, default)
515
513
516 old_separator = ','
514 old_separator = ','
517 if old_separator in raw_value:
515 if old_separator in raw_value:
518 # If we get a comma separated list, pass it to our own function.
516 # If we get a comma separated list, pass it to our own function.
519 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
517 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
520 else:
518 else:
521 # Otherwise we assume it uses pyramids space/newline separation.
519 # Otherwise we assume it uses pyramids space/newline separation.
522 settings[name] = aslist(raw_value)
520 settings[name] = aslist(raw_value)
523
521
524
522
525 def _string_setting(settings, name, default, lower=True):
523 def _string_setting(settings, name, default, lower=True):
526 value = settings.get(name, default)
524 value = settings.get(name, default)
527 if lower:
525 if lower:
528 value = value.lower()
526 value = value.lower()
529 settings[name] = value
527 settings[name] = value
General Comments 0
You need to be logged in to leave comments. Login now