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