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):