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