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