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