# HG changeset patch # User Martin Bornhold # Date 2016-10-10 14:03:00 # Node ID a0c3f57b544fa40cb89dae44d01300a3367bad09 # Parent 5635f4b8ec2fd4c4fa8f34af9f7c4892781f36c6 http: Add error handling for the repo-locked exception. Part of #4237 Previously the repo locekd exception was raised on return of the call to the hooks daemon and propagates all the way up to the WSGI server (e.g. waitress). In case of locked repos we want to return a custom response which contains an explanation for the user that the repo is locked and who has locked it. diff --git a/vcsserver/exceptions.py b/vcsserver/exceptions.py --- a/vcsserver/exceptions.py +++ b/vcsserver/exceptions.py @@ -25,6 +25,7 @@ different error conditions. """ import functools +from pyramid.httpexceptions import HTTPLocked def _make_exception(kind, *args): @@ -54,3 +55,14 @@ RequirementException = functools.partial UnhandledException = functools.partial(_make_exception, 'unhandled') URLError = functools.partial(_make_exception, 'url_error') + + +class HTTPRepoLocked(HTTPLocked): + """ + Subclass of HTTPLocked response that allows to set the title and status + code via constructor arguments. + """ + def __init__(self, title, status_code=None, **kwargs): + self.code = status_code or HTTPLocked.code + self.title = title + super(HTTPRepoLocked, self).__init__(**kwargs) diff --git a/vcsserver/http_main.py b/vcsserver/http_main.py --- a/vcsserver/http_main.py +++ b/vcsserver/http_main.py @@ -31,6 +31,7 @@ from pyramid.wsgi import wsgiapp from vcsserver import remote_wsgi, scm_app, settings, hgpatches from vcsserver.echo_stub import remote_wsgi as remote_wsgi_stub from vcsserver.echo_stub.echo_app import EchoApp +from vcsserver.exceptions import HTTPRepoLocked from vcsserver.server import VcsServer try: @@ -197,6 +198,9 @@ class HTTPApplication(object): self.config.add_view(self.hg_stream(), route_name='stream_hg') self.config.add_view(self.git_stream(), route_name='stream_git') + self.config.add_view( + self.handle_vcs_exception, context=Exception, + custom_predicates=[self.is_vcs_exception]) def wsgi_app(self): return self.config.make_wsgi_app() @@ -317,6 +321,23 @@ class HTTPApplication(object): return app(environ, start_response) return _git_stream + def is_vcs_exception(self, context, request): + """ + View predicate that returns true if the context object is a VCS + exception. + """ + return hasattr(context, '_vcs_kind') + + def handle_vcs_exception(self, exception, request): + if exception._vcs_kind == 'repo_locked': + # Get custom repo-locked status code if present. + status_code = request.headers.get('X-RC-Locked-Status-Code') + return HTTPRepoLocked( + title=exception.message, status_code=status_code) + + # Re-raise exception if we can not handle it. + raise exception + class ResponseFilter(object):