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