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