##// END OF EJS Templates
request-wrapper: ensure we wrap WHOLE request not just logic after http detection.
marcink -
r4157:3d2cea78 default
parent child Browse files
Show More
@@ -1,756 +1,754 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 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 import os
21 import os
22 import sys
22 import sys
23 import logging
23 import logging
24 import collections
24 import collections
25 import tempfile
25 import tempfile
26 import time
26 import time
27
27
28 from paste.gzipper import make_gzip_middleware
28 from paste.gzipper import make_gzip_middleware
29 import pyramid.events
29 import pyramid.events
30 from pyramid.wsgi import wsgiapp
30 from pyramid.wsgi import wsgiapp
31 from pyramid.authorization import ACLAuthorizationPolicy
31 from pyramid.authorization import ACLAuthorizationPolicy
32 from pyramid.config import Configurator
32 from pyramid.config import Configurator
33 from pyramid.settings import asbool, aslist
33 from pyramid.settings import asbool, aslist
34 from pyramid.httpexceptions import (
34 from pyramid.httpexceptions import (
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound)
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound)
36 from pyramid.renderers import render_to_response
36 from pyramid.renderers import render_to_response
37
37
38 from rhodecode.model import meta
38 from rhodecode.model import meta
39 from rhodecode.config import patches
39 from rhodecode.config import patches
40 from rhodecode.config import utils as config_utils
40 from rhodecode.config import utils as config_utils
41 from rhodecode.config.environment import load_pyramid_environment
41 from rhodecode.config.environment import load_pyramid_environment
42
42
43 import rhodecode.events
43 import rhodecode.events
44 from rhodecode.lib.middleware.vcs import VCSMiddleware
44 from rhodecode.lib.middleware.vcs import VCSMiddleware
45 from rhodecode.lib.request import Request
45 from rhodecode.lib.request import Request
46 from rhodecode.lib.vcs import VCSCommunicationError
46 from rhodecode.lib.vcs import VCSCommunicationError
47 from rhodecode.lib.exceptions import VCSServerUnavailable
47 from rhodecode.lib.exceptions import VCSServerUnavailable
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
49 from rhodecode.lib.middleware.https_fixup import HttpsFixup
49 from rhodecode.lib.middleware.https_fixup import HttpsFixup
50 from rhodecode.lib.celerylib.loader import configure_celery
50 from rhodecode.lib.celerylib.loader import configure_celery
51 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
51 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
52 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
52 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
53 from rhodecode.lib.exc_tracking import store_exception
53 from rhodecode.lib.exc_tracking import store_exception
54 from rhodecode.subscribers import (
54 from rhodecode.subscribers import (
55 scan_repositories_if_enabled, write_js_routes_if_enabled,
55 scan_repositories_if_enabled, write_js_routes_if_enabled,
56 write_metadata_if_needed, inject_app_settings)
56 write_metadata_if_needed, inject_app_settings)
57
57
58
58
59 log = logging.getLogger(__name__)
59 log = logging.getLogger(__name__)
60
60
61
61
62 def is_http_error(response):
62 def is_http_error(response):
63 # error which should have traceback
63 # error which should have traceback
64 return response.status_code > 499
64 return response.status_code > 499
65
65
66
66
67 def should_load_all():
67 def should_load_all():
68 """
68 """
69 Returns if all application components should be loaded. In some cases it's
69 Returns if all application components should be loaded. In some cases it's
70 desired to skip apps loading for faster shell script execution
70 desired to skip apps loading for faster shell script execution
71 """
71 """
72 ssh_cmd = os.environ.get('RC_CMD_SSH_WRAPPER')
72 ssh_cmd = os.environ.get('RC_CMD_SSH_WRAPPER')
73 if ssh_cmd:
73 if ssh_cmd:
74 return False
74 return False
75
75
76 return True
76 return True
77
77
78
78
79 def make_pyramid_app(global_config, **settings):
79 def make_pyramid_app(global_config, **settings):
80 """
80 """
81 Constructs the WSGI application based on Pyramid.
81 Constructs the WSGI application based on Pyramid.
82
82
83 Specials:
83 Specials:
84
84
85 * The application can also be integrated like a plugin via the call to
85 * The application can also be integrated like a plugin via the call to
86 `includeme`. This is accompanied with the other utility functions which
86 `includeme`. This is accompanied with the other utility functions which
87 are called. Changing this should be done with great care to not break
87 are called. Changing this should be done with great care to not break
88 cases when these fragments are assembled from another place.
88 cases when these fragments are assembled from another place.
89
89
90 """
90 """
91
91
92 # Allows to use format style "{ENV_NAME}" placeholders in the configuration. It
92 # Allows to use format style "{ENV_NAME}" placeholders in the configuration. It
93 # will be replaced by the value of the environment variable "NAME" in this case.
93 # will be replaced by the value of the environment variable "NAME" in this case.
94 start_time = time.time()
94 start_time = time.time()
95
95
96 debug = asbool(global_config.get('debug'))
96 debug = asbool(global_config.get('debug'))
97 if debug:
97 if debug:
98 enable_debug()
98 enable_debug()
99
99
100 environ = {'ENV_{}'.format(key): value for key, value in os.environ.items()}
100 environ = {'ENV_{}'.format(key): value for key, value in os.environ.items()}
101
101
102 global_config = _substitute_values(global_config, environ)
102 global_config = _substitute_values(global_config, environ)
103 settings = _substitute_values(settings, environ)
103 settings = _substitute_values(settings, environ)
104
104
105 sanitize_settings_and_apply_defaults(global_config, settings)
105 sanitize_settings_and_apply_defaults(global_config, settings)
106
106
107 config = Configurator(settings=settings)
107 config = Configurator(settings=settings)
108
108
109 # Apply compatibility patches
109 # Apply compatibility patches
110 patches.inspect_getargspec()
110 patches.inspect_getargspec()
111
111
112 load_pyramid_environment(global_config, settings)
112 load_pyramid_environment(global_config, settings)
113
113
114 # Static file view comes first
114 # Static file view comes first
115 includeme_first(config)
115 includeme_first(config)
116
116
117 includeme(config)
117 includeme(config)
118
118
119 pyramid_app = config.make_wsgi_app()
119 pyramid_app = config.make_wsgi_app()
120 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
120 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
121 pyramid_app.config = config
121 pyramid_app.config = config
122
122
123 config.configure_celery(global_config['__file__'])
123 config.configure_celery(global_config['__file__'])
124 # creating the app uses a connection - return it after we are done
124 # creating the app uses a connection - return it after we are done
125 meta.Session.remove()
125 meta.Session.remove()
126 total_time = time.time() - start_time
126 total_time = time.time() - start_time
127 log.info('Pyramid app `%s` created and configured in %.2fs',
127 log.info('Pyramid app `%s` created and configured in %.2fs',
128 pyramid_app.func_name, total_time)
128 pyramid_app.func_name, total_time)
129
129
130 return pyramid_app
130 return pyramid_app
131
131
132
132
133 def not_found_view(request):
133 def not_found_view(request):
134 """
134 """
135 This creates the view which should be registered as not-found-view to
135 This creates the view which should be registered as not-found-view to
136 pyramid.
136 pyramid.
137 """
137 """
138
138
139 if not getattr(request, 'vcs_call', None):
139 if not getattr(request, 'vcs_call', None):
140 # handle like regular case with our error_handler
140 # handle like regular case with our error_handler
141 return error_handler(HTTPNotFound(), request)
141 return error_handler(HTTPNotFound(), request)
142
142
143 # handle not found view as a vcs call
143 # handle not found view as a vcs call
144 settings = request.registry.settings
144 settings = request.registry.settings
145 ae_client = getattr(request, 'ae_client', None)
145 ae_client = getattr(request, 'ae_client', None)
146 vcs_app = VCSMiddleware(
146 vcs_app = VCSMiddleware(
147 HTTPNotFound(), request.registry, settings,
147 HTTPNotFound(), request.registry, settings,
148 appenlight_client=ae_client)
148 appenlight_client=ae_client)
149
149
150 return wsgiapp(vcs_app)(None, request)
150 return wsgiapp(vcs_app)(None, request)
151
151
152
152
153 def error_handler(exception, request):
153 def error_handler(exception, request):
154 import rhodecode
154 import rhodecode
155 from rhodecode.lib import helpers
155 from rhodecode.lib import helpers
156
156
157 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
157 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
158
158
159 base_response = HTTPInternalServerError()
159 base_response = HTTPInternalServerError()
160 # prefer original exception for the response since it may have headers set
160 # prefer original exception for the response since it may have headers set
161 if isinstance(exception, HTTPException):
161 if isinstance(exception, HTTPException):
162 base_response = exception
162 base_response = exception
163 elif isinstance(exception, VCSCommunicationError):
163 elif isinstance(exception, VCSCommunicationError):
164 base_response = VCSServerUnavailable()
164 base_response = VCSServerUnavailable()
165
165
166 if is_http_error(base_response):
166 if is_http_error(base_response):
167 log.exception(
167 log.exception(
168 'error occurred handling this request for path: %s', request.path)
168 'error occurred handling this request for path: %s', request.path)
169
169
170 error_explanation = base_response.explanation or str(base_response)
170 error_explanation = base_response.explanation or str(base_response)
171 if base_response.status_code == 404:
171 if base_response.status_code == 404:
172 error_explanation += " Optionally you don't have permission to access this page."
172 error_explanation += " Optionally you don't have permission to access this page."
173 c = AttributeDict()
173 c = AttributeDict()
174 c.error_message = base_response.status
174 c.error_message = base_response.status
175 c.error_explanation = error_explanation
175 c.error_explanation = error_explanation
176 c.visual = AttributeDict()
176 c.visual = AttributeDict()
177
177
178 c.visual.rhodecode_support_url = (
178 c.visual.rhodecode_support_url = (
179 request.registry.settings.get('rhodecode_support_url') or
179 request.registry.settings.get('rhodecode_support_url') or
180 request.route_url('rhodecode_support')
180 request.route_url('rhodecode_support')
181 )
181 )
182 c.redirect_time = 0
182 c.redirect_time = 0
183 c.rhodecode_name = rhodecode_title
183 c.rhodecode_name = rhodecode_title
184 if not c.rhodecode_name:
184 if not c.rhodecode_name:
185 c.rhodecode_name = 'Rhodecode'
185 c.rhodecode_name = 'Rhodecode'
186
186
187 c.causes = []
187 c.causes = []
188 if is_http_error(base_response):
188 if is_http_error(base_response):
189 c.causes.append('Server is overloaded.')
189 c.causes.append('Server is overloaded.')
190 c.causes.append('Server database connection is lost.')
190 c.causes.append('Server database connection is lost.')
191 c.causes.append('Server expected unhandled error.')
191 c.causes.append('Server expected unhandled error.')
192
192
193 if hasattr(base_response, 'causes'):
193 if hasattr(base_response, 'causes'):
194 c.causes = base_response.causes
194 c.causes = base_response.causes
195
195
196 c.messages = helpers.flash.pop_messages(request=request)
196 c.messages = helpers.flash.pop_messages(request=request)
197
197
198 exc_info = sys.exc_info()
198 exc_info = sys.exc_info()
199 c.exception_id = id(exc_info)
199 c.exception_id = id(exc_info)
200 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
200 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
201 or base_response.status_code > 499
201 or base_response.status_code > 499
202 c.exception_id_url = request.route_url(
202 c.exception_id_url = request.route_url(
203 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
203 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
204
204
205 if c.show_exception_id:
205 if c.show_exception_id:
206 store_exception(c.exception_id, exc_info)
206 store_exception(c.exception_id, exc_info)
207
207
208 response = render_to_response(
208 response = render_to_response(
209 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
209 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
210 response=base_response)
210 response=base_response)
211
211
212 return response
212 return response
213
213
214
214
215 def includeme_first(config):
215 def includeme_first(config):
216 # redirect automatic browser favicon.ico requests to correct place
216 # redirect automatic browser favicon.ico requests to correct place
217 def favicon_redirect(context, request):
217 def favicon_redirect(context, request):
218 return HTTPFound(
218 return HTTPFound(
219 request.static_path('rhodecode:public/images/favicon.ico'))
219 request.static_path('rhodecode:public/images/favicon.ico'))
220
220
221 config.add_view(favicon_redirect, route_name='favicon')
221 config.add_view(favicon_redirect, route_name='favicon')
222 config.add_route('favicon', '/favicon.ico')
222 config.add_route('favicon', '/favicon.ico')
223
223
224 def robots_redirect(context, request):
224 def robots_redirect(context, request):
225 return HTTPFound(
225 return HTTPFound(
226 request.static_path('rhodecode:public/robots.txt'))
226 request.static_path('rhodecode:public/robots.txt'))
227
227
228 config.add_view(robots_redirect, route_name='robots')
228 config.add_view(robots_redirect, route_name='robots')
229 config.add_route('robots', '/robots.txt')
229 config.add_route('robots', '/robots.txt')
230
230
231 config.add_static_view(
231 config.add_static_view(
232 '_static/deform', 'deform:static')
232 '_static/deform', 'deform:static')
233 config.add_static_view(
233 config.add_static_view(
234 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
234 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
235
235
236
236
237 def includeme(config):
237 def includeme(config):
238 log.debug('Initializing main includeme from %s', os.path.basename(__file__))
238 log.debug('Initializing main includeme from %s', os.path.basename(__file__))
239 settings = config.registry.settings
239 settings = config.registry.settings
240 config.set_request_factory(Request)
240 config.set_request_factory(Request)
241
241
242 # plugin information
242 # plugin information
243 config.registry.rhodecode_plugins = collections.OrderedDict()
243 config.registry.rhodecode_plugins = collections.OrderedDict()
244
244
245 config.add_directive(
245 config.add_directive(
246 'register_rhodecode_plugin', register_rhodecode_plugin)
246 'register_rhodecode_plugin', register_rhodecode_plugin)
247
247
248 config.add_directive('configure_celery', configure_celery)
248 config.add_directive('configure_celery', configure_celery)
249
249
250 if asbool(settings.get('appenlight', 'false')):
250 if asbool(settings.get('appenlight', 'false')):
251 config.include('appenlight_client.ext.pyramid_tween')
251 config.include('appenlight_client.ext.pyramid_tween')
252
252
253 load_all = should_load_all()
253 load_all = should_load_all()
254
254
255 # Includes which are required. The application would fail without them.
255 # Includes which are required. The application would fail without them.
256 config.include('pyramid_mako')
256 config.include('pyramid_mako')
257 config.include('rhodecode.lib.rc_beaker')
257 config.include('rhodecode.lib.rc_beaker')
258 config.include('rhodecode.lib.rc_cache')
258 config.include('rhodecode.lib.rc_cache')
259
259
260 config.include('rhodecode.apps._base.navigation')
260 config.include('rhodecode.apps._base.navigation')
261 config.include('rhodecode.apps._base.subscribers')
261 config.include('rhodecode.apps._base.subscribers')
262 config.include('rhodecode.tweens')
262 config.include('rhodecode.tweens')
263 config.include('rhodecode.authentication')
263 config.include('rhodecode.authentication')
264
264
265 if load_all:
265 if load_all:
266 config.include('rhodecode.integrations')
266 config.include('rhodecode.integrations')
267
267
268 if load_all:
268 if load_all:
269 # load CE authentication plugins
269 # load CE authentication plugins
270 config.include('rhodecode.authentication.plugins.auth_crowd')
270 config.include('rhodecode.authentication.plugins.auth_crowd')
271 config.include('rhodecode.authentication.plugins.auth_headers')
271 config.include('rhodecode.authentication.plugins.auth_headers')
272 config.include('rhodecode.authentication.plugins.auth_jasig_cas')
272 config.include('rhodecode.authentication.plugins.auth_jasig_cas')
273 config.include('rhodecode.authentication.plugins.auth_ldap')
273 config.include('rhodecode.authentication.plugins.auth_ldap')
274 config.include('rhodecode.authentication.plugins.auth_pam')
274 config.include('rhodecode.authentication.plugins.auth_pam')
275 config.include('rhodecode.authentication.plugins.auth_rhodecode')
275 config.include('rhodecode.authentication.plugins.auth_rhodecode')
276 config.include('rhodecode.authentication.plugins.auth_token')
276 config.include('rhodecode.authentication.plugins.auth_token')
277
277
278 # Auto discover authentication plugins and include their configuration.
278 # Auto discover authentication plugins and include their configuration.
279 if asbool(settings.get('auth_plugin.import_legacy_plugins', 'true')):
279 if asbool(settings.get('auth_plugin.import_legacy_plugins', 'true')):
280 from rhodecode.authentication import discover_legacy_plugins
280 from rhodecode.authentication import discover_legacy_plugins
281 discover_legacy_plugins(config)
281 discover_legacy_plugins(config)
282
282
283 # apps
283 # apps
284 if load_all:
284 if load_all:
285 config.include('rhodecode.apps._base')
285 config.include('rhodecode.apps._base')
286 config.include('rhodecode.apps.hovercards')
286 config.include('rhodecode.apps.hovercards')
287 config.include('rhodecode.apps.ops')
287 config.include('rhodecode.apps.ops')
288 config.include('rhodecode.apps.admin')
288 config.include('rhodecode.apps.admin')
289 config.include('rhodecode.apps.channelstream')
289 config.include('rhodecode.apps.channelstream')
290 config.include('rhodecode.apps.file_store')
290 config.include('rhodecode.apps.file_store')
291 config.include('rhodecode.apps.login')
291 config.include('rhodecode.apps.login')
292 config.include('rhodecode.apps.home')
292 config.include('rhodecode.apps.home')
293 config.include('rhodecode.apps.journal')
293 config.include('rhodecode.apps.journal')
294 config.include('rhodecode.apps.repository')
294 config.include('rhodecode.apps.repository')
295 config.include('rhodecode.apps.repo_group')
295 config.include('rhodecode.apps.repo_group')
296 config.include('rhodecode.apps.user_group')
296 config.include('rhodecode.apps.user_group')
297 config.include('rhodecode.apps.search')
297 config.include('rhodecode.apps.search')
298 config.include('rhodecode.apps.user_profile')
298 config.include('rhodecode.apps.user_profile')
299 config.include('rhodecode.apps.user_group_profile')
299 config.include('rhodecode.apps.user_group_profile')
300 config.include('rhodecode.apps.my_account')
300 config.include('rhodecode.apps.my_account')
301 config.include('rhodecode.apps.svn_support')
301 config.include('rhodecode.apps.svn_support')
302 config.include('rhodecode.apps.ssh_support')
302 config.include('rhodecode.apps.ssh_support')
303 config.include('rhodecode.apps.gist')
303 config.include('rhodecode.apps.gist')
304 config.include('rhodecode.apps.debug_style')
304 config.include('rhodecode.apps.debug_style')
305 config.include('rhodecode.api')
305 config.include('rhodecode.api')
306
306
307 config.add_route('rhodecode_support', 'https://rhodecode.com/help/', static=True)
307 config.add_route('rhodecode_support', 'https://rhodecode.com/help/', static=True)
308 config.add_translation_dirs('rhodecode:i18n/')
308 config.add_translation_dirs('rhodecode:i18n/')
309 settings['default_locale_name'] = settings.get('lang', 'en')
309 settings['default_locale_name'] = settings.get('lang', 'en')
310
310
311 # Add subscribers.
311 # Add subscribers.
312 if load_all:
312 if load_all:
313 config.add_subscriber(inject_app_settings,
313 config.add_subscriber(inject_app_settings,
314 pyramid.events.ApplicationCreated)
314 pyramid.events.ApplicationCreated)
315 config.add_subscriber(scan_repositories_if_enabled,
315 config.add_subscriber(scan_repositories_if_enabled,
316 pyramid.events.ApplicationCreated)
316 pyramid.events.ApplicationCreated)
317 config.add_subscriber(write_metadata_if_needed,
317 config.add_subscriber(write_metadata_if_needed,
318 pyramid.events.ApplicationCreated)
318 pyramid.events.ApplicationCreated)
319 config.add_subscriber(write_js_routes_if_enabled,
319 config.add_subscriber(write_js_routes_if_enabled,
320 pyramid.events.ApplicationCreated)
320 pyramid.events.ApplicationCreated)
321
321
322 # request custom methods
322 # request custom methods
323 config.add_request_method(
323 config.add_request_method(
324 'rhodecode.lib.partial_renderer.get_partial_renderer',
324 'rhodecode.lib.partial_renderer.get_partial_renderer',
325 'get_partial_renderer')
325 'get_partial_renderer')
326
326
327 config.add_request_method(
327 config.add_request_method(
328 'rhodecode.lib.request_counter.get_request_counter',
328 'rhodecode.lib.request_counter.get_request_counter',
329 'request_count')
329 'request_count')
330
330
331 # Set the authorization policy.
331 # Set the authorization policy.
332 authz_policy = ACLAuthorizationPolicy()
332 authz_policy = ACLAuthorizationPolicy()
333 config.set_authorization_policy(authz_policy)
333 config.set_authorization_policy(authz_policy)
334
334
335 # Set the default renderer for HTML templates to mako.
335 # Set the default renderer for HTML templates to mako.
336 config.add_mako_renderer('.html')
336 config.add_mako_renderer('.html')
337
337
338 config.add_renderer(
338 config.add_renderer(
339 name='json_ext',
339 name='json_ext',
340 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
340 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
341
341
342 # include RhodeCode plugins
342 # include RhodeCode plugins
343 includes = aslist(settings.get('rhodecode.includes', []))
343 includes = aslist(settings.get('rhodecode.includes', []))
344 for inc in includes:
344 for inc in includes:
345 config.include(inc)
345 config.include(inc)
346
346
347 # custom not found view, if our pyramid app doesn't know how to handle
347 # custom not found view, if our pyramid app doesn't know how to handle
348 # the request pass it to potential VCS handling ap
348 # the request pass it to potential VCS handling ap
349 config.add_notfound_view(not_found_view)
349 config.add_notfound_view(not_found_view)
350 if not settings.get('debugtoolbar.enabled', False):
350 if not settings.get('debugtoolbar.enabled', False):
351 # disabled debugtoolbar handle all exceptions via the error_handlers
351 # disabled debugtoolbar handle all exceptions via the error_handlers
352 config.add_view(error_handler, context=Exception)
352 config.add_view(error_handler, context=Exception)
353
353
354 # all errors including 403/404/50X
354 # all errors including 403/404/50X
355 config.add_view(error_handler, context=HTTPError)
355 config.add_view(error_handler, context=HTTPError)
356
356
357
357
358 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
358 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
359 """
359 """
360 Apply outer WSGI middlewares around the application.
360 Apply outer WSGI middlewares around the application.
361 """
361 """
362 registry = config.registry
362 registry = config.registry
363 settings = registry.settings
363 settings = registry.settings
364
364
365 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
365 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
366 pyramid_app = HttpsFixup(pyramid_app, settings)
366 pyramid_app = HttpsFixup(pyramid_app, settings)
367
367
368 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
368 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
369 pyramid_app, settings)
369 pyramid_app, settings)
370 registry.ae_client = _ae_client
370 registry.ae_client = _ae_client
371
371
372 if settings['gzip_responses']:
372 if settings['gzip_responses']:
373 pyramid_app = make_gzip_middleware(
373 pyramid_app = make_gzip_middleware(
374 pyramid_app, settings, compress_level=1)
374 pyramid_app, settings, compress_level=1)
375
375
376 # this should be the outer most middleware in the wsgi stack since
376 # this should be the outer most middleware in the wsgi stack since
377 # middleware like Routes make database calls
377 # middleware like Routes make database calls
378 def pyramid_app_with_cleanup(environ, start_response):
378 def pyramid_app_with_cleanup(environ, start_response):
379 try:
379 try:
380 return pyramid_app(environ, start_response)
380 return pyramid_app(environ, start_response)
381 finally:
381 finally:
382 # Dispose current database session and rollback uncommitted
382 # Dispose current database session and rollback uncommitted
383 # transactions.
383 # transactions.
384 meta.Session.remove()
384 meta.Session.remove()
385
385
386 # In a single threaded mode server, on non sqlite db we should have
386 # In a single threaded mode server, on non sqlite db we should have
387 # '0 Current Checked out connections' at the end of a request,
387 # '0 Current Checked out connections' at the end of a request,
388 # if not, then something, somewhere is leaving a connection open
388 # if not, then something, somewhere is leaving a connection open
389 pool = meta.Base.metadata.bind.engine.pool
389 pool = meta.Base.metadata.bind.engine.pool
390 log.debug('sa pool status: %s', pool.status())
390 log.debug('sa pool status: %s', pool.status())
391 log.debug('Request processing finalized')
391 log.debug('Request processing finalized')
392
392
393 return pyramid_app_with_cleanup
393 return pyramid_app_with_cleanup
394
394
395
395
396 def sanitize_settings_and_apply_defaults(global_config, settings):
396 def sanitize_settings_and_apply_defaults(global_config, settings):
397 """
397 """
398 Applies settings defaults and does all type conversion.
398 Applies settings defaults and does all type conversion.
399
399
400 We would move all settings parsing and preparation into this place, so that
400 We would move all settings parsing and preparation into this place, so that
401 we have only one place left which deals with this part. The remaining parts
401 we have only one place left which deals with this part. The remaining parts
402 of the application would start to rely fully on well prepared settings.
402 of the application would start to rely fully on well prepared settings.
403
403
404 This piece would later be split up per topic to avoid a big fat monster
404 This piece would later be split up per topic to avoid a big fat monster
405 function.
405 function.
406 """
406 """
407
407
408 settings.setdefault('rhodecode.edition', 'Community Edition')
408 settings.setdefault('rhodecode.edition', 'Community Edition')
409
409
410 if 'mako.default_filters' not in settings:
410 if 'mako.default_filters' not in settings:
411 # set custom default filters if we don't have it defined
411 # set custom default filters if we don't have it defined
412 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
412 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
413 settings['mako.default_filters'] = 'h_filter'
413 settings['mako.default_filters'] = 'h_filter'
414
414
415 if 'mako.directories' not in settings:
415 if 'mako.directories' not in settings:
416 mako_directories = settings.setdefault('mako.directories', [
416 mako_directories = settings.setdefault('mako.directories', [
417 # Base templates of the original application
417 # Base templates of the original application
418 'rhodecode:templates',
418 'rhodecode:templates',
419 ])
419 ])
420 log.debug(
420 log.debug(
421 "Using the following Mako template directories: %s",
421 "Using the following Mako template directories: %s",
422 mako_directories)
422 mako_directories)
423
423
424 # NOTE(marcink): fix redis requirement for schema of connection since 3.X
424 # NOTE(marcink): fix redis requirement for schema of connection since 3.X
425 if 'beaker.session.type' in settings and settings['beaker.session.type'] == 'ext:redis':
425 if 'beaker.session.type' in settings and settings['beaker.session.type'] == 'ext:redis':
426 raw_url = settings['beaker.session.url']
426 raw_url = settings['beaker.session.url']
427 if not raw_url.startswith(('redis://', 'rediss://', 'unix://')):
427 if not raw_url.startswith(('redis://', 'rediss://', 'unix://')):
428 settings['beaker.session.url'] = 'redis://' + raw_url
428 settings['beaker.session.url'] = 'redis://' + raw_url
429
429
430 # Default includes, possible to change as a user
430 # Default includes, possible to change as a user
431 pyramid_includes = settings.setdefault('pyramid.includes', [
431 pyramid_includes = settings.setdefault('pyramid.includes', [])
432 'rhodecode.lib.middleware.request_wrapper',
433 ])
434 log.debug(
432 log.debug(
435 "Using the following pyramid.includes: %s",
433 "Using the following pyramid.includes: %s",
436 pyramid_includes)
434 pyramid_includes)
437
435
438 # TODO: johbo: Re-think this, usually the call to config.include
436 # TODO: johbo: Re-think this, usually the call to config.include
439 # should allow to pass in a prefix.
437 # should allow to pass in a prefix.
440 settings.setdefault('rhodecode.api.url', '/_admin/api')
438 settings.setdefault('rhodecode.api.url', '/_admin/api')
441 settings.setdefault('__file__', global_config.get('__file__'))
439 settings.setdefault('__file__', global_config.get('__file__'))
442
440
443 # Sanitize generic settings.
441 # Sanitize generic settings.
444 _list_setting(settings, 'default_encoding', 'UTF-8')
442 _list_setting(settings, 'default_encoding', 'UTF-8')
445 _bool_setting(settings, 'is_test', 'false')
443 _bool_setting(settings, 'is_test', 'false')
446 _bool_setting(settings, 'gzip_responses', 'false')
444 _bool_setting(settings, 'gzip_responses', 'false')
447
445
448 # Call split out functions that sanitize settings for each topic.
446 # Call split out functions that sanitize settings for each topic.
449 _sanitize_appenlight_settings(settings)
447 _sanitize_appenlight_settings(settings)
450 _sanitize_vcs_settings(settings)
448 _sanitize_vcs_settings(settings)
451 _sanitize_cache_settings(settings)
449 _sanitize_cache_settings(settings)
452
450
453 # configure instance id
451 # configure instance id
454 config_utils.set_instance_id(settings)
452 config_utils.set_instance_id(settings)
455
453
456 return settings
454 return settings
457
455
458
456
459 def enable_debug():
457 def enable_debug():
460 """
458 """
461 Helper to enable debug on running instance
459 Helper to enable debug on running instance
462 :return:
460 :return:
463 """
461 """
464 import tempfile
462 import tempfile
465 import textwrap
463 import textwrap
466 import logging.config
464 import logging.config
467
465
468 ini_template = textwrap.dedent("""
466 ini_template = textwrap.dedent("""
469 #####################################
467 #####################################
470 ### DEBUG LOGGING CONFIGURATION ####
468 ### DEBUG LOGGING CONFIGURATION ####
471 #####################################
469 #####################################
472 [loggers]
470 [loggers]
473 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
471 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
474
472
475 [handlers]
473 [handlers]
476 keys = console, console_sql
474 keys = console, console_sql
477
475
478 [formatters]
476 [formatters]
479 keys = generic, color_formatter, color_formatter_sql
477 keys = generic, color_formatter, color_formatter_sql
480
478
481 #############
479 #############
482 ## LOGGERS ##
480 ## LOGGERS ##
483 #############
481 #############
484 [logger_root]
482 [logger_root]
485 level = NOTSET
483 level = NOTSET
486 handlers = console
484 handlers = console
487
485
488 [logger_sqlalchemy]
486 [logger_sqlalchemy]
489 level = INFO
487 level = INFO
490 handlers = console_sql
488 handlers = console_sql
491 qualname = sqlalchemy.engine
489 qualname = sqlalchemy.engine
492 propagate = 0
490 propagate = 0
493
491
494 [logger_beaker]
492 [logger_beaker]
495 level = DEBUG
493 level = DEBUG
496 handlers =
494 handlers =
497 qualname = beaker.container
495 qualname = beaker.container
498 propagate = 1
496 propagate = 1
499
497
500 [logger_rhodecode]
498 [logger_rhodecode]
501 level = DEBUG
499 level = DEBUG
502 handlers =
500 handlers =
503 qualname = rhodecode
501 qualname = rhodecode
504 propagate = 1
502 propagate = 1
505
503
506 [logger_ssh_wrapper]
504 [logger_ssh_wrapper]
507 level = DEBUG
505 level = DEBUG
508 handlers =
506 handlers =
509 qualname = ssh_wrapper
507 qualname = ssh_wrapper
510 propagate = 1
508 propagate = 1
511
509
512 [logger_celery]
510 [logger_celery]
513 level = DEBUG
511 level = DEBUG
514 handlers =
512 handlers =
515 qualname = celery
513 qualname = celery
516
514
517
515
518 ##############
516 ##############
519 ## HANDLERS ##
517 ## HANDLERS ##
520 ##############
518 ##############
521
519
522 [handler_console]
520 [handler_console]
523 class = StreamHandler
521 class = StreamHandler
524 args = (sys.stderr, )
522 args = (sys.stderr, )
525 level = DEBUG
523 level = DEBUG
526 formatter = color_formatter
524 formatter = color_formatter
527
525
528 [handler_console_sql]
526 [handler_console_sql]
529 # "level = DEBUG" logs SQL queries and results.
527 # "level = DEBUG" logs SQL queries and results.
530 # "level = INFO" logs SQL queries.
528 # "level = INFO" logs SQL queries.
531 # "level = WARN" logs neither. (Recommended for production systems.)
529 # "level = WARN" logs neither. (Recommended for production systems.)
532 class = StreamHandler
530 class = StreamHandler
533 args = (sys.stderr, )
531 args = (sys.stderr, )
534 level = WARN
532 level = WARN
535 formatter = color_formatter_sql
533 formatter = color_formatter_sql
536
534
537 ################
535 ################
538 ## FORMATTERS ##
536 ## FORMATTERS ##
539 ################
537 ################
540
538
541 [formatter_generic]
539 [formatter_generic]
542 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
540 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
543 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
541 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
544 datefmt = %Y-%m-%d %H:%M:%S
542 datefmt = %Y-%m-%d %H:%M:%S
545
543
546 [formatter_color_formatter]
544 [formatter_color_formatter]
547 class = rhodecode.lib.logging_formatter.ColorRequestTrackingFormatter
545 class = rhodecode.lib.logging_formatter.ColorRequestTrackingFormatter
548 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
546 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
549 datefmt = %Y-%m-%d %H:%M:%S
547 datefmt = %Y-%m-%d %H:%M:%S
550
548
551 [formatter_color_formatter_sql]
549 [formatter_color_formatter_sql]
552 class = rhodecode.lib.logging_formatter.ColorFormatterSql
550 class = rhodecode.lib.logging_formatter.ColorFormatterSql
553 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
551 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
554 datefmt = %Y-%m-%d %H:%M:%S
552 datefmt = %Y-%m-%d %H:%M:%S
555 """)
553 """)
556
554
557 with tempfile.NamedTemporaryFile(prefix='rc_debug_logging_', suffix='.ini',
555 with tempfile.NamedTemporaryFile(prefix='rc_debug_logging_', suffix='.ini',
558 delete=False) as f:
556 delete=False) as f:
559 log.info('Saved Temporary DEBUG config at %s', f.name)
557 log.info('Saved Temporary DEBUG config at %s', f.name)
560 f.write(ini_template)
558 f.write(ini_template)
561
559
562 logging.config.fileConfig(f.name)
560 logging.config.fileConfig(f.name)
563 log.debug('DEBUG MODE ON')
561 log.debug('DEBUG MODE ON')
564 os.remove(f.name)
562 os.remove(f.name)
565
563
566
564
567 def _sanitize_appenlight_settings(settings):
565 def _sanitize_appenlight_settings(settings):
568 _bool_setting(settings, 'appenlight', 'false')
566 _bool_setting(settings, 'appenlight', 'false')
569
567
570
568
571 def _sanitize_vcs_settings(settings):
569 def _sanitize_vcs_settings(settings):
572 """
570 """
573 Applies settings defaults and does type conversion for all VCS related
571 Applies settings defaults and does type conversion for all VCS related
574 settings.
572 settings.
575 """
573 """
576 _string_setting(settings, 'vcs.svn.compatible_version', '')
574 _string_setting(settings, 'vcs.svn.compatible_version', '')
577 _string_setting(settings, 'vcs.hooks.protocol', 'http')
575 _string_setting(settings, 'vcs.hooks.protocol', 'http')
578 _string_setting(settings, 'vcs.hooks.host', '127.0.0.1')
576 _string_setting(settings, 'vcs.hooks.host', '127.0.0.1')
579 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
577 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
580 _string_setting(settings, 'vcs.server', '')
578 _string_setting(settings, 'vcs.server', '')
581 _string_setting(settings, 'vcs.server.log_level', 'debug')
579 _string_setting(settings, 'vcs.server.log_level', 'debug')
582 _string_setting(settings, 'vcs.server.protocol', 'http')
580 _string_setting(settings, 'vcs.server.protocol', 'http')
583 _bool_setting(settings, 'startup.import_repos', 'false')
581 _bool_setting(settings, 'startup.import_repos', 'false')
584 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
582 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
585 _bool_setting(settings, 'vcs.server.enable', 'true')
583 _bool_setting(settings, 'vcs.server.enable', 'true')
586 _bool_setting(settings, 'vcs.start_server', 'false')
584 _bool_setting(settings, 'vcs.start_server', 'false')
587 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
585 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
588 _int_setting(settings, 'vcs.connection_timeout', 3600)
586 _int_setting(settings, 'vcs.connection_timeout', 3600)
589
587
590 # Support legacy values of vcs.scm_app_implementation. Legacy
588 # Support legacy values of vcs.scm_app_implementation. Legacy
591 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
589 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
592 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
590 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
593 scm_app_impl = settings['vcs.scm_app_implementation']
591 scm_app_impl = settings['vcs.scm_app_implementation']
594 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
592 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
595 settings['vcs.scm_app_implementation'] = 'http'
593 settings['vcs.scm_app_implementation'] = 'http'
596
594
597
595
598 def _sanitize_cache_settings(settings):
596 def _sanitize_cache_settings(settings):
599 temp_store = tempfile.gettempdir()
597 temp_store = tempfile.gettempdir()
600 default_cache_dir = os.path.join(temp_store, 'rc_cache')
598 default_cache_dir = os.path.join(temp_store, 'rc_cache')
601
599
602 # save default, cache dir, and use it for all backends later.
600 # save default, cache dir, and use it for all backends later.
603 default_cache_dir = _string_setting(
601 default_cache_dir = _string_setting(
604 settings,
602 settings,
605 'cache_dir',
603 'cache_dir',
606 default_cache_dir, lower=False, default_when_empty=True)
604 default_cache_dir, lower=False, default_when_empty=True)
607
605
608 # ensure we have our dir created
606 # ensure we have our dir created
609 if not os.path.isdir(default_cache_dir):
607 if not os.path.isdir(default_cache_dir):
610 os.makedirs(default_cache_dir, mode=0o755)
608 os.makedirs(default_cache_dir, mode=0o755)
611
609
612 # exception store cache
610 # exception store cache
613 _string_setting(
611 _string_setting(
614 settings,
612 settings,
615 'exception_tracker.store_path',
613 'exception_tracker.store_path',
616 temp_store, lower=False, default_when_empty=True)
614 temp_store, lower=False, default_when_empty=True)
617
615
618 # cache_perms
616 # cache_perms
619 _string_setting(
617 _string_setting(
620 settings,
618 settings,
621 'rc_cache.cache_perms.backend',
619 'rc_cache.cache_perms.backend',
622 'dogpile.cache.rc.file_namespace', lower=False)
620 'dogpile.cache.rc.file_namespace', lower=False)
623 _int_setting(
621 _int_setting(
624 settings,
622 settings,
625 'rc_cache.cache_perms.expiration_time',
623 'rc_cache.cache_perms.expiration_time',
626 60)
624 60)
627 _string_setting(
625 _string_setting(
628 settings,
626 settings,
629 'rc_cache.cache_perms.arguments.filename',
627 'rc_cache.cache_perms.arguments.filename',
630 os.path.join(default_cache_dir, 'rc_cache_1'), lower=False)
628 os.path.join(default_cache_dir, 'rc_cache_1'), lower=False)
631
629
632 # cache_repo
630 # cache_repo
633 _string_setting(
631 _string_setting(
634 settings,
632 settings,
635 'rc_cache.cache_repo.backend',
633 'rc_cache.cache_repo.backend',
636 'dogpile.cache.rc.file_namespace', lower=False)
634 'dogpile.cache.rc.file_namespace', lower=False)
637 _int_setting(
635 _int_setting(
638 settings,
636 settings,
639 'rc_cache.cache_repo.expiration_time',
637 'rc_cache.cache_repo.expiration_time',
640 60)
638 60)
641 _string_setting(
639 _string_setting(
642 settings,
640 settings,
643 'rc_cache.cache_repo.arguments.filename',
641 'rc_cache.cache_repo.arguments.filename',
644 os.path.join(default_cache_dir, 'rc_cache_2'), lower=False)
642 os.path.join(default_cache_dir, 'rc_cache_2'), lower=False)
645
643
646 # cache_license
644 # cache_license
647 _string_setting(
645 _string_setting(
648 settings,
646 settings,
649 'rc_cache.cache_license.backend',
647 'rc_cache.cache_license.backend',
650 'dogpile.cache.rc.file_namespace', lower=False)
648 'dogpile.cache.rc.file_namespace', lower=False)
651 _int_setting(
649 _int_setting(
652 settings,
650 settings,
653 'rc_cache.cache_license.expiration_time',
651 'rc_cache.cache_license.expiration_time',
654 5*60)
652 5*60)
655 _string_setting(
653 _string_setting(
656 settings,
654 settings,
657 'rc_cache.cache_license.arguments.filename',
655 'rc_cache.cache_license.arguments.filename',
658 os.path.join(default_cache_dir, 'rc_cache_3'), lower=False)
656 os.path.join(default_cache_dir, 'rc_cache_3'), lower=False)
659
657
660 # cache_repo_longterm memory, 96H
658 # cache_repo_longterm memory, 96H
661 _string_setting(
659 _string_setting(
662 settings,
660 settings,
663 'rc_cache.cache_repo_longterm.backend',
661 'rc_cache.cache_repo_longterm.backend',
664 'dogpile.cache.rc.memory_lru', lower=False)
662 'dogpile.cache.rc.memory_lru', lower=False)
665 _int_setting(
663 _int_setting(
666 settings,
664 settings,
667 'rc_cache.cache_repo_longterm.expiration_time',
665 'rc_cache.cache_repo_longterm.expiration_time',
668 345600)
666 345600)
669 _int_setting(
667 _int_setting(
670 settings,
668 settings,
671 'rc_cache.cache_repo_longterm.max_size',
669 'rc_cache.cache_repo_longterm.max_size',
672 10000)
670 10000)
673
671
674 # sql_cache_short
672 # sql_cache_short
675 _string_setting(
673 _string_setting(
676 settings,
674 settings,
677 'rc_cache.sql_cache_short.backend',
675 'rc_cache.sql_cache_short.backend',
678 'dogpile.cache.rc.memory_lru', lower=False)
676 'dogpile.cache.rc.memory_lru', lower=False)
679 _int_setting(
677 _int_setting(
680 settings,
678 settings,
681 'rc_cache.sql_cache_short.expiration_time',
679 'rc_cache.sql_cache_short.expiration_time',
682 30)
680 30)
683 _int_setting(
681 _int_setting(
684 settings,
682 settings,
685 'rc_cache.sql_cache_short.max_size',
683 'rc_cache.sql_cache_short.max_size',
686 10000)
684 10000)
687
685
688
686
689 def _int_setting(settings, name, default):
687 def _int_setting(settings, name, default):
690 settings[name] = int(settings.get(name, default))
688 settings[name] = int(settings.get(name, default))
691 return settings[name]
689 return settings[name]
692
690
693
691
694 def _bool_setting(settings, name, default):
692 def _bool_setting(settings, name, default):
695 input_val = settings.get(name, default)
693 input_val = settings.get(name, default)
696 if isinstance(input_val, unicode):
694 if isinstance(input_val, unicode):
697 input_val = input_val.encode('utf8')
695 input_val = input_val.encode('utf8')
698 settings[name] = asbool(input_val)
696 settings[name] = asbool(input_val)
699 return settings[name]
697 return settings[name]
700
698
701
699
702 def _list_setting(settings, name, default):
700 def _list_setting(settings, name, default):
703 raw_value = settings.get(name, default)
701 raw_value = settings.get(name, default)
704
702
705 old_separator = ','
703 old_separator = ','
706 if old_separator in raw_value:
704 if old_separator in raw_value:
707 # If we get a comma separated list, pass it to our own function.
705 # If we get a comma separated list, pass it to our own function.
708 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
706 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
709 else:
707 else:
710 # Otherwise we assume it uses pyramids space/newline separation.
708 # Otherwise we assume it uses pyramids space/newline separation.
711 settings[name] = aslist(raw_value)
709 settings[name] = aslist(raw_value)
712 return settings[name]
710 return settings[name]
713
711
714
712
715 def _string_setting(settings, name, default, lower=True, default_when_empty=False):
713 def _string_setting(settings, name, default, lower=True, default_when_empty=False):
716 value = settings.get(name, default)
714 value = settings.get(name, default)
717
715
718 if default_when_empty and not value:
716 if default_when_empty and not value:
719 # use default value when value is empty
717 # use default value when value is empty
720 value = default
718 value = default
721
719
722 if lower:
720 if lower:
723 value = value.lower()
721 value = value.lower()
724 settings[name] = value
722 settings[name] = value
725 return settings[name]
723 return settings[name]
726
724
727
725
728 def _substitute_values(mapping, substitutions):
726 def _substitute_values(mapping, substitutions):
729 result = {}
727 result = {}
730
728
731 try:
729 try:
732 for key, value in mapping.items():
730 for key, value in mapping.items():
733 # initialize without substitution first
731 # initialize without substitution first
734 result[key] = value
732 result[key] = value
735
733
736 # Note: Cannot use regular replacements, since they would clash
734 # Note: Cannot use regular replacements, since they would clash
737 # with the implementation of ConfigParser. Using "format" instead.
735 # with the implementation of ConfigParser. Using "format" instead.
738 try:
736 try:
739 result[key] = value.format(**substitutions)
737 result[key] = value.format(**substitutions)
740 except KeyError as e:
738 except KeyError as e:
741 env_var = '{}'.format(e.args[0])
739 env_var = '{}'.format(e.args[0])
742
740
743 msg = 'Failed to substitute: `{key}={{{var}}}` with environment entry. ' \
741 msg = 'Failed to substitute: `{key}={{{var}}}` with environment entry. ' \
744 'Make sure your environment has {var} set, or remove this ' \
742 'Make sure your environment has {var} set, or remove this ' \
745 'variable from config file'.format(key=key, var=env_var)
743 'variable from config file'.format(key=key, var=env_var)
746
744
747 if env_var.startswith('ENV_'):
745 if env_var.startswith('ENV_'):
748 raise ValueError(msg)
746 raise ValueError(msg)
749 else:
747 else:
750 log.warning(msg)
748 log.warning(msg)
751
749
752 except ValueError as e:
750 except ValueError as e:
753 log.warning('Failed to substitute ENV variable: %s', e)
751 log.warning('Failed to substitute ENV variable: %s', e)
754 result = mapping
752 result = mapping
755
753
756 return result
754 return result
@@ -1,61 +1,62 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2019 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 import time
21 import time
22 import logging
22 import logging
23
23
24 import rhodecode
24 import rhodecode
25 from rhodecode.lib.base import get_ip_addr, get_access_path, get_user_agent
25 from rhodecode.lib.base import get_ip_addr, get_access_path, get_user_agent
26 from rhodecode.lib.utils2 import safe_str
26 from rhodecode.lib.utils2 import safe_str
27
27
28
28
29 log = logging.getLogger(__name__)
29 log = logging.getLogger(__name__)
30
30
31
31
32 class RequestWrapperTween(object):
32 class RequestWrapperTween(object):
33 def __init__(self, handler, registry):
33 def __init__(self, handler, registry):
34 self.handler = handler
34 self.handler = handler
35 self.registry = registry
35 self.registry = registry
36
36
37 # one-time configuration code goes here
37 # one-time configuration code goes here
38
38
39 def __call__(self, request):
39 def __call__(self, request):
40 start = time.time()
40 start = time.time()
41 log.debug('Starting request time measurement')
41 try:
42 try:
42 response = self.handler(request)
43 response = self.handler(request)
43 finally:
44 finally:
44 end = time.time()
45 end = time.time()
45 total = end - start
46 total = end - start
46 count = request.request_count()
47 count = request.request_count()
47 _ver_ = rhodecode.__version__
48 _ver_ = rhodecode.__version__
48 log.info(
49 log.info(
49 'Req[%4s] IP: %s %s Request to %s time: %.4fs [%s], RhodeCode %s',
50 'Req[%4s] IP: %s %s Request to %s time: %.4fs [%s], RhodeCode %s',
50 count, get_ip_addr(request.environ), request.environ.get('REQUEST_METHOD'),
51 count, get_ip_addr(request.environ), request.environ.get('REQUEST_METHOD'),
51 safe_str(get_access_path(request.environ)), total,
52 safe_str(get_access_path(request.environ)), total,
52 get_user_agent(request. environ), _ver_
53 get_user_agent(request. environ), _ver_
53 )
54 )
54
55
55 return response
56 return response
56
57
57
58
58 def includeme(config):
59 def includeme(config):
59 config.add_tween(
60 config.add_tween(
60 'rhodecode.lib.middleware.request_wrapper.RequestWrapperTween',
61 'rhodecode.lib.middleware.request_wrapper.RequestWrapperTween',
61 )
62 )
@@ -1,118 +1,121 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 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 import logging
22 import logging
23 from pyramid.httpexceptions import HTTPException, HTTPBadRequest
23 from pyramid.httpexceptions import HTTPException, HTTPBadRequest
24
24
25 from rhodecode.lib.middleware.vcs import (
25 from rhodecode.lib.middleware.vcs import (
26 detect_vcs_request, VCS_TYPE_KEY, VCS_TYPE_SKIP)
26 detect_vcs_request, VCS_TYPE_KEY, VCS_TYPE_SKIP)
27
27
28
28
29 log = logging.getLogger(__name__)
29 log = logging.getLogger(__name__)
30
30
31
31
32 def vcs_detection_tween_factory(handler, registry):
32 def vcs_detection_tween_factory(handler, registry):
33
33
34 def vcs_detection_tween(request):
34 def vcs_detection_tween(request):
35 """
35 """
36 Do detection of vcs type, and save results for other layers to re-use
36 Do detection of vcs type, and save results for other layers to re-use
37 this information
37 this information
38 """
38 """
39 vcs_server_enabled = request.registry.settings.get('vcs.server.enable')
39 vcs_server_enabled = request.registry.settings.get('vcs.server.enable')
40 vcs_handler = vcs_server_enabled and detect_vcs_request(
40 vcs_handler = vcs_server_enabled and detect_vcs_request(
41 request.environ, request.registry.settings.get('vcs.backends'))
41 request.environ, request.registry.settings.get('vcs.backends'))
42
42
43 if vcs_handler:
43 if vcs_handler:
44 # save detected VCS type for later re-use
44 # save detected VCS type for later re-use
45 request.environ[VCS_TYPE_KEY] = vcs_handler.SCM
45 request.environ[VCS_TYPE_KEY] = vcs_handler.SCM
46 request.vcs_call = vcs_handler.SCM
46 request.vcs_call = vcs_handler.SCM
47
47
48 log.debug('Processing request with `%s` handler', handler)
48 log.debug('Processing request with `%s` handler', handler)
49 return handler(request)
49 return handler(request)
50
50
51 # mark that we didn't detect an VCS, and we can skip detection later on
51 # mark that we didn't detect an VCS, and we can skip detection later on
52 request.environ[VCS_TYPE_KEY] = VCS_TYPE_SKIP
52 request.environ[VCS_TYPE_KEY] = VCS_TYPE_SKIP
53
53
54 log.debug('Processing request with `%s` handler', handler)
54 log.debug('Processing request with `%s` handler', handler)
55 return handler(request)
55 return handler(request)
56
56
57 return vcs_detection_tween
57 return vcs_detection_tween
58
58
59
59
60 def junk_encoding_detector(request):
60 def junk_encoding_detector(request):
61 """
61 """
62 Detect bad encoded GET params, and fail immediately with BadRequest
62 Detect bad encoded GET params, and fail immediately with BadRequest
63 """
63 """
64
64
65 try:
65 try:
66 request.GET.get("", None)
66 request.GET.get("", None)
67 except UnicodeDecodeError:
67 except UnicodeDecodeError:
68 raise HTTPBadRequest("Invalid bytes in query string.")
68 raise HTTPBadRequest("Invalid bytes in query string.")
69
69
70
70
71 def bad_url_data_detector(request):
71 def bad_url_data_detector(request):
72 """
72 """
73 Detect invalid bytes in a path.
73 Detect invalid bytes in a path.
74 """
74 """
75 try:
75 try:
76 request.path_info
76 request.path_info
77 except UnicodeDecodeError:
77 except UnicodeDecodeError:
78 raise HTTPBadRequest("Invalid bytes in URL.")
78 raise HTTPBadRequest("Invalid bytes in URL.")
79
79
80
80
81 def junk_form_data_detector(request):
81 def junk_form_data_detector(request):
82 """
82 """
83 Detect bad encoded POST params, and fail immediately with BadRequest
83 Detect bad encoded POST params, and fail immediately with BadRequest
84 """
84 """
85
85
86 if request.method == "POST":
86 if request.method == "POST":
87 try:
87 try:
88 request.POST.get("", None)
88 request.POST.get("", None)
89 except ValueError:
89 except ValueError:
90 raise HTTPBadRequest("Invalid bytes in form data.")
90 raise HTTPBadRequest("Invalid bytes in form data.")
91
91
92
92
93 def sanity_check_factory(handler, registry):
93 def sanity_check_factory(handler, registry):
94 def sanity_check(request):
94 def sanity_check(request):
95 log.debug('Checking current URL sanity for bad data')
95 log.debug('Checking current URL sanity for bad data')
96 try:
96 try:
97 junk_encoding_detector(request)
97 junk_encoding_detector(request)
98 bad_url_data_detector(request)
98 bad_url_data_detector(request)
99 junk_form_data_detector(request)
99 junk_form_data_detector(request)
100 except HTTPException as exc:
100 except HTTPException as exc:
101 return exc
101 return exc
102
102
103 return handler(request)
103 return handler(request)
104
104
105 return sanity_check
105 return sanity_check
106
106
107
107
108 def includeme(config):
108 def includeme(config):
109 config.add_subscriber('rhodecode.subscribers.add_renderer_globals',
109 config.add_subscriber('rhodecode.subscribers.add_renderer_globals',
110 'pyramid.events.BeforeRender')
110 'pyramid.events.BeforeRender')
111 config.add_subscriber('rhodecode.subscribers.set_user_lang',
111 config.add_subscriber('rhodecode.subscribers.set_user_lang',
112 'pyramid.events.NewRequest')
112 'pyramid.events.NewRequest')
113 config.add_subscriber('rhodecode.subscribers.add_localizer',
113 config.add_subscriber('rhodecode.subscribers.add_localizer',
114 'pyramid.events.NewRequest')
114 'pyramid.events.NewRequest')
115 config.add_subscriber('rhodecode.subscribers.add_request_user_context',
115 config.add_subscriber('rhodecode.subscribers.add_request_user_context',
116 'pyramid.events.ContextFound')
116 'pyramid.events.ContextFound')
117 config.add_tween('rhodecode.tweens.vcs_detection_tween_factory')
117 config.add_tween('rhodecode.tweens.vcs_detection_tween_factory')
118 config.add_tween('rhodecode.tweens.sanity_check_factory')
118 config.add_tween('rhodecode.tweens.sanity_check_factory')
119
120 # This needs to be the LAST item
121 config.add_tween('rhodecode.lib.middleware.request_wrapper.RequestWrapperTween')
General Comments 0
You need to be logged in to leave comments. Login now