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