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