##// END OF EJS Templates
http: Add error handling for the repo-locked exception. Part of #4237...
Martin Bornhold -
r85:a0c3f57b default
parent child Browse files
Show More
@@ -1,56 +1,68 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2016 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
7 # (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
18 """
18 """
19 Special exception handling over the wire.
19 Special exception handling over the wire.
20
20
21 Since we cannot assume that our client is able to import our exception classes,
21 Since we cannot assume that our client is able to import our exception classes,
22 this module provides a "wrapping" mechanism to raise plain exceptions
22 this module provides a "wrapping" mechanism to raise plain exceptions
23 which contain an extra attribute `_vcs_kind` to allow a client to distinguish
23 which contain an extra attribute `_vcs_kind` to allow a client to distinguish
24 different error conditions.
24 different error conditions.
25 """
25 """
26
26
27 import functools
27 import functools
28 from pyramid.httpexceptions import HTTPLocked
28
29
29
30
30 def _make_exception(kind, *args):
31 def _make_exception(kind, *args):
31 """
32 """
32 Prepares a base `Exception` instance to be sent over the wire.
33 Prepares a base `Exception` instance to be sent over the wire.
33
34
34 To give our caller a hint what this is about, it will attach an attribute
35 To give our caller a hint what this is about, it will attach an attribute
35 `_vcs_kind` to the exception.
36 `_vcs_kind` to the exception.
36 """
37 """
37 exc = Exception(*args)
38 exc = Exception(*args)
38 exc._vcs_kind = kind
39 exc._vcs_kind = kind
39 return exc
40 return exc
40
41
41
42
42 AbortException = functools.partial(_make_exception, 'abort')
43 AbortException = functools.partial(_make_exception, 'abort')
43
44
44 ArchiveException = functools.partial(_make_exception, 'archive')
45 ArchiveException = functools.partial(_make_exception, 'archive')
45
46
46 LookupException = functools.partial(_make_exception, 'lookup')
47 LookupException = functools.partial(_make_exception, 'lookup')
47
48
48 VcsException = functools.partial(_make_exception, 'error')
49 VcsException = functools.partial(_make_exception, 'error')
49
50
50 RepositoryLockedException = functools.partial(_make_exception, 'repo_locked')
51 RepositoryLockedException = functools.partial(_make_exception, 'repo_locked')
51
52
52 RequirementException = functools.partial(_make_exception, 'requirement')
53 RequirementException = functools.partial(_make_exception, 'requirement')
53
54
54 UnhandledException = functools.partial(_make_exception, 'unhandled')
55 UnhandledException = functools.partial(_make_exception, 'unhandled')
55
56
56 URLError = functools.partial(_make_exception, 'url_error')
57 URLError = functools.partial(_make_exception, 'url_error')
58
59
60 class HTTPRepoLocked(HTTPLocked):
61 """
62 Subclass of HTTPLocked response that allows to set the title and status
63 code via constructor arguments.
64 """
65 def __init__(self, title, status_code=None, **kwargs):
66 self.code = status_code or HTTPLocked.code
67 self.title = title
68 super(HTTPRepoLocked, self).__init__(**kwargs)
@@ -1,337 +1,358 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2016 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
7 # (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
18 import base64
18 import base64
19 import locale
19 import locale
20 import logging
20 import logging
21 import uuid
21 import uuid
22 import wsgiref.util
22 import wsgiref.util
23 from itertools import chain
23 from itertools import chain
24
24
25 import msgpack
25 import msgpack
26 from beaker.cache import CacheManager
26 from beaker.cache import CacheManager
27 from beaker.util import parse_cache_config_options
27 from beaker.util import parse_cache_config_options
28 from pyramid.config import Configurator
28 from pyramid.config import Configurator
29 from pyramid.wsgi import wsgiapp
29 from pyramid.wsgi import wsgiapp
30
30
31 from vcsserver import remote_wsgi, scm_app, settings, hgpatches
31 from vcsserver import remote_wsgi, scm_app, settings, hgpatches
32 from vcsserver.echo_stub import remote_wsgi as remote_wsgi_stub
32 from vcsserver.echo_stub import remote_wsgi as remote_wsgi_stub
33 from vcsserver.echo_stub.echo_app import EchoApp
33 from vcsserver.echo_stub.echo_app import EchoApp
34 from vcsserver.exceptions import HTTPRepoLocked
34 from vcsserver.server import VcsServer
35 from vcsserver.server import VcsServer
35
36
36 try:
37 try:
37 from vcsserver.git import GitFactory, GitRemote
38 from vcsserver.git import GitFactory, GitRemote
38 except ImportError:
39 except ImportError:
39 GitFactory = None
40 GitFactory = None
40 GitRemote = None
41 GitRemote = None
41 try:
42 try:
42 from vcsserver.hg import MercurialFactory, HgRemote
43 from vcsserver.hg import MercurialFactory, HgRemote
43 except ImportError:
44 except ImportError:
44 MercurialFactory = None
45 MercurialFactory = None
45 HgRemote = None
46 HgRemote = None
46 try:
47 try:
47 from vcsserver.svn import SubversionFactory, SvnRemote
48 from vcsserver.svn import SubversionFactory, SvnRemote
48 except ImportError:
49 except ImportError:
49 SubversionFactory = None
50 SubversionFactory = None
50 SvnRemote = None
51 SvnRemote = None
51
52
52 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
53
54
54
55
55 class VCS(object):
56 class VCS(object):
56 def __init__(self, locale=None, cache_config=None):
57 def __init__(self, locale=None, cache_config=None):
57 self.locale = locale
58 self.locale = locale
58 self.cache_config = cache_config
59 self.cache_config = cache_config
59 self._configure_locale()
60 self._configure_locale()
60 self._initialize_cache()
61 self._initialize_cache()
61
62
62 if GitFactory and GitRemote:
63 if GitFactory and GitRemote:
63 git_repo_cache = self.cache.get_cache_region(
64 git_repo_cache = self.cache.get_cache_region(
64 'git', region='repo_object')
65 'git', region='repo_object')
65 git_factory = GitFactory(git_repo_cache)
66 git_factory = GitFactory(git_repo_cache)
66 self._git_remote = GitRemote(git_factory)
67 self._git_remote = GitRemote(git_factory)
67 else:
68 else:
68 log.info("Git client import failed")
69 log.info("Git client import failed")
69
70
70 if MercurialFactory and HgRemote:
71 if MercurialFactory and HgRemote:
71 hg_repo_cache = self.cache.get_cache_region(
72 hg_repo_cache = self.cache.get_cache_region(
72 'hg', region='repo_object')
73 'hg', region='repo_object')
73 hg_factory = MercurialFactory(hg_repo_cache)
74 hg_factory = MercurialFactory(hg_repo_cache)
74 self._hg_remote = HgRemote(hg_factory)
75 self._hg_remote = HgRemote(hg_factory)
75 else:
76 else:
76 log.info("Mercurial client import failed")
77 log.info("Mercurial client import failed")
77
78
78 if SubversionFactory and SvnRemote:
79 if SubversionFactory and SvnRemote:
79 svn_repo_cache = self.cache.get_cache_region(
80 svn_repo_cache = self.cache.get_cache_region(
80 'svn', region='repo_object')
81 'svn', region='repo_object')
81 svn_factory = SubversionFactory(svn_repo_cache)
82 svn_factory = SubversionFactory(svn_repo_cache)
82 self._svn_remote = SvnRemote(svn_factory, hg_factory=hg_factory)
83 self._svn_remote = SvnRemote(svn_factory, hg_factory=hg_factory)
83 else:
84 else:
84 log.info("Subversion client import failed")
85 log.info("Subversion client import failed")
85
86
86 self._vcsserver = VcsServer()
87 self._vcsserver = VcsServer()
87
88
88 def _initialize_cache(self):
89 def _initialize_cache(self):
89 cache_config = parse_cache_config_options(self.cache_config)
90 cache_config = parse_cache_config_options(self.cache_config)
90 log.info('Initializing beaker cache: %s' % cache_config)
91 log.info('Initializing beaker cache: %s' % cache_config)
91 self.cache = CacheManager(**cache_config)
92 self.cache = CacheManager(**cache_config)
92
93
93 def _configure_locale(self):
94 def _configure_locale(self):
94 if self.locale:
95 if self.locale:
95 log.info('Settings locale: `LC_ALL` to %s' % self.locale)
96 log.info('Settings locale: `LC_ALL` to %s' % self.locale)
96 else:
97 else:
97 log.info(
98 log.info(
98 'Configuring locale subsystem based on environment variables')
99 'Configuring locale subsystem based on environment variables')
99 try:
100 try:
100 # If self.locale is the empty string, then the locale
101 # If self.locale is the empty string, then the locale
101 # module will use the environment variables. See the
102 # module will use the environment variables. See the
102 # documentation of the package `locale`.
103 # documentation of the package `locale`.
103 locale.setlocale(locale.LC_ALL, self.locale)
104 locale.setlocale(locale.LC_ALL, self.locale)
104
105
105 language_code, encoding = locale.getlocale()
106 language_code, encoding = locale.getlocale()
106 log.info(
107 log.info(
107 'Locale set to language code "%s" with encoding "%s".',
108 'Locale set to language code "%s" with encoding "%s".',
108 language_code, encoding)
109 language_code, encoding)
109 except locale.Error:
110 except locale.Error:
110 log.exception(
111 log.exception(
111 'Cannot set locale, not configuring the locale system')
112 'Cannot set locale, not configuring the locale system')
112
113
113
114
114 class WsgiProxy(object):
115 class WsgiProxy(object):
115 def __init__(self, wsgi):
116 def __init__(self, wsgi):
116 self.wsgi = wsgi
117 self.wsgi = wsgi
117
118
118 def __call__(self, environ, start_response):
119 def __call__(self, environ, start_response):
119 input_data = environ['wsgi.input'].read()
120 input_data = environ['wsgi.input'].read()
120 input_data = msgpack.unpackb(input_data)
121 input_data = msgpack.unpackb(input_data)
121
122
122 error = None
123 error = None
123 try:
124 try:
124 data, status, headers = self.wsgi.handle(
125 data, status, headers = self.wsgi.handle(
125 input_data['environment'], input_data['input_data'],
126 input_data['environment'], input_data['input_data'],
126 *input_data['args'], **input_data['kwargs'])
127 *input_data['args'], **input_data['kwargs'])
127 except Exception as e:
128 except Exception as e:
128 data, status, headers = [], None, None
129 data, status, headers = [], None, None
129 error = {
130 error = {
130 'message': str(e),
131 'message': str(e),
131 '_vcs_kind': getattr(e, '_vcs_kind', None)
132 '_vcs_kind': getattr(e, '_vcs_kind', None)
132 }
133 }
133
134
134 start_response(200, {})
135 start_response(200, {})
135 return self._iterator(error, status, headers, data)
136 return self._iterator(error, status, headers, data)
136
137
137 def _iterator(self, error, status, headers, data):
138 def _iterator(self, error, status, headers, data):
138 initial_data = [
139 initial_data = [
139 error,
140 error,
140 status,
141 status,
141 headers,
142 headers,
142 ]
143 ]
143
144
144 for d in chain(initial_data, data):
145 for d in chain(initial_data, data):
145 yield msgpack.packb(d)
146 yield msgpack.packb(d)
146
147
147
148
148 class HTTPApplication(object):
149 class HTTPApplication(object):
149 ALLOWED_EXCEPTIONS = ('KeyError', 'URLError')
150 ALLOWED_EXCEPTIONS = ('KeyError', 'URLError')
150
151
151 remote_wsgi = remote_wsgi
152 remote_wsgi = remote_wsgi
152 _use_echo_app = False
153 _use_echo_app = False
153
154
154 def __init__(self, settings=None):
155 def __init__(self, settings=None):
155 self.config = Configurator(settings=settings)
156 self.config = Configurator(settings=settings)
156 locale = settings.get('', 'en_US.UTF-8')
157 locale = settings.get('', 'en_US.UTF-8')
157 vcs = VCS(locale=locale, cache_config=settings)
158 vcs = VCS(locale=locale, cache_config=settings)
158 self._remotes = {
159 self._remotes = {
159 'hg': vcs._hg_remote,
160 'hg': vcs._hg_remote,
160 'git': vcs._git_remote,
161 'git': vcs._git_remote,
161 'svn': vcs._svn_remote,
162 'svn': vcs._svn_remote,
162 'server': vcs._vcsserver,
163 'server': vcs._vcsserver,
163 }
164 }
164 if settings.get('dev.use_echo_app', 'false').lower() == 'true':
165 if settings.get('dev.use_echo_app', 'false').lower() == 'true':
165 self._use_echo_app = True
166 self._use_echo_app = True
166 log.warning("Using EchoApp for VCS operations.")
167 log.warning("Using EchoApp for VCS operations.")
167 self.remote_wsgi = remote_wsgi_stub
168 self.remote_wsgi = remote_wsgi_stub
168 self._configure_settings(settings)
169 self._configure_settings(settings)
169 self._configure()
170 self._configure()
170
171
171 def _configure_settings(self, app_settings):
172 def _configure_settings(self, app_settings):
172 """
173 """
173 Configure the settings module.
174 Configure the settings module.
174 """
175 """
175 git_path = app_settings.get('git_path', None)
176 git_path = app_settings.get('git_path', None)
176 if git_path:
177 if git_path:
177 settings.GIT_EXECUTABLE = git_path
178 settings.GIT_EXECUTABLE = git_path
178
179
179 def _configure(self):
180 def _configure(self):
180 self.config.add_renderer(
181 self.config.add_renderer(
181 name='msgpack',
182 name='msgpack',
182 factory=self._msgpack_renderer_factory)
183 factory=self._msgpack_renderer_factory)
183
184
184 self.config.add_route('status', '/status')
185 self.config.add_route('status', '/status')
185 self.config.add_route('hg_proxy', '/proxy/hg')
186 self.config.add_route('hg_proxy', '/proxy/hg')
186 self.config.add_route('git_proxy', '/proxy/git')
187 self.config.add_route('git_proxy', '/proxy/git')
187 self.config.add_route('vcs', '/{backend}')
188 self.config.add_route('vcs', '/{backend}')
188 self.config.add_route('stream_git', '/stream/git/*repo_name')
189 self.config.add_route('stream_git', '/stream/git/*repo_name')
189 self.config.add_route('stream_hg', '/stream/hg/*repo_name')
190 self.config.add_route('stream_hg', '/stream/hg/*repo_name')
190
191
191 self.config.add_view(
192 self.config.add_view(
192 self.status_view, route_name='status', renderer='json')
193 self.status_view, route_name='status', renderer='json')
193 self.config.add_view(self.hg_proxy(), route_name='hg_proxy')
194 self.config.add_view(self.hg_proxy(), route_name='hg_proxy')
194 self.config.add_view(self.git_proxy(), route_name='git_proxy')
195 self.config.add_view(self.git_proxy(), route_name='git_proxy')
195 self.config.add_view(
196 self.config.add_view(
196 self.vcs_view, route_name='vcs', renderer='msgpack')
197 self.vcs_view, route_name='vcs', renderer='msgpack')
197
198
198 self.config.add_view(self.hg_stream(), route_name='stream_hg')
199 self.config.add_view(self.hg_stream(), route_name='stream_hg')
199 self.config.add_view(self.git_stream(), route_name='stream_git')
200 self.config.add_view(self.git_stream(), route_name='stream_git')
201 self.config.add_view(
202 self.handle_vcs_exception, context=Exception,
203 custom_predicates=[self.is_vcs_exception])
200
204
201 def wsgi_app(self):
205 def wsgi_app(self):
202 return self.config.make_wsgi_app()
206 return self.config.make_wsgi_app()
203
207
204 def vcs_view(self, request):
208 def vcs_view(self, request):
205 remote = self._remotes[request.matchdict['backend']]
209 remote = self._remotes[request.matchdict['backend']]
206 payload = msgpack.unpackb(request.body, use_list=True)
210 payload = msgpack.unpackb(request.body, use_list=True)
207 method = payload.get('method')
211 method = payload.get('method')
208 params = payload.get('params')
212 params = payload.get('params')
209 wire = params.get('wire')
213 wire = params.get('wire')
210 args = params.get('args')
214 args = params.get('args')
211 kwargs = params.get('kwargs')
215 kwargs = params.get('kwargs')
212 if wire:
216 if wire:
213 try:
217 try:
214 wire['context'] = uuid.UUID(wire['context'])
218 wire['context'] = uuid.UUID(wire['context'])
215 except KeyError:
219 except KeyError:
216 pass
220 pass
217 args.insert(0, wire)
221 args.insert(0, wire)
218
222
219 try:
223 try:
220 resp = getattr(remote, method)(*args, **kwargs)
224 resp = getattr(remote, method)(*args, **kwargs)
221 except Exception as e:
225 except Exception as e:
222 type_ = e.__class__.__name__
226 type_ = e.__class__.__name__
223 if type_ not in self.ALLOWED_EXCEPTIONS:
227 if type_ not in self.ALLOWED_EXCEPTIONS:
224 type_ = None
228 type_ = None
225
229
226 resp = {
230 resp = {
227 'id': payload.get('id'),
231 'id': payload.get('id'),
228 'error': {
232 'error': {
229 'message': e.message,
233 'message': e.message,
230 'type': type_
234 'type': type_
231 }
235 }
232 }
236 }
233 try:
237 try:
234 resp['error']['_vcs_kind'] = e._vcs_kind
238 resp['error']['_vcs_kind'] = e._vcs_kind
235 except AttributeError:
239 except AttributeError:
236 pass
240 pass
237 else:
241 else:
238 resp = {
242 resp = {
239 'id': payload.get('id'),
243 'id': payload.get('id'),
240 'result': resp
244 'result': resp
241 }
245 }
242
246
243 return resp
247 return resp
244
248
245 def status_view(self, request):
249 def status_view(self, request):
246 return {'status': 'OK'}
250 return {'status': 'OK'}
247
251
248 def _msgpack_renderer_factory(self, info):
252 def _msgpack_renderer_factory(self, info):
249 def _render(value, system):
253 def _render(value, system):
250 value = msgpack.packb(value)
254 value = msgpack.packb(value)
251 request = system.get('request')
255 request = system.get('request')
252 if request is not None:
256 if request is not None:
253 response = request.response
257 response = request.response
254 ct = response.content_type
258 ct = response.content_type
255 if ct == response.default_content_type:
259 if ct == response.default_content_type:
256 response.content_type = 'application/x-msgpack'
260 response.content_type = 'application/x-msgpack'
257 return value
261 return value
258 return _render
262 return _render
259
263
260 def hg_proxy(self):
264 def hg_proxy(self):
261 @wsgiapp
265 @wsgiapp
262 def _hg_proxy(environ, start_response):
266 def _hg_proxy(environ, start_response):
263 app = WsgiProxy(self.remote_wsgi.HgRemoteWsgi())
267 app = WsgiProxy(self.remote_wsgi.HgRemoteWsgi())
264 return app(environ, start_response)
268 return app(environ, start_response)
265 return _hg_proxy
269 return _hg_proxy
266
270
267 def git_proxy(self):
271 def git_proxy(self):
268 @wsgiapp
272 @wsgiapp
269 def _git_proxy(environ, start_response):
273 def _git_proxy(environ, start_response):
270 app = WsgiProxy(self.remote_wsgi.GitRemoteWsgi())
274 app = WsgiProxy(self.remote_wsgi.GitRemoteWsgi())
271 return app(environ, start_response)
275 return app(environ, start_response)
272 return _git_proxy
276 return _git_proxy
273
277
274 def hg_stream(self):
278 def hg_stream(self):
275 if self._use_echo_app:
279 if self._use_echo_app:
276 @wsgiapp
280 @wsgiapp
277 def _hg_stream(environ, start_response):
281 def _hg_stream(environ, start_response):
278 app = EchoApp('fake_path', 'fake_name', None)
282 app = EchoApp('fake_path', 'fake_name', None)
279 return app(environ, start_response)
283 return app(environ, start_response)
280 return _hg_stream
284 return _hg_stream
281 else:
285 else:
282 @wsgiapp
286 @wsgiapp
283 def _hg_stream(environ, start_response):
287 def _hg_stream(environ, start_response):
284 repo_path = environ['HTTP_X_RC_REPO_PATH']
288 repo_path = environ['HTTP_X_RC_REPO_PATH']
285 repo_name = environ['HTTP_X_RC_REPO_NAME']
289 repo_name = environ['HTTP_X_RC_REPO_NAME']
286 packed_config = base64.b64decode(
290 packed_config = base64.b64decode(
287 environ['HTTP_X_RC_REPO_CONFIG'])
291 environ['HTTP_X_RC_REPO_CONFIG'])
288 config = msgpack.unpackb(packed_config)
292 config = msgpack.unpackb(packed_config)
289 app = scm_app.create_hg_wsgi_app(
293 app = scm_app.create_hg_wsgi_app(
290 repo_path, repo_name, config)
294 repo_path, repo_name, config)
291
295
292 # Consitent path information for hgweb
296 # Consitent path information for hgweb
293 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
297 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
294 environ['REPO_NAME'] = repo_name
298 environ['REPO_NAME'] = repo_name
295 return app(environ, ResponseFilter(start_response))
299 return app(environ, ResponseFilter(start_response))
296 return _hg_stream
300 return _hg_stream
297
301
298 def git_stream(self):
302 def git_stream(self):
299 if self._use_echo_app:
303 if self._use_echo_app:
300 @wsgiapp
304 @wsgiapp
301 def _git_stream(environ, start_response):
305 def _git_stream(environ, start_response):
302 app = EchoApp('fake_path', 'fake_name', None)
306 app = EchoApp('fake_path', 'fake_name', None)
303 return app(environ, start_response)
307 return app(environ, start_response)
304 return _git_stream
308 return _git_stream
305 else:
309 else:
306 @wsgiapp
310 @wsgiapp
307 def _git_stream(environ, start_response):
311 def _git_stream(environ, start_response):
308 repo_path = environ['HTTP_X_RC_REPO_PATH']
312 repo_path = environ['HTTP_X_RC_REPO_PATH']
309 repo_name = environ['HTTP_X_RC_REPO_NAME']
313 repo_name = environ['HTTP_X_RC_REPO_NAME']
310 packed_config = base64.b64decode(
314 packed_config = base64.b64decode(
311 environ['HTTP_X_RC_REPO_CONFIG'])
315 environ['HTTP_X_RC_REPO_CONFIG'])
312 config = msgpack.unpackb(packed_config)
316 config = msgpack.unpackb(packed_config)
313
317
314 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
318 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
315 app = scm_app.create_git_wsgi_app(
319 app = scm_app.create_git_wsgi_app(
316 repo_path, repo_name, config)
320 repo_path, repo_name, config)
317 return app(environ, start_response)
321 return app(environ, start_response)
318 return _git_stream
322 return _git_stream
319
323
324 def is_vcs_exception(self, context, request):
325 """
326 View predicate that returns true if the context object is a VCS
327 exception.
328 """
329 return hasattr(context, '_vcs_kind')
330
331 def handle_vcs_exception(self, exception, request):
332 if exception._vcs_kind == 'repo_locked':
333 # Get custom repo-locked status code if present.
334 status_code = request.headers.get('X-RC-Locked-Status-Code')
335 return HTTPRepoLocked(
336 title=exception.message, status_code=status_code)
337
338 # Re-raise exception if we can not handle it.
339 raise exception
340
320
341
321 class ResponseFilter(object):
342 class ResponseFilter(object):
322
343
323 def __init__(self, start_response):
344 def __init__(self, start_response):
324 self._start_response = start_response
345 self._start_response = start_response
325
346
326 def __call__(self, status, response_headers, exc_info=None):
347 def __call__(self, status, response_headers, exc_info=None):
327 headers = tuple(
348 headers = tuple(
328 (h, v) for h, v in response_headers
349 (h, v) for h, v in response_headers
329 if not wsgiref.util.is_hop_by_hop(h))
350 if not wsgiref.util.is_hop_by_hop(h))
330 return self._start_response(status, headers, exc_info)
351 return self._start_response(status, headers, exc_info)
331
352
332
353
333 def main(global_config, **settings):
354 def main(global_config, **settings):
334 if MercurialFactory:
355 if MercurialFactory:
335 hgpatches.patch_largefiles_capabilities()
356 hgpatches.patch_largefiles_capabilities()
336 app = HTTPApplication(settings=settings)
357 app = HTTPApplication(settings=settings)
337 return app.wsgi_app()
358 return app.wsgi_app()
General Comments 0
You need to be logged in to leave comments. Login now