##// END OF EJS Templates
pyramid: add config to make_pyramid_app for inspection afterwards
dan -
r619:2097f436 default
parent child Browse files
Show More
@@ -1,470 +1,471 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 HTTPError, HTTPInternalServerError
34 from pyramid.httpexceptions import HTTPError, HTTPInternalServerError
35 from pylons.controllers.util import redirect
35 from pylons.controllers.util import redirect
36 from pyramid.events import ApplicationCreated
36 from pyramid.events import ApplicationCreated
37 import pyramid.httpexceptions as httpexceptions
37 import pyramid.httpexceptions as httpexceptions
38 from pyramid.renderers import render_to_response
38 from pyramid.renderers import render_to_response
39 from routes.middleware import RoutesMiddleware
39 from routes.middleware import RoutesMiddleware
40 import routes.util
40 import routes.util
41
41
42 import rhodecode
42 import rhodecode
43 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
44 from rhodecode.config import patches
44 from rhodecode.config import patches
45 from rhodecode.config.routing import STATIC_FILE_PREFIX
45 from rhodecode.config.routing import STATIC_FILE_PREFIX
46 from rhodecode.config.environment import (
46 from rhodecode.config.environment import (
47 load_environment, load_pyramid_environment)
47 load_environment, load_pyramid_environment)
48 from rhodecode.lib.middleware import csrf
48 from rhodecode.lib.middleware import csrf
49 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
49 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
50 from rhodecode.lib.middleware.disable_vcs import DisableVCSPagesWrapper
50 from rhodecode.lib.middleware.disable_vcs import DisableVCSPagesWrapper
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 return pyramid_app
163 return pyramid_app
163
164
164
165
165 def make_not_found_view(config):
166 def make_not_found_view(config):
166 """
167 """
167 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
168 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.
169 Additionally it is wrapped by some other middlewares.
170 Additionally it is wrapped by some other middlewares.
170 """
171 """
171 settings = config.registry.settings
172 settings = config.registry.settings
172 vcs_server_enabled = settings['vcs.server.enable']
173 vcs_server_enabled = settings['vcs.server.enable']
173
174
174 # Make pylons app from unprepared settings.
175 # Make pylons app from unprepared settings.
175 pylons_app = make_app(
176 pylons_app = make_app(
176 config.registry._pylons_compat_global_config,
177 config.registry._pylons_compat_global_config,
177 **config.registry._pylons_compat_settings)
178 **config.registry._pylons_compat_settings)
178 config.registry._pylons_compat_config = pylons_app.config
179 config.registry._pylons_compat_config = pylons_app.config
179
180
180 # Appenlight monitoring.
181 # Appenlight monitoring.
181 pylons_app, appenlight_client = wrap_in_appenlight_if_enabled(
182 pylons_app, appenlight_client = wrap_in_appenlight_if_enabled(
182 pylons_app, settings)
183 pylons_app, settings)
183
184
184 # The VCSMiddleware shall operate like a fallback if pyramid doesn't find
185 # The VCSMiddleware shall operate like a fallback if pyramid doesn't find
185 # a view to handle the request. Therefore we wrap it around the pylons app.
186 # a view to handle the request. Therefore we wrap it around the pylons app.
186 if vcs_server_enabled:
187 if vcs_server_enabled:
187 pylons_app = VCSMiddleware(
188 pylons_app = VCSMiddleware(
188 pylons_app, settings, appenlight_client, registry=config.registry)
189 pylons_app, settings, appenlight_client, registry=config.registry)
189
190
190 pylons_app_as_view = wsgiapp(pylons_app)
191 pylons_app_as_view = wsgiapp(pylons_app)
191
192
192 # Protect from VCS Server error related pages when server is not available
193 # Protect from VCS Server error related pages when server is not available
193 if not vcs_server_enabled:
194 if not vcs_server_enabled:
194 pylons_app_as_view = DisableVCSPagesWrapper(pylons_app_as_view)
195 pylons_app_as_view = DisableVCSPagesWrapper(pylons_app_as_view)
195
196
196 def pylons_app_with_error_handler(context, request):
197 def pylons_app_with_error_handler(context, request):
197 """
198 """
198 Handle exceptions from rc pylons app:
199 Handle exceptions from rc pylons app:
199
200
200 - old webob type exceptions get converted to pyramid exceptions
201 - old webob type exceptions get converted to pyramid exceptions
201 - pyramid exceptions are passed to the error handler view
202 - pyramid exceptions are passed to the error handler view
202 """
203 """
203 def is_vcs_response(response):
204 def is_vcs_response(response):
204 return 'X-RhodeCode-Backend' in response.headers
205 return 'X-RhodeCode-Backend' in response.headers
205
206
206 def is_http_error(response):
207 def is_http_error(response):
207 # webob type error responses
208 # webob type error responses
208 return (400 <= response.status_int <= 599)
209 return (400 <= response.status_int <= 599)
209
210
210 def is_error_handling_needed(response):
211 def is_error_handling_needed(response):
211 return is_http_error(response) and not is_vcs_response(response)
212 return is_http_error(response) and not is_vcs_response(response)
212
213
213 try:
214 try:
214 response = pylons_app_as_view(context, request)
215 response = pylons_app_as_view(context, request)
215 if is_error_handling_needed(response):
216 if is_error_handling_needed(response):
216 response = webob_to_pyramid_http_response(response)
217 response = webob_to_pyramid_http_response(response)
217 return error_handler(response, request)
218 return error_handler(response, request)
218 except HTTPError as e: # pyramid type exceptions
219 except HTTPError as e: # pyramid type exceptions
219 return error_handler(e, request)
220 return error_handler(e, request)
220 except Exception:
221 except Exception:
221 if settings.get('debugtoolbar.enabled', False):
222 if settings.get('debugtoolbar.enabled', False):
222 raise
223 raise
223 return error_handler(HTTPInternalServerError(), request)
224 return error_handler(HTTPInternalServerError(), request)
224 return response
225 return response
225
226
226 return pylons_app_with_error_handler
227 return pylons_app_with_error_handler
227
228
228
229
229 def add_pylons_compat_data(registry, global_config, settings):
230 def add_pylons_compat_data(registry, global_config, settings):
230 """
231 """
231 Attach data to the registry to support the Pylons integration.
232 Attach data to the registry to support the Pylons integration.
232 """
233 """
233 registry._pylons_compat_global_config = global_config
234 registry._pylons_compat_global_config = global_config
234 registry._pylons_compat_settings = settings
235 registry._pylons_compat_settings = settings
235
236
236
237
237 def webob_to_pyramid_http_response(webob_response):
238 def webob_to_pyramid_http_response(webob_response):
238 ResponseClass = httpexceptions.status_map[webob_response.status_int]
239 ResponseClass = httpexceptions.status_map[webob_response.status_int]
239 pyramid_response = ResponseClass(webob_response.status)
240 pyramid_response = ResponseClass(webob_response.status)
240 pyramid_response.status = webob_response.status
241 pyramid_response.status = webob_response.status
241 pyramid_response.headers.update(webob_response.headers)
242 pyramid_response.headers.update(webob_response.headers)
242 if pyramid_response.headers['content-type'] == 'text/html':
243 if pyramid_response.headers['content-type'] == 'text/html':
243 pyramid_response.headers['content-type'] = 'text/html; charset=UTF-8'
244 pyramid_response.headers['content-type'] = 'text/html; charset=UTF-8'
244 return pyramid_response
245 return pyramid_response
245
246
246
247
247 def error_handler(exception, request):
248 def error_handler(exception, request):
248 # TODO: dan: replace the old pylons error controller with this
249 # TODO: dan: replace the old pylons error controller with this
249 from rhodecode.model.settings import SettingsModel
250 from rhodecode.model.settings import SettingsModel
250 from rhodecode.lib.utils2 import AttributeDict
251 from rhodecode.lib.utils2 import AttributeDict
251
252
252 try:
253 try:
253 rc_config = SettingsModel().get_all_settings()
254 rc_config = SettingsModel().get_all_settings()
254 except Exception:
255 except Exception:
255 log.exception('failed to fetch settings')
256 log.exception('failed to fetch settings')
256 rc_config = {}
257 rc_config = {}
257
258
258 base_response = HTTPInternalServerError()
259 base_response = HTTPInternalServerError()
259 # prefer original exception for the response since it may have headers set
260 # prefer original exception for the response since it may have headers set
260 if isinstance(exception, HTTPError):
261 if isinstance(exception, HTTPError):
261 base_response = exception
262 base_response = exception
262
263
263 c = AttributeDict()
264 c = AttributeDict()
264 c.error_message = base_response.status
265 c.error_message = base_response.status
265 c.error_explanation = base_response.explanation or str(base_response)
266 c.error_explanation = base_response.explanation or str(base_response)
266 c.visual = AttributeDict()
267 c.visual = AttributeDict()
267
268
268 c.visual.rhodecode_support_url = (
269 c.visual.rhodecode_support_url = (
269 request.registry.settings.get('rhodecode_support_url') or
270 request.registry.settings.get('rhodecode_support_url') or
270 request.route_url('rhodecode_support')
271 request.route_url('rhodecode_support')
271 )
272 )
272 c.redirect_time = 0
273 c.redirect_time = 0
273 c.rhodecode_name = rc_config.get('rhodecode_title', '')
274 c.rhodecode_name = rc_config.get('rhodecode_title', '')
274 if not c.rhodecode_name:
275 if not c.rhodecode_name:
275 c.rhodecode_name = 'Rhodecode'
276 c.rhodecode_name = 'Rhodecode'
276
277
277 response = render_to_response(
278 response = render_to_response(
278 '/errors/error_document.html', {'c': c}, request=request,
279 '/errors/error_document.html', {'c': c}, request=request,
279 response=base_response)
280 response=base_response)
280
281
281 return response
282 return response
282
283
283
284
284 def includeme(config):
285 def includeme(config):
285 settings = config.registry.settings
286 settings = config.registry.settings
286
287
287 # plugin information
288 # plugin information
288 config.registry.rhodecode_plugins = OrderedDict()
289 config.registry.rhodecode_plugins = OrderedDict()
289
290
290 config.add_directive(
291 config.add_directive(
291 'register_rhodecode_plugin', register_rhodecode_plugin)
292 'register_rhodecode_plugin', register_rhodecode_plugin)
292
293
293 if asbool(settings.get('appenlight', 'false')):
294 if asbool(settings.get('appenlight', 'false')):
294 config.include('appenlight_client.ext.pyramid_tween')
295 config.include('appenlight_client.ext.pyramid_tween')
295
296
296 # Includes which are required. The application would fail without them.
297 # Includes which are required. The application would fail without them.
297 config.include('pyramid_mako')
298 config.include('pyramid_mako')
298 config.include('pyramid_beaker')
299 config.include('pyramid_beaker')
299 config.include('rhodecode.channelstream')
300 config.include('rhodecode.channelstream')
300 config.include('rhodecode.admin')
301 config.include('rhodecode.admin')
301 config.include('rhodecode.authentication')
302 config.include('rhodecode.authentication')
302 config.include('rhodecode.integrations')
303 config.include('rhodecode.integrations')
303 config.include('rhodecode.login')
304 config.include('rhodecode.login')
304 config.include('rhodecode.tweens')
305 config.include('rhodecode.tweens')
305 config.include('rhodecode.api')
306 config.include('rhodecode.api')
306 config.include('rhodecode.svn_support')
307 config.include('rhodecode.svn_support')
307 config.add_route(
308 config.add_route(
308 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
309 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
309
310
310 # Add subscribers.
311 # Add subscribers.
311 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
312 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
312
313
313 # Set the authorization policy.
314 # Set the authorization policy.
314 authz_policy = ACLAuthorizationPolicy()
315 authz_policy = ACLAuthorizationPolicy()
315 config.set_authorization_policy(authz_policy)
316 config.set_authorization_policy(authz_policy)
316
317
317 # Set the default renderer for HTML templates to mako.
318 # Set the default renderer for HTML templates to mako.
318 config.add_mako_renderer('.html')
319 config.add_mako_renderer('.html')
319
320
320 # include RhodeCode plugins
321 # include RhodeCode plugins
321 includes = aslist(settings.get('rhodecode.includes', []))
322 includes = aslist(settings.get('rhodecode.includes', []))
322 for inc in includes:
323 for inc in includes:
323 config.include(inc)
324 config.include(inc)
324
325
325 # This is the glue which allows us to migrate in chunks. By registering the
326 # This is the glue which allows us to migrate in chunks. By registering the
326 # pylons based application as the "Not Found" view in Pyramid, we will
327 # pylons based application as the "Not Found" view in Pyramid, we will
327 # fallback to the old application each time the new one does not yet know
328 # fallback to the old application each time the new one does not yet know
328 # how to handle a request.
329 # how to handle a request.
329 config.add_notfound_view(make_not_found_view(config))
330 config.add_notfound_view(make_not_found_view(config))
330
331
331 if not settings.get('debugtoolbar.enabled', False):
332 if not settings.get('debugtoolbar.enabled', False):
332 # if no toolbar, then any exception gets caught and rendered
333 # if no toolbar, then any exception gets caught and rendered
333 config.add_view(error_handler, context=Exception)
334 config.add_view(error_handler, context=Exception)
334
335
335 config.add_view(error_handler, context=HTTPError)
336 config.add_view(error_handler, context=HTTPError)
336
337
337
338
338 def includeme_first(config):
339 def includeme_first(config):
339 # redirect automatic browser favicon.ico requests to correct place
340 # redirect automatic browser favicon.ico requests to correct place
340 def favicon_redirect(context, request):
341 def favicon_redirect(context, request):
341 return redirect(
342 return redirect(
342 request.static_path('rhodecode:public/images/favicon.ico'))
343 request.static_path('rhodecode:public/images/favicon.ico'))
343
344
344 config.add_view(favicon_redirect, route_name='favicon')
345 config.add_view(favicon_redirect, route_name='favicon')
345 config.add_route('favicon', '/favicon.ico')
346 config.add_route('favicon', '/favicon.ico')
346
347
347 config.add_static_view(
348 config.add_static_view(
348 '_static/deform', 'deform:static')
349 '_static/deform', 'deform:static')
349 config.add_static_view(
350 config.add_static_view(
350 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
351 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
351
352
352
353
353 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
354 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
354 """
355 """
355 Apply outer WSGI middlewares around the application.
356 Apply outer WSGI middlewares around the application.
356
357
357 Part of this has been moved up from the Pylons layer, so that the
358 Part of this has been moved up from the Pylons layer, so that the
358 data is also available if old Pylons code is hit through an already ported
359 data is also available if old Pylons code is hit through an already ported
359 view.
360 view.
360 """
361 """
361 settings = config.registry.settings
362 settings = config.registry.settings
362
363
363 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
364 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
364 pyramid_app = HttpsFixup(pyramid_app, settings)
365 pyramid_app = HttpsFixup(pyramid_app, settings)
365
366
366 # Add RoutesMiddleware to support the pylons compatibility tween during
367 # Add RoutesMiddleware to support the pylons compatibility tween during
367 # migration to pyramid.
368 # migration to pyramid.
368 pyramid_app = SkippableRoutesMiddleware(
369 pyramid_app = SkippableRoutesMiddleware(
369 pyramid_app, config.registry._pylons_compat_config['routes.map'],
370 pyramid_app, config.registry._pylons_compat_config['routes.map'],
370 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
371 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
371
372
372 pyramid_app, _ = wrap_in_appenlight_if_enabled(pyramid_app, settings)
373 pyramid_app, _ = wrap_in_appenlight_if_enabled(pyramid_app, settings)
373
374
374 if settings['gzip_responses']:
375 if settings['gzip_responses']:
375 pyramid_app = make_gzip_middleware(
376 pyramid_app = make_gzip_middleware(
376 pyramid_app, settings, compress_level=1)
377 pyramid_app, settings, compress_level=1)
377
378
378 return pyramid_app
379 return pyramid_app
379
380
380
381
381 def sanitize_settings_and_apply_defaults(settings):
382 def sanitize_settings_and_apply_defaults(settings):
382 """
383 """
383 Applies settings defaults and does all type conversion.
384 Applies settings defaults and does all type conversion.
384
385
385 We would move all settings parsing and preparation into this place, so that
386 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
387 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.
388 of the application would start to rely fully on well prepared settings.
388
389
389 This piece would later be split up per topic to avoid a big fat monster
390 This piece would later be split up per topic to avoid a big fat monster
390 function.
391 function.
391 """
392 """
392
393
393 # Pyramid's mako renderer has to search in the templates folder so that the
394 # 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
395 # old templates still work. Ported and new templates are expected to use
395 # real asset specifications for the includes.
396 # real asset specifications for the includes.
396 mako_directories = settings.setdefault('mako.directories', [
397 mako_directories = settings.setdefault('mako.directories', [
397 # Base templates of the original Pylons application
398 # Base templates of the original Pylons application
398 'rhodecode:templates',
399 'rhodecode:templates',
399 ])
400 ])
400 log.debug(
401 log.debug(
401 "Using the following Mako template directories: %s",
402 "Using the following Mako template directories: %s",
402 mako_directories)
403 mako_directories)
403
404
404 # Default includes, possible to change as a user
405 # Default includes, possible to change as a user
405 pyramid_includes = settings.setdefault('pyramid.includes', [
406 pyramid_includes = settings.setdefault('pyramid.includes', [
406 'rhodecode.lib.middleware.request_wrapper',
407 'rhodecode.lib.middleware.request_wrapper',
407 ])
408 ])
408 log.debug(
409 log.debug(
409 "Using the following pyramid.includes: %s",
410 "Using the following pyramid.includes: %s",
410 pyramid_includes)
411 pyramid_includes)
411
412
412 # TODO: johbo: Re-think this, usually the call to config.include
413 # TODO: johbo: Re-think this, usually the call to config.include
413 # should allow to pass in a prefix.
414 # should allow to pass in a prefix.
414 settings.setdefault('rhodecode.api.url', '/_admin/api')
415 settings.setdefault('rhodecode.api.url', '/_admin/api')
415
416
416 # Sanitize generic settings.
417 # Sanitize generic settings.
417 _list_setting(settings, 'default_encoding', 'UTF-8')
418 _list_setting(settings, 'default_encoding', 'UTF-8')
418 _bool_setting(settings, 'is_test', 'false')
419 _bool_setting(settings, 'is_test', 'false')
419 _bool_setting(settings, 'gzip_responses', 'false')
420 _bool_setting(settings, 'gzip_responses', 'false')
420
421
421 # Call split out functions that sanitize settings for each topic.
422 # Call split out functions that sanitize settings for each topic.
422 _sanitize_appenlight_settings(settings)
423 _sanitize_appenlight_settings(settings)
423 _sanitize_vcs_settings(settings)
424 _sanitize_vcs_settings(settings)
424
425
425 return settings
426 return settings
426
427
427
428
428 def _sanitize_appenlight_settings(settings):
429 def _sanitize_appenlight_settings(settings):
429 _bool_setting(settings, 'appenlight', 'false')
430 _bool_setting(settings, 'appenlight', 'false')
430
431
431
432
432 def _sanitize_vcs_settings(settings):
433 def _sanitize_vcs_settings(settings):
433 """
434 """
434 Applies settings defaults and does type conversion for all VCS related
435 Applies settings defaults and does type conversion for all VCS related
435 settings.
436 settings.
436 """
437 """
437 _string_setting(settings, 'vcs.svn.compatible_version', '')
438 _string_setting(settings, 'vcs.svn.compatible_version', '')
438 _string_setting(settings, 'git_rev_filter', '--all')
439 _string_setting(settings, 'git_rev_filter', '--all')
439 _string_setting(settings, 'vcs.hooks.protocol', 'pyro4')
440 _string_setting(settings, 'vcs.hooks.protocol', 'pyro4')
440 _string_setting(settings, 'vcs.server', '')
441 _string_setting(settings, 'vcs.server', '')
441 _string_setting(settings, 'vcs.server.log_level', 'debug')
442 _string_setting(settings, 'vcs.server.log_level', 'debug')
442 _string_setting(settings, 'vcs.server.protocol', 'pyro4')
443 _string_setting(settings, 'vcs.server.protocol', 'pyro4')
443 _bool_setting(settings, 'startup.import_repos', 'false')
444 _bool_setting(settings, 'startup.import_repos', 'false')
444 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
445 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
445 _bool_setting(settings, 'vcs.server.enable', 'true')
446 _bool_setting(settings, 'vcs.server.enable', 'true')
446 _bool_setting(settings, 'vcs.start_server', 'false')
447 _bool_setting(settings, 'vcs.start_server', 'false')
447 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
448 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
448
449
449
450
450 def _bool_setting(settings, name, default):
451 def _bool_setting(settings, name, default):
451 input = settings.get(name, default)
452 input = settings.get(name, default)
452 if isinstance(input, unicode):
453 if isinstance(input, unicode):
453 input = input.encode('utf8')
454 input = input.encode('utf8')
454 settings[name] = asbool(input)
455 settings[name] = asbool(input)
455
456
456
457
457 def _list_setting(settings, name, default):
458 def _list_setting(settings, name, default):
458 raw_value = settings.get(name, default)
459 raw_value = settings.get(name, default)
459
460
460 old_separator = ','
461 old_separator = ','
461 if old_separator in raw_value:
462 if old_separator in raw_value:
462 # If we get a comma separated list, pass it to our own function.
463 # If we get a comma separated list, pass it to our own function.
463 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
464 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
464 else:
465 else:
465 # Otherwise we assume it uses pyramids space/newline separation.
466 # Otherwise we assume it uses pyramids space/newline separation.
466 settings[name] = aslist(raw_value)
467 settings[name] = aslist(raw_value)
467
468
468
469
469 def _string_setting(settings, name, default):
470 def _string_setting(settings, name, default):
470 settings[name] = settings.get(name, default).lower()
471 settings[name] = settings.get(name, default).lower()
General Comments 0
You need to be logged in to leave comments. Login now