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