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