##// END OF EJS Templates
errors: use a better interface to track exceptions and tracebacks.
marcink -
r478:439940e9 default
parent child Browse files
Show More
@@ -1,487 +1,490 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-2018 RhodeCode GmbH
2 # Copyright (C) 2014-2018 RhodeCode 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 os
18 import os
19 import base64
19 import base64
20 import locale
20 import locale
21 import logging
21 import logging
22 import uuid
22 import uuid
23 import wsgiref.util
23 import wsgiref.util
24 import traceback
24 import traceback
25 from itertools import chain
25 from itertools import chain
26
26
27 import simplejson as json
27 import simplejson as json
28 import msgpack
28 import msgpack
29 from beaker.cache import CacheManager
29 from beaker.cache import CacheManager
30 from beaker.util import parse_cache_config_options
30 from beaker.util import parse_cache_config_options
31 from pyramid.config import Configurator
31 from pyramid.config import Configurator
32 from pyramid.wsgi import wsgiapp
32 from pyramid.wsgi import wsgiapp
33 from pyramid.compat import configparser
33 from pyramid.compat import configparser
34
34
35 from vcsserver import remote_wsgi, scm_app, settings, hgpatches
35 from vcsserver import remote_wsgi, scm_app, settings, hgpatches
36 from vcsserver.git_lfs.app import GIT_LFS_CONTENT_TYPE, GIT_LFS_PROTO_PAT
36 from vcsserver.git_lfs.app import GIT_LFS_CONTENT_TYPE, GIT_LFS_PROTO_PAT
37 from vcsserver.echo_stub import remote_wsgi as remote_wsgi_stub
37 from vcsserver.echo_stub import remote_wsgi as remote_wsgi_stub
38 from vcsserver.echo_stub.echo_app import EchoApp
38 from vcsserver.echo_stub.echo_app import EchoApp
39 from vcsserver.exceptions import HTTPRepoLocked
39 from vcsserver.exceptions import HTTPRepoLocked
40 from vcsserver.server import VcsServer
40 from vcsserver.server import VcsServer
41
41
42 try:
42 try:
43 from vcsserver.git import GitFactory, GitRemote
43 from vcsserver.git import GitFactory, GitRemote
44 except ImportError:
44 except ImportError:
45 GitFactory = None
45 GitFactory = None
46 GitRemote = None
46 GitRemote = None
47
47
48 try:
48 try:
49 from vcsserver.hg import MercurialFactory, HgRemote
49 from vcsserver.hg import MercurialFactory, HgRemote
50 except ImportError:
50 except ImportError:
51 MercurialFactory = None
51 MercurialFactory = None
52 HgRemote = None
52 HgRemote = None
53
53
54 try:
54 try:
55 from vcsserver.svn import SubversionFactory, SvnRemote
55 from vcsserver.svn import SubversionFactory, SvnRemote
56 except ImportError:
56 except ImportError:
57 SubversionFactory = None
57 SubversionFactory = None
58 SvnRemote = None
58 SvnRemote = None
59
59
60 log = logging.getLogger(__name__)
60 log = logging.getLogger(__name__)
61
61
62
62
63 def _is_request_chunked(environ):
63 def _is_request_chunked(environ):
64 stream = environ.get('HTTP_TRANSFER_ENCODING', '') == 'chunked'
64 stream = environ.get('HTTP_TRANSFER_ENCODING', '') == 'chunked'
65 return stream
65 return stream
66
66
67
67
68 class VCS(object):
68 class VCS(object):
69 def __init__(self, locale=None, cache_config=None):
69 def __init__(self, locale=None, cache_config=None):
70 self.locale = locale
70 self.locale = locale
71 self.cache_config = cache_config
71 self.cache_config = cache_config
72 self._configure_locale()
72 self._configure_locale()
73 self._initialize_cache()
73 self._initialize_cache()
74
74
75 if GitFactory and GitRemote:
75 if GitFactory and GitRemote:
76 git_repo_cache = self.cache.get_cache_region(
76 git_repo_cache = self.cache.get_cache_region(
77 'git', region='repo_object')
77 'git', region='repo_object')
78 git_factory = GitFactory(git_repo_cache)
78 git_factory = GitFactory(git_repo_cache)
79 self._git_remote = GitRemote(git_factory)
79 self._git_remote = GitRemote(git_factory)
80 else:
80 else:
81 log.info("Git client import failed")
81 log.info("Git client import failed")
82
82
83 if MercurialFactory and HgRemote:
83 if MercurialFactory and HgRemote:
84 hg_repo_cache = self.cache.get_cache_region(
84 hg_repo_cache = self.cache.get_cache_region(
85 'hg', region='repo_object')
85 'hg', region='repo_object')
86 hg_factory = MercurialFactory(hg_repo_cache)
86 hg_factory = MercurialFactory(hg_repo_cache)
87 self._hg_remote = HgRemote(hg_factory)
87 self._hg_remote = HgRemote(hg_factory)
88 else:
88 else:
89 log.info("Mercurial client import failed")
89 log.info("Mercurial client import failed")
90
90
91 if SubversionFactory and SvnRemote:
91 if SubversionFactory and SvnRemote:
92 svn_repo_cache = self.cache.get_cache_region(
92 svn_repo_cache = self.cache.get_cache_region(
93 'svn', region='repo_object')
93 'svn', region='repo_object')
94 svn_factory = SubversionFactory(svn_repo_cache)
94 svn_factory = SubversionFactory(svn_repo_cache)
95 # hg factory is used for svn url validation
95 # hg factory is used for svn url validation
96 hg_repo_cache = self.cache.get_cache_region(
96 hg_repo_cache = self.cache.get_cache_region(
97 'hg', region='repo_object')
97 'hg', region='repo_object')
98 hg_factory = MercurialFactory(hg_repo_cache)
98 hg_factory = MercurialFactory(hg_repo_cache)
99 self._svn_remote = SvnRemote(svn_factory, hg_factory=hg_factory)
99 self._svn_remote = SvnRemote(svn_factory, hg_factory=hg_factory)
100 else:
100 else:
101 log.info("Subversion client import failed")
101 log.info("Subversion client import failed")
102
102
103 self._vcsserver = VcsServer()
103 self._vcsserver = VcsServer()
104
104
105 def _initialize_cache(self):
105 def _initialize_cache(self):
106 cache_config = parse_cache_config_options(self.cache_config)
106 cache_config = parse_cache_config_options(self.cache_config)
107 log.info('Initializing beaker cache: %s' % cache_config)
107 log.info('Initializing beaker cache: %s' % cache_config)
108 self.cache = CacheManager(**cache_config)
108 self.cache = CacheManager(**cache_config)
109
109
110 def _configure_locale(self):
110 def _configure_locale(self):
111 if self.locale:
111 if self.locale:
112 log.info('Settings locale: `LC_ALL` to %s' % self.locale)
112 log.info('Settings locale: `LC_ALL` to %s' % self.locale)
113 else:
113 else:
114 log.info(
114 log.info(
115 'Configuring locale subsystem based on environment variables')
115 'Configuring locale subsystem based on environment variables')
116 try:
116 try:
117 # If self.locale is the empty string, then the locale
117 # If self.locale is the empty string, then the locale
118 # module will use the environment variables. See the
118 # module will use the environment variables. See the
119 # documentation of the package `locale`.
119 # documentation of the package `locale`.
120 locale.setlocale(locale.LC_ALL, self.locale)
120 locale.setlocale(locale.LC_ALL, self.locale)
121
121
122 language_code, encoding = locale.getlocale()
122 language_code, encoding = locale.getlocale()
123 log.info(
123 log.info(
124 'Locale set to language code "%s" with encoding "%s".',
124 'Locale set to language code "%s" with encoding "%s".',
125 language_code, encoding)
125 language_code, encoding)
126 except locale.Error:
126 except locale.Error:
127 log.exception(
127 log.exception(
128 'Cannot set locale, not configuring the locale system')
128 'Cannot set locale, not configuring the locale system')
129
129
130
130
131 class WsgiProxy(object):
131 class WsgiProxy(object):
132 def __init__(self, wsgi):
132 def __init__(self, wsgi):
133 self.wsgi = wsgi
133 self.wsgi = wsgi
134
134
135 def __call__(self, environ, start_response):
135 def __call__(self, environ, start_response):
136 input_data = environ['wsgi.input'].read()
136 input_data = environ['wsgi.input'].read()
137 input_data = msgpack.unpackb(input_data)
137 input_data = msgpack.unpackb(input_data)
138
138
139 error = None
139 error = None
140 try:
140 try:
141 data, status, headers = self.wsgi.handle(
141 data, status, headers = self.wsgi.handle(
142 input_data['environment'], input_data['input_data'],
142 input_data['environment'], input_data['input_data'],
143 *input_data['args'], **input_data['kwargs'])
143 *input_data['args'], **input_data['kwargs'])
144 except Exception as e:
144 except Exception as e:
145 data, status, headers = [], None, None
145 data, status, headers = [], None, None
146 error = {
146 error = {
147 'message': str(e),
147 'message': str(e),
148 '_vcs_kind': getattr(e, '_vcs_kind', None)
148 '_vcs_kind': getattr(e, '_vcs_kind', None)
149 }
149 }
150
150
151 start_response(200, {})
151 start_response(200, {})
152 return self._iterator(error, status, headers, data)
152 return self._iterator(error, status, headers, data)
153
153
154 def _iterator(self, error, status, headers, data):
154 def _iterator(self, error, status, headers, data):
155 initial_data = [
155 initial_data = [
156 error,
156 error,
157 status,
157 status,
158 headers,
158 headers,
159 ]
159 ]
160
160
161 for d in chain(initial_data, data):
161 for d in chain(initial_data, data):
162 yield msgpack.packb(d)
162 yield msgpack.packb(d)
163
163
164
164
165 class HTTPApplication(object):
165 class HTTPApplication(object):
166 ALLOWED_EXCEPTIONS = ('KeyError', 'URLError')
166 ALLOWED_EXCEPTIONS = ('KeyError', 'URLError')
167
167
168 remote_wsgi = remote_wsgi
168 remote_wsgi = remote_wsgi
169 _use_echo_app = False
169 _use_echo_app = False
170
170
171 def __init__(self, settings=None, global_config=None):
171 def __init__(self, settings=None, global_config=None):
172 self.config = Configurator(settings=settings)
172 self.config = Configurator(settings=settings)
173 self.global_config = global_config
173 self.global_config = global_config
174
174
175 locale = settings.get('locale', '') or 'en_US.UTF-8'
175 locale = settings.get('locale', '') or 'en_US.UTF-8'
176 vcs = VCS(locale=locale, cache_config=settings)
176 vcs = VCS(locale=locale, cache_config=settings)
177 self._remotes = {
177 self._remotes = {
178 'hg': vcs._hg_remote,
178 'hg': vcs._hg_remote,
179 'git': vcs._git_remote,
179 'git': vcs._git_remote,
180 'svn': vcs._svn_remote,
180 'svn': vcs._svn_remote,
181 'server': vcs._vcsserver,
181 'server': vcs._vcsserver,
182 }
182 }
183 if settings.get('dev.use_echo_app', 'false').lower() == 'true':
183 if settings.get('dev.use_echo_app', 'false').lower() == 'true':
184 self._use_echo_app = True
184 self._use_echo_app = True
185 log.warning("Using EchoApp for VCS operations.")
185 log.warning("Using EchoApp for VCS operations.")
186 self.remote_wsgi = remote_wsgi_stub
186 self.remote_wsgi = remote_wsgi_stub
187 self._configure_settings(settings)
187 self._configure_settings(settings)
188 self._configure()
188 self._configure()
189
189
190 def _configure_settings(self, app_settings):
190 def _configure_settings(self, app_settings):
191 """
191 """
192 Configure the settings module.
192 Configure the settings module.
193 """
193 """
194 git_path = app_settings.get('git_path', None)
194 git_path = app_settings.get('git_path', None)
195 if git_path:
195 if git_path:
196 settings.GIT_EXECUTABLE = git_path
196 settings.GIT_EXECUTABLE = git_path
197 binary_dir = app_settings.get('core.binary_dir', None)
197 binary_dir = app_settings.get('core.binary_dir', None)
198 if binary_dir:
198 if binary_dir:
199 settings.BINARY_DIR = binary_dir
199 settings.BINARY_DIR = binary_dir
200
200
201 def _configure(self):
201 def _configure(self):
202 self.config.add_renderer(
202 self.config.add_renderer(
203 name='msgpack',
203 name='msgpack',
204 factory=self._msgpack_renderer_factory)
204 factory=self._msgpack_renderer_factory)
205
205
206 self.config.add_route('service', '/_service')
206 self.config.add_route('service', '/_service')
207 self.config.add_route('status', '/status')
207 self.config.add_route('status', '/status')
208 self.config.add_route('hg_proxy', '/proxy/hg')
208 self.config.add_route('hg_proxy', '/proxy/hg')
209 self.config.add_route('git_proxy', '/proxy/git')
209 self.config.add_route('git_proxy', '/proxy/git')
210 self.config.add_route('vcs', '/{backend}')
210 self.config.add_route('vcs', '/{backend}')
211 self.config.add_route('stream_git', '/stream/git/*repo_name')
211 self.config.add_route('stream_git', '/stream/git/*repo_name')
212 self.config.add_route('stream_hg', '/stream/hg/*repo_name')
212 self.config.add_route('stream_hg', '/stream/hg/*repo_name')
213
213
214 self.config.add_view(
214 self.config.add_view(
215 self.status_view, route_name='status', renderer='json')
215 self.status_view, route_name='status', renderer='json')
216 self.config.add_view(
216 self.config.add_view(
217 self.service_view, route_name='service', renderer='msgpack')
217 self.service_view, route_name='service', renderer='msgpack')
218
218
219 self.config.add_view(self.hg_proxy(), route_name='hg_proxy')
219 self.config.add_view(self.hg_proxy(), route_name='hg_proxy')
220 self.config.add_view(self.git_proxy(), route_name='git_proxy')
220 self.config.add_view(self.git_proxy(), route_name='git_proxy')
221 self.config.add_view(
221 self.config.add_view(
222 self.vcs_view, route_name='vcs', renderer='msgpack',
222 self.vcs_view, route_name='vcs', renderer='msgpack',
223 custom_predicates=[self.is_vcs_view])
223 custom_predicates=[self.is_vcs_view])
224
224
225 self.config.add_view(self.hg_stream(), route_name='stream_hg')
225 self.config.add_view(self.hg_stream(), route_name='stream_hg')
226 self.config.add_view(self.git_stream(), route_name='stream_git')
226 self.config.add_view(self.git_stream(), route_name='stream_git')
227
227
228 def notfound(request):
228 def notfound(request):
229 return {'status': '404 NOT FOUND'}
229 return {'status': '404 NOT FOUND'}
230 self.config.add_notfound_view(notfound, renderer='json')
230 self.config.add_notfound_view(notfound, renderer='json')
231
231
232 self.config.add_view(self.handle_vcs_exception, context=Exception)
232 self.config.add_view(self.handle_vcs_exception, context=Exception)
233
233
234 self.config.add_tween(
234 self.config.add_tween(
235 'vcsserver.tweens.RequestWrapperTween',
235 'vcsserver.tweens.RequestWrapperTween',
236 )
236 )
237
237
238 def wsgi_app(self):
238 def wsgi_app(self):
239 return self.config.make_wsgi_app()
239 return self.config.make_wsgi_app()
240
240
241 def vcs_view(self, request):
241 def vcs_view(self, request):
242 remote = self._remotes[request.matchdict['backend']]
242 remote = self._remotes[request.matchdict['backend']]
243 payload = msgpack.unpackb(request.body, use_list=True)
243 payload = msgpack.unpackb(request.body, use_list=True)
244 method = payload.get('method')
244 method = payload.get('method')
245 params = payload.get('params')
245 params = payload.get('params')
246 wire = params.get('wire')
246 wire = params.get('wire')
247 args = params.get('args')
247 args = params.get('args')
248 kwargs = params.get('kwargs')
248 kwargs = params.get('kwargs')
249 if wire:
249 if wire:
250 try:
250 try:
251 wire['context'] = uuid.UUID(wire['context'])
251 wire['context'] = uuid.UUID(wire['context'])
252 except KeyError:
252 except KeyError:
253 pass
253 pass
254 args.insert(0, wire)
254 args.insert(0, wire)
255
255
256 log.debug('method called:%s with kwargs:%s', method, kwargs)
256 log.debug('method called:%s with kwargs:%s', method, kwargs)
257 try:
257 try:
258 resp = getattr(remote, method)(*args, **kwargs)
258 resp = getattr(remote, method)(*args, **kwargs)
259 except Exception as e:
259 except Exception as e:
260 tb_info = traceback.format_exc()
260 tb_info = traceback.format_exc()
261
261
262 type_ = e.__class__.__name__
262 type_ = e.__class__.__name__
263 if type_ not in self.ALLOWED_EXCEPTIONS:
263 if type_ not in self.ALLOWED_EXCEPTIONS:
264 type_ = None
264 type_ = None
265
265
266 resp = {
266 resp = {
267 'id': payload.get('id'),
267 'id': payload.get('id'),
268 'error': {
268 'error': {
269 'message': e.message,
269 'message': e.message,
270 'traceback': tb_info,
270 'traceback': tb_info,
271 'type': type_
271 'type': type_
272 }
272 }
273 }
273 }
274 try:
274 try:
275 resp['error']['_vcs_kind'] = e._vcs_kind
275 resp['error']['_vcs_kind'] = e._vcs_kind
276 except AttributeError:
276 except AttributeError:
277 pass
277 pass
278 else:
278 else:
279 resp = {
279 resp = {
280 'id': payload.get('id'),
280 'id': payload.get('id'),
281 'result': resp
281 'result': resp
282 }
282 }
283
283
284 return resp
284 return resp
285
285
286 def status_view(self, request):
286 def status_view(self, request):
287 import vcsserver
287 import vcsserver
288 return {'status': 'OK', 'vcsserver_version': vcsserver.__version__,
288 return {'status': 'OK', 'vcsserver_version': vcsserver.__version__,
289 'pid': os.getpid()}
289 'pid': os.getpid()}
290
290
291 def service_view(self, request):
291 def service_view(self, request):
292 import vcsserver
292 import vcsserver
293
293
294 payload = msgpack.unpackb(request.body, use_list=True)
294 payload = msgpack.unpackb(request.body, use_list=True)
295
295
296 try:
296 try:
297 path = self.global_config['__file__']
297 path = self.global_config['__file__']
298 config = configparser.ConfigParser()
298 config = configparser.ConfigParser()
299 config.read(path)
299 config.read(path)
300 parsed_ini = config
300 parsed_ini = config
301 if parsed_ini.has_section('server:main'):
301 if parsed_ini.has_section('server:main'):
302 parsed_ini = dict(parsed_ini.items('server:main'))
302 parsed_ini = dict(parsed_ini.items('server:main'))
303 except Exception:
303 except Exception:
304 log.exception('Failed to read .ini file for display')
304 log.exception('Failed to read .ini file for display')
305 parsed_ini = {}
305 parsed_ini = {}
306
306
307 resp = {
307 resp = {
308 'id': payload.get('id'),
308 'id': payload.get('id'),
309 'result': dict(
309 'result': dict(
310 version=vcsserver.__version__,
310 version=vcsserver.__version__,
311 config=parsed_ini,
311 config=parsed_ini,
312 payload=payload,
312 payload=payload,
313 )
313 )
314 }
314 }
315 return resp
315 return resp
316
316
317 def _msgpack_renderer_factory(self, info):
317 def _msgpack_renderer_factory(self, info):
318 def _render(value, system):
318 def _render(value, system):
319 value = msgpack.packb(value)
319 value = msgpack.packb(value)
320 request = system.get('request')
320 request = system.get('request')
321 if request is not None:
321 if request is not None:
322 response = request.response
322 response = request.response
323 ct = response.content_type
323 ct = response.content_type
324 if ct == response.default_content_type:
324 if ct == response.default_content_type:
325 response.content_type = 'application/x-msgpack'
325 response.content_type = 'application/x-msgpack'
326 return value
326 return value
327 return _render
327 return _render
328
328
329 def set_env_from_config(self, environ, config):
329 def set_env_from_config(self, environ, config):
330 dict_conf = {}
330 dict_conf = {}
331 try:
331 try:
332 for elem in config:
332 for elem in config:
333 if elem[0] == 'rhodecode':
333 if elem[0] == 'rhodecode':
334 dict_conf = json.loads(elem[2])
334 dict_conf = json.loads(elem[2])
335 break
335 break
336 except Exception:
336 except Exception:
337 log.exception('Failed to fetch SCM CONFIG')
337 log.exception('Failed to fetch SCM CONFIG')
338 return
338 return
339
339
340 username = dict_conf.get('username')
340 username = dict_conf.get('username')
341 if username:
341 if username:
342 environ['REMOTE_USER'] = username
342 environ['REMOTE_USER'] = username
343 # mercurial specific, some extension api rely on this
343 # mercurial specific, some extension api rely on this
344 environ['HGUSER'] = username
344 environ['HGUSER'] = username
345
345
346 ip = dict_conf.get('ip')
346 ip = dict_conf.get('ip')
347 if ip:
347 if ip:
348 environ['REMOTE_HOST'] = ip
348 environ['REMOTE_HOST'] = ip
349
349
350 if _is_request_chunked(environ):
350 if _is_request_chunked(environ):
351 # set the compatibility flag for webob
351 # set the compatibility flag for webob
352 environ['wsgi.input_terminated'] = True
352 environ['wsgi.input_terminated'] = True
353
353
354 def hg_proxy(self):
354 def hg_proxy(self):
355 @wsgiapp
355 @wsgiapp
356 def _hg_proxy(environ, start_response):
356 def _hg_proxy(environ, start_response):
357 app = WsgiProxy(self.remote_wsgi.HgRemoteWsgi())
357 app = WsgiProxy(self.remote_wsgi.HgRemoteWsgi())
358 return app(environ, start_response)
358 return app(environ, start_response)
359 return _hg_proxy
359 return _hg_proxy
360
360
361 def git_proxy(self):
361 def git_proxy(self):
362 @wsgiapp
362 @wsgiapp
363 def _git_proxy(environ, start_response):
363 def _git_proxy(environ, start_response):
364 app = WsgiProxy(self.remote_wsgi.GitRemoteWsgi())
364 app = WsgiProxy(self.remote_wsgi.GitRemoteWsgi())
365 return app(environ, start_response)
365 return app(environ, start_response)
366 return _git_proxy
366 return _git_proxy
367
367
368 def hg_stream(self):
368 def hg_stream(self):
369 if self._use_echo_app:
369 if self._use_echo_app:
370 @wsgiapp
370 @wsgiapp
371 def _hg_stream(environ, start_response):
371 def _hg_stream(environ, start_response):
372 app = EchoApp('fake_path', 'fake_name', None)
372 app = EchoApp('fake_path', 'fake_name', None)
373 return app(environ, start_response)
373 return app(environ, start_response)
374 return _hg_stream
374 return _hg_stream
375 else:
375 else:
376 @wsgiapp
376 @wsgiapp
377 def _hg_stream(environ, start_response):
377 def _hg_stream(environ, start_response):
378 log.debug('http-app: handling hg stream')
378 log.debug('http-app: handling hg stream')
379 repo_path = environ['HTTP_X_RC_REPO_PATH']
379 repo_path = environ['HTTP_X_RC_REPO_PATH']
380 repo_name = environ['HTTP_X_RC_REPO_NAME']
380 repo_name = environ['HTTP_X_RC_REPO_NAME']
381 packed_config = base64.b64decode(
381 packed_config = base64.b64decode(
382 environ['HTTP_X_RC_REPO_CONFIG'])
382 environ['HTTP_X_RC_REPO_CONFIG'])
383 config = msgpack.unpackb(packed_config)
383 config = msgpack.unpackb(packed_config)
384 app = scm_app.create_hg_wsgi_app(
384 app = scm_app.create_hg_wsgi_app(
385 repo_path, repo_name, config)
385 repo_path, repo_name, config)
386
386
387 # Consistent path information for hgweb
387 # Consistent path information for hgweb
388 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
388 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
389 environ['REPO_NAME'] = repo_name
389 environ['REPO_NAME'] = repo_name
390 self.set_env_from_config(environ, config)
390 self.set_env_from_config(environ, config)
391
391
392 log.debug('http-app: starting app handler '
392 log.debug('http-app: starting app handler '
393 'with %s and process request', app)
393 'with %s and process request', app)
394 return app(environ, ResponseFilter(start_response))
394 return app(environ, ResponseFilter(start_response))
395 return _hg_stream
395 return _hg_stream
396
396
397 def git_stream(self):
397 def git_stream(self):
398 if self._use_echo_app:
398 if self._use_echo_app:
399 @wsgiapp
399 @wsgiapp
400 def _git_stream(environ, start_response):
400 def _git_stream(environ, start_response):
401 app = EchoApp('fake_path', 'fake_name', None)
401 app = EchoApp('fake_path', 'fake_name', None)
402 return app(environ, start_response)
402 return app(environ, start_response)
403 return _git_stream
403 return _git_stream
404 else:
404 else:
405 @wsgiapp
405 @wsgiapp
406 def _git_stream(environ, start_response):
406 def _git_stream(environ, start_response):
407 log.debug('http-app: handling git stream')
407 log.debug('http-app: handling git stream')
408 repo_path = environ['HTTP_X_RC_REPO_PATH']
408 repo_path = environ['HTTP_X_RC_REPO_PATH']
409 repo_name = environ['HTTP_X_RC_REPO_NAME']
409 repo_name = environ['HTTP_X_RC_REPO_NAME']
410 packed_config = base64.b64decode(
410 packed_config = base64.b64decode(
411 environ['HTTP_X_RC_REPO_CONFIG'])
411 environ['HTTP_X_RC_REPO_CONFIG'])
412 config = msgpack.unpackb(packed_config)
412 config = msgpack.unpackb(packed_config)
413
413
414 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
414 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
415 self.set_env_from_config(environ, config)
415 self.set_env_from_config(environ, config)
416
416
417 content_type = environ.get('CONTENT_TYPE', '')
417 content_type = environ.get('CONTENT_TYPE', '')
418
418
419 path = environ['PATH_INFO']
419 path = environ['PATH_INFO']
420 is_lfs_request = GIT_LFS_CONTENT_TYPE in content_type
420 is_lfs_request = GIT_LFS_CONTENT_TYPE in content_type
421 log.debug(
421 log.debug(
422 'LFS: Detecting if request `%s` is LFS server path based '
422 'LFS: Detecting if request `%s` is LFS server path based '
423 'on content type:`%s`, is_lfs:%s',
423 'on content type:`%s`, is_lfs:%s',
424 path, content_type, is_lfs_request)
424 path, content_type, is_lfs_request)
425
425
426 if not is_lfs_request:
426 if not is_lfs_request:
427 # fallback detection by path
427 # fallback detection by path
428 if GIT_LFS_PROTO_PAT.match(path):
428 if GIT_LFS_PROTO_PAT.match(path):
429 is_lfs_request = True
429 is_lfs_request = True
430 log.debug(
430 log.debug(
431 'LFS: fallback detection by path of: `%s`, is_lfs:%s',
431 'LFS: fallback detection by path of: `%s`, is_lfs:%s',
432 path, is_lfs_request)
432 path, is_lfs_request)
433
433
434 if is_lfs_request:
434 if is_lfs_request:
435 app = scm_app.create_git_lfs_wsgi_app(
435 app = scm_app.create_git_lfs_wsgi_app(
436 repo_path, repo_name, config)
436 repo_path, repo_name, config)
437 else:
437 else:
438 app = scm_app.create_git_wsgi_app(
438 app = scm_app.create_git_wsgi_app(
439 repo_path, repo_name, config)
439 repo_path, repo_name, config)
440
440
441 log.debug('http-app: starting app handler '
441 log.debug('http-app: starting app handler '
442 'with %s and process request', app)
442 'with %s and process request', app)
443
443
444 return app(environ, start_response)
444 return app(environ, start_response)
445
445
446 return _git_stream
446 return _git_stream
447
447
448 def is_vcs_view(self, context, request):
448 def is_vcs_view(self, context, request):
449 """
449 """
450 View predicate that returns true if given backend is supported by
450 View predicate that returns true if given backend is supported by
451 defined remotes.
451 defined remotes.
452 """
452 """
453 backend = request.matchdict.get('backend')
453 backend = request.matchdict.get('backend')
454 return backend in self._remotes
454 return backend in self._remotes
455
455
456 def handle_vcs_exception(self, exception, request):
456 def handle_vcs_exception(self, exception, request):
457 _vcs_kind = getattr(exception, '_vcs_kind', '')
457 _vcs_kind = getattr(exception, '_vcs_kind', '')
458 if _vcs_kind == 'repo_locked':
458 if _vcs_kind == 'repo_locked':
459 # Get custom repo-locked status code if present.
459 # Get custom repo-locked status code if present.
460 status_code = request.headers.get('X-RC-Locked-Status-Code')
460 status_code = request.headers.get('X-RC-Locked-Status-Code')
461 return HTTPRepoLocked(
461 return HTTPRepoLocked(
462 title=exception.message, status_code=status_code)
462 title=exception.message, status_code=status_code)
463 traceback_info = 'unavailable'
464 if request.exc_info:
465 traceback_info = traceback.format_exc(request.exc_info[2])
463
466
464 # Re-raise exception if we can not handle it.
467 log.error(
465 log.exception(
468 'error occurred handling this request for path: %s, \n tb: %s',
466 'error occurred handling this request for path: %s', request.path)
469 request.path, traceback_info)
467 raise exception
470 raise exception
468
471
469
472
470 class ResponseFilter(object):
473 class ResponseFilter(object):
471
474
472 def __init__(self, start_response):
475 def __init__(self, start_response):
473 self._start_response = start_response
476 self._start_response = start_response
474
477
475 def __call__(self, status, response_headers, exc_info=None):
478 def __call__(self, status, response_headers, exc_info=None):
476 headers = tuple(
479 headers = tuple(
477 (h, v) for h, v in response_headers
480 (h, v) for h, v in response_headers
478 if not wsgiref.util.is_hop_by_hop(h))
481 if not wsgiref.util.is_hop_by_hop(h))
479 return self._start_response(status, headers, exc_info)
482 return self._start_response(status, headers, exc_info)
480
483
481
484
482 def main(global_config, **settings):
485 def main(global_config, **settings):
483 if MercurialFactory:
486 if MercurialFactory:
484 hgpatches.patch_largefiles_capabilities()
487 hgpatches.patch_largefiles_capabilities()
485 hgpatches.patch_subrepo_type_mapping()
488 hgpatches.patch_subrepo_type_mapping()
486 app = HTTPApplication(settings=settings, global_config=global_config)
489 app = HTTPApplication(settings=settings, global_config=global_config)
487 return app.wsgi_app()
490 return app.wsgi_app()
General Comments 0
You need to be logged in to leave comments. Login now