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