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