##// END OF EJS Templates
init: better error reporting on failed components
marcink -
r988:67405995 python3
parent child Browse files
Show More
@@ -1,688 +1,693 b''
1 1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 2 # Copyright (C) 2014-2020 RhodeCode GmbH
3 3 #
4 4 # This program is free software; you can redistribute it and/or modify
5 5 # it under the terms of the GNU General Public License as published by
6 6 # the Free Software Foundation; either version 3 of the License, or
7 7 # (at your option) any later version.
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 General Public License
15 15 # along with this program; if not, write to the Free Software Foundation,
16 16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 17
18 18 import os
19 19 import sys
20 20 import base64
21 21 import locale
22 22 import logging
23 23 import uuid
24 24 import wsgiref.util
25 25 import traceback
26 26 import tempfile
27 27 from itertools import chain
28 28 from io import StringIO
29 29
30 30 import simplejson as json
31 31 import msgpack
32 32 from pyramid.config import Configurator
33 33 from pyramid.settings import asbool, aslist
34 34 from pyramid.wsgi import wsgiapp
35 35 from pyramid.compat import configparser
36 36 from pyramid.response import Response
37 37
38 38 from vcsserver.utils import safe_int
39 39
40 40 log = logging.getLogger(__name__)
41 41
42 42 # due to Mercurial/glibc2.27 problems we need to detect if locale settings are
43 43 # causing problems and "fix" it in case they do and fallback to LC_ALL = C
44 44
45 45 try:
46 46 locale.setlocale(locale.LC_ALL, '')
47 47 except locale.Error as e:
48 48 log.error(
49 49 'LOCALE ERROR: failed to set LC_ALL, fallback to LC_ALL=C, org error: %s', e)
50 50 os.environ['LC_ALL'] = 'C'
51 51
52 52 import vcsserver
53 53 from vcsserver import remote_wsgi, scm_app, settings, hgpatches
54 54 from vcsserver.git_lfs.app import GIT_LFS_CONTENT_TYPE, GIT_LFS_PROTO_PAT
55 55 from vcsserver.echo_stub import remote_wsgi as remote_wsgi_stub
56 56 from vcsserver.echo_stub.echo_app import EchoApp
57 57 from vcsserver.exceptions import HTTPRepoLocked, HTTPRepoBranchProtected
58 58 from vcsserver.lib.exc_tracking import store_exception
59 59 from vcsserver.server import VcsServer
60 60
61
62 git_import_err = None
61 63 try:
62 64 from vcsserver.git import GitFactory, GitRemote
63 except ImportError:
65 except ImportError as e:
64 66 GitFactory = None
65 67 GitRemote = None
68 git_import_err = e
66 69
70 hg_import_err = None
67 71 try:
68 72 from vcsserver.hg import MercurialFactory, HgRemote
69 except ImportError:
73 except ImportError as e:
70 74 MercurialFactory = None
71 75 HgRemote = None
76 hg_import_err = e
72 77
78 svn_import_err = None
73 79 try:
74 80 from vcsserver.svn import SubversionFactory, SvnRemote
75 except ImportError:
81 except ImportError as e:
76 82 SubversionFactory = None
77 83 SvnRemote = None
84 svn_import_err = e
78 85
79 86
80 87 def _is_request_chunked(environ):
81 88 stream = environ.get('HTTP_TRANSFER_ENCODING', '') == 'chunked'
82 89 return stream
83 90
84 91
85 92 def _int_setting(settings, name, default):
86 93 settings[name] = int(settings.get(name, default))
87 94 return settings[name]
88 95
89 96
90 97 def _bool_setting(settings, name, default):
91 98 input_val = settings.get(name, default)
92 99 if isinstance(input_val, str):
93 100 input_val = input_val.encode('utf8')
94 101 settings[name] = asbool(input_val)
95 102 return settings[name]
96 103
97 104
98 105 def _list_setting(settings, name, default):
99 106 raw_value = settings.get(name, default)
100 107
101 108 # Otherwise we assume it uses pyramids space/newline separation.
102 109 settings[name] = aslist(raw_value)
103 110 return settings[name]
104 111
105 112
106 113 def _string_setting(settings, name, default, lower=True, default_when_empty=False):
107 114 value = settings.get(name, default)
108 115
109 116 if default_when_empty and not value:
110 117 # use default value when value is empty
111 118 value = default
112 119
113 120 if lower:
114 121 value = value.lower()
115 122 settings[name] = value
116 123 return settings[name]
117 124
118 125
119 126 class VCS(object):
120 127 def __init__(self, locale_conf=None, cache_config=None):
121 128 self.locale = locale_conf
122 129 self.cache_config = cache_config
123 130 self._configure_locale()
124 131
125 132 if GitFactory and GitRemote:
126 133 git_factory = GitFactory()
127 134 self._git_remote = GitRemote(git_factory)
128 135 else:
129 log.info("Git client import failed")
136 log.error("Git client import failed: %s", git_import_err)
130 137
131 138 if MercurialFactory and HgRemote:
132 139 hg_factory = MercurialFactory()
133 140 self._hg_remote = HgRemote(hg_factory)
134 141 else:
135 log.info("Mercurial client import failed")
142 log.error("Mercurial client import failed: %s", hg_import_err)
136 143
137 144 if SubversionFactory and SvnRemote:
138 145 svn_factory = SubversionFactory()
139 146
140 147 # hg factory is used for svn url validation
141 148 hg_factory = MercurialFactory()
142 149 self._svn_remote = SvnRemote(svn_factory, hg_factory=hg_factory)
143 150 else:
144 log.info("Subversion client import failed")
151 log.error("Subversion client import failed: %s", svn_import_err)
145 152
146 153 self._vcsserver = VcsServer()
147 154
148 155 def _configure_locale(self):
149 156 if self.locale:
150 157 log.info('Settings locale: `LC_ALL` to %s', self.locale)
151 158 else:
152 log.info(
153 'Configuring locale subsystem based on environment variables')
159 log.info('Configuring locale subsystem based on environment variables')
154 160 try:
155 161 # If self.locale is the empty string, then the locale
156 162 # module will use the environment variables. See the
157 163 # documentation of the package `locale`.
158 164 locale.setlocale(locale.LC_ALL, self.locale)
159 165
160 166 language_code, encoding = locale.getlocale()
161 167 log.info(
162 168 'Locale set to language code "%s" with encoding "%s".',
163 169 language_code, encoding)
164 170 except locale.Error:
165 log.exception(
166 'Cannot set locale, not configuring the locale system')
171 log.exception('Cannot set locale, not configuring the locale system')
167 172
168 173
169 174 class WsgiProxy(object):
170 175 def __init__(self, wsgi):
171 176 self.wsgi = wsgi
172 177
173 178 def __call__(self, environ, start_response):
174 179 input_data = environ['wsgi.input'].read()
175 180 input_data = msgpack.unpackb(input_data)
176 181
177 182 error = None
178 183 try:
179 184 data, status, headers = self.wsgi.handle(
180 185 input_data['environment'], input_data['input_data'],
181 186 *input_data['args'], **input_data['kwargs'])
182 187 except Exception as e:
183 188 data, status, headers = [], None, None
184 189 error = {
185 190 'message': str(e),
186 191 '_vcs_kind': getattr(e, '_vcs_kind', None)
187 192 }
188 193
189 194 start_response(200, {})
190 195 return self._iterator(error, status, headers, data)
191 196
192 197 def _iterator(self, error, status, headers, data):
193 198 initial_data = [
194 199 error,
195 200 status,
196 201 headers,
197 202 ]
198 203
199 204 for d in chain(initial_data, data):
200 205 yield msgpack.packb(d)
201 206
202 207
203 208 def not_found(request):
204 209 return {'status': '404 NOT FOUND'}
205 210
206 211
207 212 class VCSViewPredicate(object):
208 213 def __init__(self, val, config):
209 214 self.remotes = val
210 215
211 216 def text(self):
212 217 return 'vcs view method = %s' % (list(self.remotes.keys()),)
213 218
214 219 phash = text
215 220
216 221 def __call__(self, context, request):
217 222 """
218 223 View predicate that returns true if given backend is supported by
219 224 defined remotes.
220 225 """
221 226 backend = request.matchdict.get('backend')
222 227 return backend in self.remotes
223 228
224 229
225 230 class HTTPApplication(object):
226 231 ALLOWED_EXCEPTIONS = ('KeyError', 'URLError')
227 232
228 233 remote_wsgi = remote_wsgi
229 234 _use_echo_app = False
230 235
231 236 def __init__(self, settings=None, global_config=None):
232 237 self._sanitize_settings_and_apply_defaults(settings)
233 238
234 239 self.config = Configurator(settings=settings)
235 240 self.global_config = global_config
236 241 self.config.include('vcsserver.lib.rc_cache')
237 242
238 243 settings_locale = settings.get('locale', '') or 'en_US.UTF-8'
239 244 vcs = VCS(locale_conf=settings_locale, cache_config=settings)
240 245 self._remotes = {
241 246 'hg': vcs._hg_remote,
242 247 'git': vcs._git_remote,
243 248 'svn': vcs._svn_remote,
244 249 'server': vcs._vcsserver,
245 250 }
246 251 if settings.get('dev.use_echo_app', 'false').lower() == 'true':
247 252 self._use_echo_app = True
248 253 log.warning("Using EchoApp for VCS operations.")
249 254 self.remote_wsgi = remote_wsgi_stub
250 255
251 256 self._configure_settings(global_config, settings)
252 257 self._configure()
253 258
254 259 def _configure_settings(self, global_config, app_settings):
255 260 """
256 261 Configure the settings module.
257 262 """
258 263 settings_merged = global_config.copy()
259 264 settings_merged.update(app_settings)
260 265
261 266 git_path = app_settings.get('git_path', None)
262 267 if git_path:
263 268 settings.GIT_EXECUTABLE = git_path
264 269 binary_dir = app_settings.get('core.binary_dir', None)
265 270 if binary_dir:
266 271 settings.BINARY_DIR = binary_dir
267 272
268 273 # Store the settings to make them available to other modules.
269 274 vcsserver.PYRAMID_SETTINGS = settings_merged
270 275 vcsserver.CONFIG = settings_merged
271 276
272 277 def _sanitize_settings_and_apply_defaults(self, settings):
273 278 temp_store = tempfile.gettempdir()
274 279 default_cache_dir = os.path.join(temp_store, 'rc_cache')
275 280
276 281 # save default, cache dir, and use it for all backends later.
277 282 default_cache_dir = _string_setting(
278 283 settings,
279 284 'cache_dir',
280 285 default_cache_dir, lower=False, default_when_empty=True)
281 286
282 287 # ensure we have our dir created
283 288 if not os.path.isdir(default_cache_dir):
284 289 os.makedirs(default_cache_dir, mode=0o755)
285 290
286 291 # exception store cache
287 292 _string_setting(
288 293 settings,
289 294 'exception_tracker.store_path',
290 295 temp_store, lower=False, default_when_empty=True)
291 296
292 297 # repo_object cache
293 298 _string_setting(
294 299 settings,
295 300 'rc_cache.repo_object.backend',
296 301 'dogpile.cache.rc.file_namespace', lower=False)
297 302 _int_setting(
298 303 settings,
299 304 'rc_cache.repo_object.expiration_time',
300 305 30 * 24 * 60 * 60)
301 306 _string_setting(
302 307 settings,
303 308 'rc_cache.repo_object.arguments.filename',
304 309 os.path.join(default_cache_dir, 'vcsserver_cache_1'), lower=False)
305 310
306 311 def _configure(self):
307 312 self.config.add_renderer(name='msgpack', factory=self._msgpack_renderer_factory)
308 313
309 314 self.config.add_route('service', '/_service')
310 315 self.config.add_route('status', '/status')
311 316 self.config.add_route('hg_proxy', '/proxy/hg')
312 317 self.config.add_route('git_proxy', '/proxy/git')
313 318
314 319 # rpc methods
315 320 self.config.add_route('vcs', '/{backend}')
316 321
317 322 # streaming rpc remote methods
318 323 self.config.add_route('vcs_stream', '/{backend}/stream')
319 324
320 325 # vcs operations clone/push as streaming
321 326 self.config.add_route('stream_git', '/stream/git/*repo_name')
322 327 self.config.add_route('stream_hg', '/stream/hg/*repo_name')
323 328
324 329 self.config.add_view(self.status_view, route_name='status', renderer='json')
325 330 self.config.add_view(self.service_view, route_name='service', renderer='msgpack')
326 331
327 332 self.config.add_view(self.hg_proxy(), route_name='hg_proxy')
328 333 self.config.add_view(self.git_proxy(), route_name='git_proxy')
329 334 self.config.add_view(self.vcs_view, route_name='vcs', renderer='msgpack',
330 335 vcs_view=self._remotes)
331 336 self.config.add_view(self.vcs_stream_view, route_name='vcs_stream',
332 337 vcs_view=self._remotes)
333 338
334 339 self.config.add_view(self.hg_stream(), route_name='stream_hg')
335 340 self.config.add_view(self.git_stream(), route_name='stream_git')
336 341
337 342 self.config.add_view_predicate('vcs_view', VCSViewPredicate)
338 343
339 344 self.config.add_notfound_view(not_found, renderer='json')
340 345
341 346 self.config.add_view(self.handle_vcs_exception, context=Exception)
342 347
343 348 self.config.add_tween(
344 349 'vcsserver.tweens.request_wrapper.RequestWrapperTween',
345 350 )
346 351 self.config.add_request_method(
347 352 'vcsserver.lib.request_counter.get_request_counter',
348 353 'request_count')
349 354
350 355 def wsgi_app(self):
351 356 return self.config.make_wsgi_app()
352 357
353 358 def _vcs_view_params(self, request):
354 359 remote = self._remotes[request.matchdict['backend']]
355 360 payload = msgpack.unpackb(request.body, use_list=True)
356 361 method = payload.get('method')
357 362 params = payload['params']
358 363 wire = params.get('wire')
359 364 args = params.get('args')
360 365 kwargs = params.get('kwargs')
361 366 context_uid = None
362 367
363 368 if wire:
364 369 try:
365 370 wire['context'] = context_uid = uuid.UUID(wire['context'])
366 371 except KeyError:
367 372 pass
368 373 args.insert(0, wire)
369 374 repo_state_uid = wire.get('repo_state_uid') if wire else None
370 375
371 376 # NOTE(marcink): trading complexity for slight performance
372 377 if log.isEnabledFor(logging.DEBUG):
373 378 no_args_methods = [
374 379 'archive_repo'
375 380 ]
376 381 if method in no_args_methods:
377 382 call_args = ''
378 383 else:
379 384 call_args = args[1:]
380 385
381 386 log.debug('method requested:%s with args:%s kwargs:%s context_uid: %s, repo_state_uid:%s',
382 387 method, call_args, kwargs, context_uid, repo_state_uid)
383 388
384 389 return payload, remote, method, args, kwargs
385 390
386 391 def vcs_view(self, request):
387 392
388 393 payload, remote, method, args, kwargs = self._vcs_view_params(request)
389 394 payload_id = payload.get('id')
390 395
391 396 try:
392 397 resp = getattr(remote, method)(*args, **kwargs)
393 398 except Exception as e:
394 399 exc_info = list(sys.exc_info())
395 400 exc_type, exc_value, exc_traceback = exc_info
396 401
397 402 org_exc = getattr(e, '_org_exc', None)
398 403 org_exc_name = None
399 404 org_exc_tb = ''
400 405 if org_exc:
401 406 org_exc_name = org_exc.__class__.__name__
402 407 org_exc_tb = getattr(e, '_org_exc_tb', '')
403 408 # replace our "faked" exception with our org
404 409 exc_info[0] = org_exc.__class__
405 410 exc_info[1] = org_exc
406 411
407 412 should_store_exc = True
408 413 if org_exc:
409 414 def get_exc_fqn(_exc_obj):
410 415 module_name = getattr(org_exc.__class__, '__module__', 'UNKNOWN')
411 416 return module_name + '.' + org_exc_name
412 417
413 418 exc_fqn = get_exc_fqn(org_exc)
414 419
415 420 if exc_fqn in ['mercurial.error.RepoLookupError',
416 421 'vcsserver.exceptions.RefNotFoundException']:
417 422 should_store_exc = False
418 423
419 424 if should_store_exc:
420 425 store_exception(id(exc_info), exc_info)
421 426
422 427 tb_info = ''.join(
423 428 traceback.format_exception(exc_type, exc_value, exc_traceback))
424 429
425 430 type_ = e.__class__.__name__
426 431 if type_ not in self.ALLOWED_EXCEPTIONS:
427 432 type_ = None
428 433
429 434 resp = {
430 435 'id': payload_id,
431 436 'error': {
432 437 'message': e.message,
433 438 'traceback': tb_info,
434 439 'org_exc': org_exc_name,
435 440 'org_exc_tb': org_exc_tb,
436 441 'type': type_
437 442 }
438 443 }
439 444 try:
440 445 resp['error']['_vcs_kind'] = getattr(e, '_vcs_kind', None)
441 446 except AttributeError:
442 447 pass
443 448 else:
444 449 resp = {
445 450 'id': payload_id,
446 451 'result': resp
447 452 }
448 453
449 454 return resp
450 455
451 456 def vcs_stream_view(self, request):
452 457 payload, remote, method, args, kwargs = self._vcs_view_params(request)
453 458 # this method has a stream: marker we remove it here
454 459 method = method.split('stream:')[-1]
455 460 chunk_size = safe_int(payload.get('chunk_size')) or 4096
456 461
457 462 try:
458 463 resp = getattr(remote, method)(*args, **kwargs)
459 464 except Exception as e:
460 465 raise
461 466
462 467 def get_chunked_data(method_resp):
463 468 stream = StringIO(method_resp)
464 469 while 1:
465 470 chunk = stream.read(chunk_size)
466 471 if not chunk:
467 472 break
468 473 yield chunk
469 474
470 475 response = Response(app_iter=get_chunked_data(resp))
471 476 response.content_type = 'application/octet-stream'
472 477
473 478 return response
474 479
475 480 def status_view(self, request):
476 481 import vcsserver
477 482 return {'status': 'OK', 'vcsserver_version': vcsserver.__version__,
478 483 'pid': os.getpid()}
479 484
480 485 def service_view(self, request):
481 486 import vcsserver
482 487
483 488 payload = msgpack.unpackb(request.body, use_list=True)
484 489 server_config, app_config = {}, {}
485 490
486 491 try:
487 492 path = self.global_config['__file__']
488 493 config = configparser.RawConfigParser()
489 494
490 495 config.read(path)
491 496
492 497 if config.has_section('server:main'):
493 498 server_config = dict(config.items('server:main'))
494 499 if config.has_section('app:main'):
495 500 app_config = dict(config.items('app:main'))
496 501
497 502 except Exception:
498 503 log.exception('Failed to read .ini file for display')
499 504
500 505 environ = list(os.environ.items())
501 506
502 507 resp = {
503 508 'id': payload.get('id'),
504 509 'result': dict(
505 510 version=vcsserver.__version__,
506 511 config=server_config,
507 512 app_config=app_config,
508 513 environ=environ,
509 514 payload=payload,
510 515 )
511 516 }
512 517 return resp
513 518
514 519 def _msgpack_renderer_factory(self, info):
515 520 def _render(value, system):
516 521 request = system.get('request')
517 522 if request is not None:
518 523 response = request.response
519 524 ct = response.content_type
520 525 if ct == response.default_content_type:
521 526 response.content_type = 'application/x-msgpack'
522 527 return msgpack.packb(value)
523 528 return _render
524 529
525 530 def set_env_from_config(self, environ, config):
526 531 dict_conf = {}
527 532 try:
528 533 for elem in config:
529 534 if elem[0] == 'rhodecode':
530 535 dict_conf = json.loads(elem[2])
531 536 break
532 537 except Exception:
533 538 log.exception('Failed to fetch SCM CONFIG')
534 539 return
535 540
536 541 username = dict_conf.get('username')
537 542 if username:
538 543 environ['REMOTE_USER'] = username
539 544 # mercurial specific, some extension api rely on this
540 545 environ['HGUSER'] = username
541 546
542 547 ip = dict_conf.get('ip')
543 548 if ip:
544 549 environ['REMOTE_HOST'] = ip
545 550
546 551 if _is_request_chunked(environ):
547 552 # set the compatibility flag for webob
548 553 environ['wsgi.input_terminated'] = True
549 554
550 555 def hg_proxy(self):
551 556 @wsgiapp
552 557 def _hg_proxy(environ, start_response):
553 558 app = WsgiProxy(self.remote_wsgi.HgRemoteWsgi())
554 559 return app(environ, start_response)
555 560 return _hg_proxy
556 561
557 562 def git_proxy(self):
558 563 @wsgiapp
559 564 def _git_proxy(environ, start_response):
560 565 app = WsgiProxy(self.remote_wsgi.GitRemoteWsgi())
561 566 return app(environ, start_response)
562 567 return _git_proxy
563 568
564 569 def hg_stream(self):
565 570 if self._use_echo_app:
566 571 @wsgiapp
567 572 def _hg_stream(environ, start_response):
568 573 app = EchoApp('fake_path', 'fake_name', None)
569 574 return app(environ, start_response)
570 575 return _hg_stream
571 576 else:
572 577 @wsgiapp
573 578 def _hg_stream(environ, start_response):
574 579 log.debug('http-app: handling hg stream')
575 580 repo_path = environ['HTTP_X_RC_REPO_PATH']
576 581 repo_name = environ['HTTP_X_RC_REPO_NAME']
577 582 packed_config = base64.b64decode(
578 583 environ['HTTP_X_RC_REPO_CONFIG'])
579 584 config = msgpack.unpackb(packed_config)
580 585 app = scm_app.create_hg_wsgi_app(
581 586 repo_path, repo_name, config)
582 587
583 588 # Consistent path information for hgweb
584 589 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
585 590 environ['REPO_NAME'] = repo_name
586 591 self.set_env_from_config(environ, config)
587 592
588 593 log.debug('http-app: starting app handler '
589 594 'with %s and process request', app)
590 595 return app(environ, ResponseFilter(start_response))
591 596 return _hg_stream
592 597
593 598 def git_stream(self):
594 599 if self._use_echo_app:
595 600 @wsgiapp
596 601 def _git_stream(environ, start_response):
597 602 app = EchoApp('fake_path', 'fake_name', None)
598 603 return app(environ, start_response)
599 604 return _git_stream
600 605 else:
601 606 @wsgiapp
602 607 def _git_stream(environ, start_response):
603 608 log.debug('http-app: handling git stream')
604 609 repo_path = environ['HTTP_X_RC_REPO_PATH']
605 610 repo_name = environ['HTTP_X_RC_REPO_NAME']
606 611 packed_config = base64.b64decode(
607 612 environ['HTTP_X_RC_REPO_CONFIG'])
608 613 config = msgpack.unpackb(packed_config)
609 614
610 615 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
611 616 self.set_env_from_config(environ, config)
612 617
613 618 content_type = environ.get('CONTENT_TYPE', '')
614 619
615 620 path = environ['PATH_INFO']
616 621 is_lfs_request = GIT_LFS_CONTENT_TYPE in content_type
617 622 log.debug(
618 623 'LFS: Detecting if request `%s` is LFS server path based '
619 624 'on content type:`%s`, is_lfs:%s',
620 625 path, content_type, is_lfs_request)
621 626
622 627 if not is_lfs_request:
623 628 # fallback detection by path
624 629 if GIT_LFS_PROTO_PAT.match(path):
625 630 is_lfs_request = True
626 631 log.debug(
627 632 'LFS: fallback detection by path of: `%s`, is_lfs:%s',
628 633 path, is_lfs_request)
629 634
630 635 if is_lfs_request:
631 636 app = scm_app.create_git_lfs_wsgi_app(
632 637 repo_path, repo_name, config)
633 638 else:
634 639 app = scm_app.create_git_wsgi_app(
635 640 repo_path, repo_name, config)
636 641
637 642 log.debug('http-app: starting app handler '
638 643 'with %s and process request', app)
639 644
640 645 return app(environ, start_response)
641 646
642 647 return _git_stream
643 648
644 649 def handle_vcs_exception(self, exception, request):
645 650 _vcs_kind = getattr(exception, '_vcs_kind', '')
646 651 if _vcs_kind == 'repo_locked':
647 652 # Get custom repo-locked status code if present.
648 653 status_code = request.headers.get('X-RC-Locked-Status-Code')
649 654 return HTTPRepoLocked(
650 655 title=exception.message, status_code=status_code)
651 656
652 657 elif _vcs_kind == 'repo_branch_protected':
653 658 # Get custom repo-branch-protected status code if present.
654 659 return HTTPRepoBranchProtected(title=exception.message)
655 660
656 661 exc_info = request.exc_info
657 662 store_exception(id(exc_info), exc_info)
658 663
659 664 traceback_info = 'unavailable'
660 665 if request.exc_info:
661 666 exc_type, exc_value, exc_tb = request.exc_info
662 667 traceback_info = ''.join(traceback.format_exception(exc_type, exc_value, exc_tb))
663 668
664 669 log.error(
665 670 'error occurred handling this request for path: %s, \n tb: %s',
666 671 request.path, traceback_info)
667 672 raise exception
668 673
669 674
670 675 class ResponseFilter(object):
671 676
672 677 def __init__(self, start_response):
673 678 self._start_response = start_response
674 679
675 680 def __call__(self, status, response_headers, exc_info=None):
676 681 headers = tuple(
677 682 (h, v) for h, v in response_headers
678 683 if not wsgiref.util.is_hop_by_hop(h))
679 684 return self._start_response(status, headers, exc_info)
680 685
681 686
682 687 def main(global_config, **settings):
683 688 if MercurialFactory:
684 689 hgpatches.patch_largefiles_capabilities()
685 690 hgpatches.patch_subrepo_type_mapping()
686 691
687 692 app = HTTPApplication(settings=settings, global_config=global_config)
688 693 return app.wsgi_app()
General Comments 0
You need to be logged in to leave comments. Login now