##// END OF EJS Templates
db: Remove comment line.
johbo -
r119:ab140f34 default
parent child Browse files
Show More
@@ -1,327 +1,326 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
25
26 from paste.registry import RegistryManager
26 from paste.registry import RegistryManager
27 from paste.gzipper import make_gzip_middleware
27 from paste.gzipper import make_gzip_middleware
28 from pylons.middleware import ErrorHandler, StatusCodeRedirect
28 from pylons.middleware import ErrorHandler, StatusCodeRedirect
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
32 from pyramid.static import static_view
33 from pyramid.settings import asbool, aslist
33 from pyramid.settings import asbool, aslist
34 from pyramid.wsgi import wsgiapp
34 from pyramid.wsgi import wsgiapp
35 from routes.middleware import RoutesMiddleware
35 from routes.middleware import RoutesMiddleware
36 import routes.util
36 import routes.util
37
37
38 import rhodecode
38 import rhodecode
39 from rhodecode.config import patches, utils
39 from rhodecode.config import patches, utils
40 from rhodecode.config.environment import load_environment
40 from rhodecode.config.environment import load_environment
41 from rhodecode.lib.middleware import csrf
41 from rhodecode.lib.middleware import csrf
42 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
42 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
43 from rhodecode.lib.middleware.disable_vcs import DisableVCSPagesWrapper
43 from rhodecode.lib.middleware.disable_vcs import DisableVCSPagesWrapper
44 from rhodecode.lib.middleware.https_fixup import HttpsFixup
44 from rhodecode.lib.middleware.https_fixup import HttpsFixup
45 from rhodecode.lib.middleware.vcs import VCSMiddleware
45 from rhodecode.lib.middleware.vcs import VCSMiddleware
46 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
46 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
47
47
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51
51
52 def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
52 def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
53 """Create a Pylons WSGI application and return it
53 """Create a Pylons WSGI application and return it
54
54
55 ``global_conf``
55 ``global_conf``
56 The inherited configuration for this application. Normally from
56 The inherited configuration for this application. Normally from
57 the [DEFAULT] section of the Paste ini file.
57 the [DEFAULT] section of the Paste ini file.
58
58
59 ``full_stack``
59 ``full_stack``
60 Whether or not this application provides a full WSGI stack (by
60 Whether or not this application provides a full WSGI stack (by
61 default, meaning it handles its own exceptions and errors).
61 default, meaning it handles its own exceptions and errors).
62 Disable full_stack when this application is "managed" by
62 Disable full_stack when this application is "managed" by
63 another WSGI middleware.
63 another WSGI middleware.
64
64
65 ``app_conf``
65 ``app_conf``
66 The application's local configuration. Normally specified in
66 The application's local configuration. Normally specified in
67 the [app:<name>] section of the Paste ini file (where <name>
67 the [app:<name>] section of the Paste ini file (where <name>
68 defaults to main).
68 defaults to main).
69
69
70 """
70 """
71 # Apply compatibility patches
71 # Apply compatibility patches
72 patches.kombu_1_5_1_python_2_7_11()
72 patches.kombu_1_5_1_python_2_7_11()
73 patches.inspect_getargspec()
73 patches.inspect_getargspec()
74
74
75 # Configure the Pylons environment
75 # Configure the Pylons environment
76 config = load_environment(global_conf, app_conf)
76 config = load_environment(global_conf, app_conf)
77
77
78 # The Pylons WSGI app
78 # The Pylons WSGI app
79 app = PylonsApp(config=config)
79 app = PylonsApp(config=config)
80 if rhodecode.is_test:
80 if rhodecode.is_test:
81 app = csrf.CSRFDetector(app)
81 app = csrf.CSRFDetector(app)
82
82
83 expected_origin = config.get('expected_origin')
83 expected_origin = config.get('expected_origin')
84 if expected_origin:
84 if expected_origin:
85 # The API can be accessed from other Origins.
85 # The API can be accessed from other Origins.
86 app = csrf.OriginChecker(app, expected_origin,
86 app = csrf.OriginChecker(app, expected_origin,
87 skip_urls=[routes.util.url_for('api')])
87 skip_urls=[routes.util.url_for('api')])
88
88
89 # Add RoutesMiddleware. Currently we have two instances in the stack. This
89 # Add RoutesMiddleware. Currently we have two instances in the stack. This
90 # is the lower one to make the StatusCodeRedirect middleware happy.
90 # is the lower one to make the StatusCodeRedirect middleware happy.
91 # TODO: johbo: This is not optimal, search for a better solution.
91 # TODO: johbo: This is not optimal, search for a better solution.
92 app = RoutesMiddleware(app, config['routes.map'])
92 app = RoutesMiddleware(app, config['routes.map'])
93
93
94 # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
94 # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
95 if asbool(config['pdebug']):
95 if asbool(config['pdebug']):
96 from rhodecode.lib.profiler import ProfilingMiddleware
96 from rhodecode.lib.profiler import ProfilingMiddleware
97 app = ProfilingMiddleware(app)
97 app = ProfilingMiddleware(app)
98
98
99 # Protect from VCS Server error related pages when server is not available
99 # Protect from VCS Server error related pages when server is not available
100 vcs_server_enabled = asbool(config.get('vcs.server.enable', 'true'))
100 vcs_server_enabled = asbool(config.get('vcs.server.enable', 'true'))
101 if not vcs_server_enabled:
101 if not vcs_server_enabled:
102 app = DisableVCSPagesWrapper(app)
102 app = DisableVCSPagesWrapper(app)
103
103
104 if asbool(full_stack):
104 if asbool(full_stack):
105
105
106 # Appenlight monitoring and error handler
106 # Appenlight monitoring and error handler
107 app, appenlight_client = wrap_in_appenlight_if_enabled(app, config)
107 app, appenlight_client = wrap_in_appenlight_if_enabled(app, config)
108
108
109 # Handle Python exceptions
109 # Handle Python exceptions
110 app = ErrorHandler(app, global_conf, **config['pylons.errorware'])
110 app = ErrorHandler(app, global_conf, **config['pylons.errorware'])
111
111
112 # we want our low level middleware to get to the request ASAP. We don't
112 # we want our low level middleware to get to the request ASAP. We don't
113 # need any pylons stack middleware in them
113 # need any pylons stack middleware in them
114 app = VCSMiddleware(app, config, appenlight_client)
114 app = VCSMiddleware(app, config, appenlight_client)
115 # Display error documents for 401, 403, 404 status codes (and
115 # Display error documents for 401, 403, 404 status codes (and
116 # 500 when debug is disabled)
116 # 500 when debug is disabled)
117 if asbool(config['debug']):
117 if asbool(config['debug']):
118 app = StatusCodeRedirect(app)
118 app = StatusCodeRedirect(app)
119 else:
119 else:
120 app = StatusCodeRedirect(app, [400, 401, 403, 404, 500])
120 app = StatusCodeRedirect(app, [400, 401, 403, 404, 500])
121
121
122 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
122 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
123 app = HttpsFixup(app, config)
123 app = HttpsFixup(app, config)
124
124
125 # Establish the Registry for this application
125 # Establish the Registry for this application
126 app = RegistryManager(app)
126 app = RegistryManager(app)
127
127
128 app.config = config
128 app.config = config
129
129
130 return app
130 return app
131
131
132
132
133 def make_pyramid_app(global_config, **settings):
133 def make_pyramid_app(global_config, **settings):
134 """
134 """
135 Constructs the WSGI application based on Pyramid and wraps the Pylons based
135 Constructs the WSGI application based on Pyramid and wraps the Pylons based
136 application.
136 application.
137
137
138 Specials:
138 Specials:
139
139
140 * We migrate from Pylons to Pyramid. While doing this, we keep both
140 * We migrate from Pylons to Pyramid. While doing this, we keep both
141 frameworks functional. This involves moving some WSGI middlewares around
141 frameworks functional. This involves moving some WSGI middlewares around
142 and providing access to some data internals, so that the old code is
142 and providing access to some data internals, so that the old code is
143 still functional.
143 still functional.
144
144
145 * The application can also be integrated like a plugin via the call to
145 * The application can also be integrated like a plugin via the call to
146 `includeme`. This is accompanied with the other utility functions which
146 `includeme`. This is accompanied with the other utility functions which
147 are called. Changing this should be done with great care to not break
147 are called. Changing this should be done with great care to not break
148 cases when these fragments are assembled from another place.
148 cases when these fragments are assembled from another place.
149
149
150 """
150 """
151 # The edition string should be available in pylons too, so we add it here
151 # The edition string should be available in pylons too, so we add it here
152 # before copying the settings.
152 # before copying the settings.
153 settings.setdefault('rhodecode.edition', 'Community Edition')
153 settings.setdefault('rhodecode.edition', 'Community Edition')
154
154
155 # As long as our Pylons application does expect "unprepared" settings, make
155 # As long as our Pylons application does expect "unprepared" settings, make
156 # sure that we keep an unmodified copy. This avoids unintentional change of
156 # sure that we keep an unmodified copy. This avoids unintentional change of
157 # behavior in the old application.
157 # behavior in the old application.
158 settings_pylons = settings.copy()
158 settings_pylons = settings.copy()
159
159
160 # Some parts of the code expect a merge of global and app settings.
160 # Some parts of the code expect a merge of global and app settings.
161 settings_merged = global_config.copy()
161 settings_merged = global_config.copy()
162 settings_merged.update(settings)
162 settings_merged.update(settings)
163
163
164 sanitize_settings_and_apply_defaults(settings)
164 sanitize_settings_and_apply_defaults(settings)
165 config = Configurator(settings=settings)
165 config = Configurator(settings=settings)
166 add_pylons_compat_data(config.registry, global_config, settings_pylons)
166 add_pylons_compat_data(config.registry, global_config, settings_pylons)
167
167
168 # If this is a test run we prepare the test environment like
168 # If this is a test run we prepare the test environment like
169 # creating a test database, test search index and test repositories.
169 # creating a test database, test search index and test repositories.
170 # This has to be done before the database connection is initialized.
170 # This has to be done before the database connection is initialized.
171 # if settings['is_test']:
172 if settings['is_test']:
171 if settings['is_test']:
173 rhodecode.is_test = True
172 rhodecode.is_test = True
174 utils.initialize_test_environment(settings_merged)
173 utils.initialize_test_environment(settings_merged)
175
174
176 # Initialize the database connection.
175 # Initialize the database connection.
177 utils.initialize_database(settings_merged)
176 utils.initialize_database(settings_merged)
178
177
179 includeme(config)
178 includeme(config)
180 includeme_last(config)
179 includeme_last(config)
181 pyramid_app = config.make_wsgi_app()
180 pyramid_app = config.make_wsgi_app()
182 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
181 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
183 return pyramid_app
182 return pyramid_app
184
183
185
184
186 def add_pylons_compat_data(registry, global_config, settings):
185 def add_pylons_compat_data(registry, global_config, settings):
187 """
186 """
188 Attach data to the registry to support the Pylons integration.
187 Attach data to the registry to support the Pylons integration.
189 """
188 """
190 registry._pylons_compat_global_config = global_config
189 registry._pylons_compat_global_config = global_config
191 registry._pylons_compat_settings = settings
190 registry._pylons_compat_settings = settings
192
191
193
192
194 def includeme(config):
193 def includeme(config):
195 settings = config.registry.settings
194 settings = config.registry.settings
196
195
197 # Includes which are required. The application would fail without them.
196 # Includes which are required. The application would fail without them.
198 config.include('pyramid_mako')
197 config.include('pyramid_mako')
199 config.include('pyramid_beaker')
198 config.include('pyramid_beaker')
200 config.include('rhodecode.authentication')
199 config.include('rhodecode.authentication')
201 config.include('rhodecode.login')
200 config.include('rhodecode.login')
202 config.include('rhodecode.tweens')
201 config.include('rhodecode.tweens')
203 config.include('rhodecode.api')
202 config.include('rhodecode.api')
204
203
205 # Set the authorization policy.
204 # Set the authorization policy.
206 authz_policy = ACLAuthorizationPolicy()
205 authz_policy = ACLAuthorizationPolicy()
207 config.set_authorization_policy(authz_policy)
206 config.set_authorization_policy(authz_policy)
208
207
209 # Set the default renderer for HTML templates to mako.
208 # Set the default renderer for HTML templates to mako.
210 config.add_mako_renderer('.html')
209 config.add_mako_renderer('.html')
211
210
212 # plugin information
211 # plugin information
213 config.registry.rhodecode_plugins = {}
212 config.registry.rhodecode_plugins = {}
214
213
215 config.add_directive(
214 config.add_directive(
216 'register_rhodecode_plugin', register_rhodecode_plugin)
215 'register_rhodecode_plugin', register_rhodecode_plugin)
217 # include RhodeCode plugins
216 # include RhodeCode plugins
218 includes = aslist(settings.get('rhodecode.includes', []))
217 includes = aslist(settings.get('rhodecode.includes', []))
219 for inc in includes:
218 for inc in includes:
220 config.include(inc)
219 config.include(inc)
221
220
222 # This is the glue which allows us to migrate in chunks. By registering the
221 # This is the glue which allows us to migrate in chunks. By registering the
223 # pylons based application as the "Not Found" view in Pyramid, we will
222 # pylons based application as the "Not Found" view in Pyramid, we will
224 # fallback to the old application each time the new one does not yet know
223 # fallback to the old application each time the new one does not yet know
225 # how to handle a request.
224 # how to handle a request.
226 pylons_app = make_app(
225 pylons_app = make_app(
227 config.registry._pylons_compat_global_config,
226 config.registry._pylons_compat_global_config,
228 **config.registry._pylons_compat_settings)
227 **config.registry._pylons_compat_settings)
229 config.registry._pylons_compat_config = pylons_app.config
228 config.registry._pylons_compat_config = pylons_app.config
230 pylons_app_as_view = wsgiapp(pylons_app)
229 pylons_app_as_view = wsgiapp(pylons_app)
231 config.add_notfound_view(pylons_app_as_view)
230 config.add_notfound_view(pylons_app_as_view)
232
231
233
232
234 def includeme_last(config):
233 def includeme_last(config):
235 """
234 """
236 The static file catchall needs to be last in the view configuration.
235 The static file catchall needs to be last in the view configuration.
237 """
236 """
238 settings = config.registry.settings
237 settings = config.registry.settings
239
238
240 # Note: johbo: I would prefer to register a prefix for static files at some
239 # Note: johbo: I would prefer to register a prefix for static files at some
241 # point, e.g. move them under '_static/'. This would fully avoid that we
240 # point, e.g. move them under '_static/'. This would fully avoid that we
242 # can have name clashes with a repository name. Imaging someone calling his
241 # can have name clashes with a repository name. Imaging someone calling his
243 # repo "css" ;-) Also having an external web server to serve out the static
242 # repo "css" ;-) Also having an external web server to serve out the static
244 # files seems to be easier to set up if they have a common prefix.
243 # files seems to be easier to set up if they have a common prefix.
245 #
244 #
246 # Example: config.add_static_view('_static', path='rhodecode:public')
245 # Example: config.add_static_view('_static', path='rhodecode:public')
247 #
246 #
248 # It might be an option to register both paths for a while and then migrate
247 # It might be an option to register both paths for a while and then migrate
249 # over to the new location.
248 # over to the new location.
250
249
251 # Serving static files with a catchall.
250 # Serving static files with a catchall.
252 if settings['static_files']:
251 if settings['static_files']:
253 config.add_route('catchall_static', '/*subpath')
252 config.add_route('catchall_static', '/*subpath')
254 config.add_view(
253 config.add_view(
255 static_view('rhodecode:public'), route_name='catchall_static')
254 static_view('rhodecode:public'), route_name='catchall_static')
256
255
257
256
258 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
257 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
259 """
258 """
260 Apply outer WSGI middlewares around the application.
259 Apply outer WSGI middlewares around the application.
261
260
262 Part of this has been moved up from the Pylons layer, so that the
261 Part of this has been moved up from the Pylons layer, so that the
263 data is also available if old Pylons code is hit through an already ported
262 data is also available if old Pylons code is hit through an already ported
264 view.
263 view.
265 """
264 """
266 settings = config.registry.settings
265 settings = config.registry.settings
267
266
268 # Add RoutesMiddleware. Currently we have two instances in the stack. This
267 # Add RoutesMiddleware. Currently we have two instances in the stack. This
269 # is the upper one to support the pylons compatibility tween during
268 # is the upper one to support the pylons compatibility tween during
270 # migration to pyramid.
269 # migration to pyramid.
271 pyramid_app = RoutesMiddleware(
270 pyramid_app = RoutesMiddleware(
272 pyramid_app, config.registry._pylons_compat_config['routes.map'])
271 pyramid_app, config.registry._pylons_compat_config['routes.map'])
273
272
274 # TODO: johbo: Don't really see why we enable the gzip middleware when
273 # TODO: johbo: Don't really see why we enable the gzip middleware when
275 # serving static files, might be something that should have its own setting
274 # serving static files, might be something that should have its own setting
276 # as well?
275 # as well?
277 if settings['static_files']:
276 if settings['static_files']:
278 pyramid_app = make_gzip_middleware(
277 pyramid_app = make_gzip_middleware(
279 pyramid_app, settings, compress_level=1)
278 pyramid_app, settings, compress_level=1)
280
279
281 return pyramid_app
280 return pyramid_app
282
281
283
282
284 def sanitize_settings_and_apply_defaults(settings):
283 def sanitize_settings_and_apply_defaults(settings):
285 """
284 """
286 Applies settings defaults and does all type conversion.
285 Applies settings defaults and does all type conversion.
287
286
288 We would move all settings parsing and preparation into this place, so that
287 We would move all settings parsing and preparation into this place, so that
289 we have only one place left which deals with this part. The remaining parts
288 we have only one place left which deals with this part. The remaining parts
290 of the application would start to rely fully on well prepared settings.
289 of the application would start to rely fully on well prepared settings.
291
290
292 This piece would later be split up per topic to avoid a big fat monster
291 This piece would later be split up per topic to avoid a big fat monster
293 function.
292 function.
294 """
293 """
295
294
296 # Pyramid's mako renderer has to search in the templates folder so that the
295 # Pyramid's mako renderer has to search in the templates folder so that the
297 # old templates still work. Ported and new templates are expected to use
296 # old templates still work. Ported and new templates are expected to use
298 # real asset specifications for the includes.
297 # real asset specifications for the includes.
299 mako_directories = settings.setdefault('mako.directories', [
298 mako_directories = settings.setdefault('mako.directories', [
300 # Base templates of the original Pylons application
299 # Base templates of the original Pylons application
301 'rhodecode:templates',
300 'rhodecode:templates',
302 ])
301 ])
303 log.debug(
302 log.debug(
304 "Using the following Mako template directories: %s",
303 "Using the following Mako template directories: %s",
305 mako_directories)
304 mako_directories)
306
305
307 # Default includes, possible to change as a user
306 # Default includes, possible to change as a user
308 pyramid_includes = settings.setdefault('pyramid.includes', [
307 pyramid_includes = settings.setdefault('pyramid.includes', [
309 'rhodecode.lib.middleware.request_wrapper',
308 'rhodecode.lib.middleware.request_wrapper',
310 ])
309 ])
311 log.debug(
310 log.debug(
312 "Using the following pyramid.includes: %s",
311 "Using the following pyramid.includes: %s",
313 pyramid_includes)
312 pyramid_includes)
314
313
315 # TODO: johbo: Re-think this, usually the call to config.include
314 # TODO: johbo: Re-think this, usually the call to config.include
316 # should allow to pass in a prefix.
315 # should allow to pass in a prefix.
317 settings.setdefault('rhodecode.api.url', '/_admin/api')
316 settings.setdefault('rhodecode.api.url', '/_admin/api')
318
317
319 _bool_setting(settings, 'vcs.server.enable', 'true')
318 _bool_setting(settings, 'vcs.server.enable', 'true')
320 _bool_setting(settings, 'static_files', 'true')
319 _bool_setting(settings, 'static_files', 'true')
321 _bool_setting(settings, 'is_test', 'false')
320 _bool_setting(settings, 'is_test', 'false')
322
321
323 return settings
322 return settings
324
323
325
324
326 def _bool_setting(settings, name, default):
325 def _bool_setting(settings, name, default):
327 settings[name] = asbool(settings.get(name, default))
326 settings[name] = asbool(settings.get(name, default))
General Comments 0
You need to be logged in to leave comments. Login now