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