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