##// END OF EJS Templates
http: added catch-all exception handler to show errors in logs.
marcink -
r150:ed9a1494 default
parent child Browse files
Show More
@@ -1,380 +1,390 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 msgpack
26 import msgpack
27 from beaker.cache import CacheManager
27 from beaker.cache import CacheManager
28 from beaker.util import parse_cache_config_options
28 from beaker.util import parse_cache_config_options
29 from pyramid.config import Configurator
29 from pyramid.config import Configurator
30 from pyramid.wsgi import wsgiapp
30 from pyramid.wsgi import wsgiapp
31
31
32 from vcsserver import remote_wsgi, scm_app, settings, hgpatches
32 from vcsserver import remote_wsgi, scm_app, settings, hgpatches
33 from vcsserver.echo_stub import remote_wsgi as remote_wsgi_stub
33 from vcsserver.echo_stub import remote_wsgi as remote_wsgi_stub
34 from vcsserver.echo_stub.echo_app import EchoApp
34 from vcsserver.echo_stub.echo_app import EchoApp
35 from vcsserver.exceptions import HTTPRepoLocked
35 from vcsserver.exceptions import HTTPRepoLocked
36 from vcsserver.server import VcsServer
36 from vcsserver.server import VcsServer
37
37
38 try:
38 try:
39 from vcsserver.git import GitFactory, GitRemote
39 from vcsserver.git import GitFactory, GitRemote
40 except ImportError:
40 except ImportError:
41 GitFactory = None
41 GitFactory = None
42 GitRemote = None
42 GitRemote = None
43 try:
43 try:
44 from vcsserver.hg import MercurialFactory, HgRemote
44 from vcsserver.hg import MercurialFactory, HgRemote
45 except ImportError:
45 except ImportError:
46 MercurialFactory = None
46 MercurialFactory = None
47 HgRemote = None
47 HgRemote = None
48 try:
48 try:
49 from vcsserver.svn import SubversionFactory, SvnRemote
49 from vcsserver.svn import SubversionFactory, SvnRemote
50 except ImportError:
50 except ImportError:
51 SubversionFactory = None
51 SubversionFactory = None
52 SvnRemote = None
52 SvnRemote = None
53
53
54 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
55
55
56
56
57 class VCS(object):
57 class VCS(object):
58 def __init__(self, locale=None, cache_config=None):
58 def __init__(self, locale=None, cache_config=None):
59 self.locale = locale
59 self.locale = locale
60 self.cache_config = cache_config
60 self.cache_config = cache_config
61 self._configure_locale()
61 self._configure_locale()
62 self._initialize_cache()
62 self._initialize_cache()
63
63
64 if GitFactory and GitRemote:
64 if GitFactory and GitRemote:
65 git_repo_cache = self.cache.get_cache_region(
65 git_repo_cache = self.cache.get_cache_region(
66 'git', region='repo_object')
66 'git', region='repo_object')
67 git_factory = GitFactory(git_repo_cache)
67 git_factory = GitFactory(git_repo_cache)
68 self._git_remote = GitRemote(git_factory)
68 self._git_remote = GitRemote(git_factory)
69 else:
69 else:
70 log.info("Git client import failed")
70 log.info("Git client import failed")
71
71
72 if MercurialFactory and HgRemote:
72 if MercurialFactory and HgRemote:
73 hg_repo_cache = self.cache.get_cache_region(
73 hg_repo_cache = self.cache.get_cache_region(
74 'hg', region='repo_object')
74 'hg', region='repo_object')
75 hg_factory = MercurialFactory(hg_repo_cache)
75 hg_factory = MercurialFactory(hg_repo_cache)
76 self._hg_remote = HgRemote(hg_factory)
76 self._hg_remote = HgRemote(hg_factory)
77 else:
77 else:
78 log.info("Mercurial client import failed")
78 log.info("Mercurial client import failed")
79
79
80 if SubversionFactory and SvnRemote:
80 if SubversionFactory and SvnRemote:
81 svn_repo_cache = self.cache.get_cache_region(
81 svn_repo_cache = self.cache.get_cache_region(
82 'svn', region='repo_object')
82 'svn', region='repo_object')
83 svn_factory = SubversionFactory(svn_repo_cache)
83 svn_factory = SubversionFactory(svn_repo_cache)
84 self._svn_remote = SvnRemote(svn_factory, hg_factory=hg_factory)
84 self._svn_remote = SvnRemote(svn_factory, hg_factory=hg_factory)
85 else:
85 else:
86 log.info("Subversion client import failed")
86 log.info("Subversion client import failed")
87
87
88 self._vcsserver = VcsServer()
88 self._vcsserver = VcsServer()
89
89
90 def _initialize_cache(self):
90 def _initialize_cache(self):
91 cache_config = parse_cache_config_options(self.cache_config)
91 cache_config = parse_cache_config_options(self.cache_config)
92 log.info('Initializing beaker cache: %s' % cache_config)
92 log.info('Initializing beaker cache: %s' % cache_config)
93 self.cache = CacheManager(**cache_config)
93 self.cache = CacheManager(**cache_config)
94
94
95 def _configure_locale(self):
95 def _configure_locale(self):
96 if self.locale:
96 if self.locale:
97 log.info('Settings locale: `LC_ALL` to %s' % self.locale)
97 log.info('Settings locale: `LC_ALL` to %s' % self.locale)
98 else:
98 else:
99 log.info(
99 log.info(
100 'Configuring locale subsystem based on environment variables')
100 'Configuring locale subsystem based on environment variables')
101 try:
101 try:
102 # If self.locale is the empty string, then the locale
102 # If self.locale is the empty string, then the locale
103 # module will use the environment variables. See the
103 # module will use the environment variables. See the
104 # documentation of the package `locale`.
104 # documentation of the package `locale`.
105 locale.setlocale(locale.LC_ALL, self.locale)
105 locale.setlocale(locale.LC_ALL, self.locale)
106
106
107 language_code, encoding = locale.getlocale()
107 language_code, encoding = locale.getlocale()
108 log.info(
108 log.info(
109 'Locale set to language code "%s" with encoding "%s".',
109 'Locale set to language code "%s" with encoding "%s".',
110 language_code, encoding)
110 language_code, encoding)
111 except locale.Error:
111 except locale.Error:
112 log.exception(
112 log.exception(
113 'Cannot set locale, not configuring the locale system')
113 'Cannot set locale, not configuring the locale system')
114
114
115
115
116 class WsgiProxy(object):
116 class WsgiProxy(object):
117 def __init__(self, wsgi):
117 def __init__(self, wsgi):
118 self.wsgi = wsgi
118 self.wsgi = wsgi
119
119
120 def __call__(self, environ, start_response):
120 def __call__(self, environ, start_response):
121 input_data = environ['wsgi.input'].read()
121 input_data = environ['wsgi.input'].read()
122 input_data = msgpack.unpackb(input_data)
122 input_data = msgpack.unpackb(input_data)
123
123
124 error = None
124 error = None
125 try:
125 try:
126 data, status, headers = self.wsgi.handle(
126 data, status, headers = self.wsgi.handle(
127 input_data['environment'], input_data['input_data'],
127 input_data['environment'], input_data['input_data'],
128 *input_data['args'], **input_data['kwargs'])
128 *input_data['args'], **input_data['kwargs'])
129 except Exception as e:
129 except Exception as e:
130 data, status, headers = [], None, None
130 data, status, headers = [], None, None
131 error = {
131 error = {
132 'message': str(e),
132 'message': str(e),
133 '_vcs_kind': getattr(e, '_vcs_kind', None)
133 '_vcs_kind': getattr(e, '_vcs_kind', None)
134 }
134 }
135
135
136 start_response(200, {})
136 start_response(200, {})
137 return self._iterator(error, status, headers, data)
137 return self._iterator(error, status, headers, data)
138
138
139 def _iterator(self, error, status, headers, data):
139 def _iterator(self, error, status, headers, data):
140 initial_data = [
140 initial_data = [
141 error,
141 error,
142 status,
142 status,
143 headers,
143 headers,
144 ]
144 ]
145
145
146 for d in chain(initial_data, data):
146 for d in chain(initial_data, data):
147 yield msgpack.packb(d)
147 yield msgpack.packb(d)
148
148
149
149
150 class HTTPApplication(object):
150 class HTTPApplication(object):
151 ALLOWED_EXCEPTIONS = ('KeyError', 'URLError')
151 ALLOWED_EXCEPTIONS = ('KeyError', 'URLError')
152
152
153 remote_wsgi = remote_wsgi
153 remote_wsgi = remote_wsgi
154 _use_echo_app = False
154 _use_echo_app = False
155
155
156 def __init__(self, settings=None):
156 def __init__(self, settings=None):
157 self.config = Configurator(settings=settings)
157 self.config = Configurator(settings=settings)
158 locale = settings.get('locale', '') or 'en_US.UTF-8'
158 locale = settings.get('locale', '') or 'en_US.UTF-8'
159 vcs = VCS(locale=locale, cache_config=settings)
159 vcs = VCS(locale=locale, cache_config=settings)
160 self._remotes = {
160 self._remotes = {
161 'hg': vcs._hg_remote,
161 'hg': vcs._hg_remote,
162 'git': vcs._git_remote,
162 'git': vcs._git_remote,
163 'svn': vcs._svn_remote,
163 'svn': vcs._svn_remote,
164 'server': vcs._vcsserver,
164 'server': vcs._vcsserver,
165 }
165 }
166 if settings.get('dev.use_echo_app', 'false').lower() == 'true':
166 if settings.get('dev.use_echo_app', 'false').lower() == 'true':
167 self._use_echo_app = True
167 self._use_echo_app = True
168 log.warning("Using EchoApp for VCS operations.")
168 log.warning("Using EchoApp for VCS operations.")
169 self.remote_wsgi = remote_wsgi_stub
169 self.remote_wsgi = remote_wsgi_stub
170 self._configure_settings(settings)
170 self._configure_settings(settings)
171 self._configure()
171 self._configure()
172
172
173 def _configure_settings(self, app_settings):
173 def _configure_settings(self, app_settings):
174 """
174 """
175 Configure the settings module.
175 Configure the settings module.
176 """
176 """
177 git_path = app_settings.get('git_path', None)
177 git_path = app_settings.get('git_path', None)
178 if git_path:
178 if git_path:
179 settings.GIT_EXECUTABLE = git_path
179 settings.GIT_EXECUTABLE = git_path
180
180
181 def _configure(self):
181 def _configure(self):
182 self.config.add_renderer(
182 self.config.add_renderer(
183 name='msgpack',
183 name='msgpack',
184 factory=self._msgpack_renderer_factory)
184 factory=self._msgpack_renderer_factory)
185
185
186 self.config.add_route('service', '/_service')
186 self.config.add_route('service', '/_service')
187 self.config.add_route('status', '/status')
187 self.config.add_route('status', '/status')
188 self.config.add_route('hg_proxy', '/proxy/hg')
188 self.config.add_route('hg_proxy', '/proxy/hg')
189 self.config.add_route('git_proxy', '/proxy/git')
189 self.config.add_route('git_proxy', '/proxy/git')
190 self.config.add_route('vcs', '/{backend}')
190 self.config.add_route('vcs', '/{backend}')
191 self.config.add_route('stream_git', '/stream/git/*repo_name')
191 self.config.add_route('stream_git', '/stream/git/*repo_name')
192 self.config.add_route('stream_hg', '/stream/hg/*repo_name')
192 self.config.add_route('stream_hg', '/stream/hg/*repo_name')
193
193
194 self.config.add_view(
194 self.config.add_view(
195 self.status_view, route_name='status', renderer='json')
195 self.status_view, route_name='status', renderer='json')
196 self.config.add_view(
196 self.config.add_view(
197 self.service_view, route_name='service', renderer='msgpack')
197 self.service_view, route_name='service', renderer='msgpack')
198
198
199 self.config.add_view(self.hg_proxy(), route_name='hg_proxy')
199 self.config.add_view(self.hg_proxy(), route_name='hg_proxy')
200 self.config.add_view(self.git_proxy(), route_name='git_proxy')
200 self.config.add_view(self.git_proxy(), route_name='git_proxy')
201 self.config.add_view(
201 self.config.add_view(
202 self.vcs_view, route_name='vcs', renderer='msgpack')
202 self.vcs_view, route_name='vcs', renderer='msgpack')
203
203
204 self.config.add_view(self.hg_stream(), route_name='stream_hg')
204 self.config.add_view(self.hg_stream(), route_name='stream_hg')
205 self.config.add_view(self.git_stream(), route_name='stream_git')
205 self.config.add_view(self.git_stream(), route_name='stream_git')
206 self.config.add_view(
206 self.config.add_view(
207 self.handle_vcs_exception, context=Exception,
207 self.handle_vcs_exception, context=Exception,
208 custom_predicates=[self.is_vcs_exception])
208 custom_predicates=[self.is_vcs_exception])
209
209
210 self.config.add_view(
211 self.general_error_handler, context=Exception)
212
213
210 def wsgi_app(self):
214 def wsgi_app(self):
211 return self.config.make_wsgi_app()
215 return self.config.make_wsgi_app()
212
216
213 def vcs_view(self, request):
217 def vcs_view(self, request):
214 remote = self._remotes[request.matchdict['backend']]
218 remote = self._remotes[request.matchdict['backend']]
215 payload = msgpack.unpackb(request.body, use_list=True)
219 payload = msgpack.unpackb(request.body, use_list=True)
216 method = payload.get('method')
220 method = payload.get('method')
217 params = payload.get('params')
221 params = payload.get('params')
218 wire = params.get('wire')
222 wire = params.get('wire')
219 args = params.get('args')
223 args = params.get('args')
220 kwargs = params.get('kwargs')
224 kwargs = params.get('kwargs')
221 if wire:
225 if wire:
222 try:
226 try:
223 wire['context'] = uuid.UUID(wire['context'])
227 wire['context'] = uuid.UUID(wire['context'])
224 except KeyError:
228 except KeyError:
225 pass
229 pass
226 args.insert(0, wire)
230 args.insert(0, wire)
227
231
228 try:
232 try:
229 resp = getattr(remote, method)(*args, **kwargs)
233 resp = getattr(remote, method)(*args, **kwargs)
230 except Exception as e:
234 except Exception as e:
231 tb_info = traceback.format_exc()
235 tb_info = traceback.format_exc()
232
236
233 type_ = e.__class__.__name__
237 type_ = e.__class__.__name__
234 if type_ not in self.ALLOWED_EXCEPTIONS:
238 if type_ not in self.ALLOWED_EXCEPTIONS:
235 type_ = None
239 type_ = None
236
240
237 resp = {
241 resp = {
238 'id': payload.get('id'),
242 'id': payload.get('id'),
239 'error': {
243 'error': {
240 'message': e.message,
244 'message': e.message,
241 'traceback': tb_info,
245 'traceback': tb_info,
242 'type': type_
246 'type': type_
243 }
247 }
244 }
248 }
245 try:
249 try:
246 resp['error']['_vcs_kind'] = e._vcs_kind
250 resp['error']['_vcs_kind'] = e._vcs_kind
247 except AttributeError:
251 except AttributeError:
248 pass
252 pass
249 else:
253 else:
250 resp = {
254 resp = {
251 'id': payload.get('id'),
255 'id': payload.get('id'),
252 'result': resp
256 'result': resp
253 }
257 }
254
258
255 return resp
259 return resp
256
260
257 def status_view(self, request):
261 def status_view(self, request):
258 return {'status': 'OK'}
262 return {'status': 'OK'}
259
263
260 def service_view(self, request):
264 def service_view(self, request):
261 import vcsserver
265 import vcsserver
262 payload = msgpack.unpackb(request.body, use_list=True)
266 payload = msgpack.unpackb(request.body, use_list=True)
263 resp = {
267 resp = {
264 'id': payload.get('id'),
268 'id': payload.get('id'),
265 'result': dict(
269 'result': dict(
266 version=vcsserver.__version__,
270 version=vcsserver.__version__,
267 config={},
271 config={},
268 payload=payload,
272 payload=payload,
269 )
273 )
270 }
274 }
271 return resp
275 return resp
272
276
273 def _msgpack_renderer_factory(self, info):
277 def _msgpack_renderer_factory(self, info):
274 def _render(value, system):
278 def _render(value, system):
275 value = msgpack.packb(value)
279 value = msgpack.packb(value)
276 request = system.get('request')
280 request = system.get('request')
277 if request is not None:
281 if request is not None:
278 response = request.response
282 response = request.response
279 ct = response.content_type
283 ct = response.content_type
280 if ct == response.default_content_type:
284 if ct == response.default_content_type:
281 response.content_type = 'application/x-msgpack'
285 response.content_type = 'application/x-msgpack'
282 return value
286 return value
283 return _render
287 return _render
284
288
285 def hg_proxy(self):
289 def hg_proxy(self):
286 @wsgiapp
290 @wsgiapp
287 def _hg_proxy(environ, start_response):
291 def _hg_proxy(environ, start_response):
288 app = WsgiProxy(self.remote_wsgi.HgRemoteWsgi())
292 app = WsgiProxy(self.remote_wsgi.HgRemoteWsgi())
289 return app(environ, start_response)
293 return app(environ, start_response)
290 return _hg_proxy
294 return _hg_proxy
291
295
292 def git_proxy(self):
296 def git_proxy(self):
293 @wsgiapp
297 @wsgiapp
294 def _git_proxy(environ, start_response):
298 def _git_proxy(environ, start_response):
295 app = WsgiProxy(self.remote_wsgi.GitRemoteWsgi())
299 app = WsgiProxy(self.remote_wsgi.GitRemoteWsgi())
296 return app(environ, start_response)
300 return app(environ, start_response)
297 return _git_proxy
301 return _git_proxy
298
302
299 def hg_stream(self):
303 def hg_stream(self):
300 if self._use_echo_app:
304 if self._use_echo_app:
301 @wsgiapp
305 @wsgiapp
302 def _hg_stream(environ, start_response):
306 def _hg_stream(environ, start_response):
303 app = EchoApp('fake_path', 'fake_name', None)
307 app = EchoApp('fake_path', 'fake_name', None)
304 return app(environ, start_response)
308 return app(environ, start_response)
305 return _hg_stream
309 return _hg_stream
306 else:
310 else:
307 @wsgiapp
311 @wsgiapp
308 def _hg_stream(environ, start_response):
312 def _hg_stream(environ, start_response):
309 repo_path = environ['HTTP_X_RC_REPO_PATH']
313 repo_path = environ['HTTP_X_RC_REPO_PATH']
310 repo_name = environ['HTTP_X_RC_REPO_NAME']
314 repo_name = environ['HTTP_X_RC_REPO_NAME']
311 packed_config = base64.b64decode(
315 packed_config = base64.b64decode(
312 environ['HTTP_X_RC_REPO_CONFIG'])
316 environ['HTTP_X_RC_REPO_CONFIG'])
313 config = msgpack.unpackb(packed_config)
317 config = msgpack.unpackb(packed_config)
314 app = scm_app.create_hg_wsgi_app(
318 app = scm_app.create_hg_wsgi_app(
315 repo_path, repo_name, config)
319 repo_path, repo_name, config)
316
320
317 # Consitent path information for hgweb
321 # Consitent path information for hgweb
318 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
322 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
319 environ['REPO_NAME'] = repo_name
323 environ['REPO_NAME'] = repo_name
320 return app(environ, ResponseFilter(start_response))
324 return app(environ, ResponseFilter(start_response))
321 return _hg_stream
325 return _hg_stream
322
326
323 def git_stream(self):
327 def git_stream(self):
324 if self._use_echo_app:
328 if self._use_echo_app:
325 @wsgiapp
329 @wsgiapp
326 def _git_stream(environ, start_response):
330 def _git_stream(environ, start_response):
327 app = EchoApp('fake_path', 'fake_name', None)
331 app = EchoApp('fake_path', 'fake_name', None)
328 return app(environ, start_response)
332 return app(environ, start_response)
329 return _git_stream
333 return _git_stream
330 else:
334 else:
331 @wsgiapp
335 @wsgiapp
332 def _git_stream(environ, start_response):
336 def _git_stream(environ, start_response):
333 repo_path = environ['HTTP_X_RC_REPO_PATH']
337 repo_path = environ['HTTP_X_RC_REPO_PATH']
334 repo_name = environ['HTTP_X_RC_REPO_NAME']
338 repo_name = environ['HTTP_X_RC_REPO_NAME']
335 packed_config = base64.b64decode(
339 packed_config = base64.b64decode(
336 environ['HTTP_X_RC_REPO_CONFIG'])
340 environ['HTTP_X_RC_REPO_CONFIG'])
337 config = msgpack.unpackb(packed_config)
341 config = msgpack.unpackb(packed_config)
338
342
339 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
343 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
340 app = scm_app.create_git_wsgi_app(
344 app = scm_app.create_git_wsgi_app(
341 repo_path, repo_name, config)
345 repo_path, repo_name, config)
342 return app(environ, start_response)
346 return app(environ, start_response)
343 return _git_stream
347 return _git_stream
344
348
345 def is_vcs_exception(self, context, request):
349 def is_vcs_exception(self, context, request):
346 """
350 """
347 View predicate that returns true if the context object is a VCS
351 View predicate that returns true if the context object is a VCS
348 exception.
352 exception.
349 """
353 """
350 return hasattr(context, '_vcs_kind')
354 return hasattr(context, '_vcs_kind')
351
355
352 def handle_vcs_exception(self, exception, request):
356 def handle_vcs_exception(self, exception, request):
353 if exception._vcs_kind == 'repo_locked':
357 if exception._vcs_kind == 'repo_locked':
354 # Get custom repo-locked status code if present.
358 # Get custom repo-locked status code if present.
355 status_code = request.headers.get('X-RC-Locked-Status-Code')
359 status_code = request.headers.get('X-RC-Locked-Status-Code')
356 return HTTPRepoLocked(
360 return HTTPRepoLocked(
357 title=exception.message, status_code=status_code)
361 title=exception.message, status_code=status_code)
358
362
359 # Re-raise exception if we can not handle it.
363 # Re-raise exception if we can not handle it.
360 raise exception
364 raise exception
361
365
366 def general_error_handler(self, exception, request):
367 log.exception(
368 'error occurred handling this request for path: %s',
369 request.path)
370 raise exception
371
362
372
363 class ResponseFilter(object):
373 class ResponseFilter(object):
364
374
365 def __init__(self, start_response):
375 def __init__(self, start_response):
366 self._start_response = start_response
376 self._start_response = start_response
367
377
368 def __call__(self, status, response_headers, exc_info=None):
378 def __call__(self, status, response_headers, exc_info=None):
369 headers = tuple(
379 headers = tuple(
370 (h, v) for h, v in response_headers
380 (h, v) for h, v in response_headers
371 if not wsgiref.util.is_hop_by_hop(h))
381 if not wsgiref.util.is_hop_by_hop(h))
372 return self._start_response(status, headers, exc_info)
382 return self._start_response(status, headers, exc_info)
373
383
374
384
375 def main(global_config, **settings):
385 def main(global_config, **settings):
376 if MercurialFactory:
386 if MercurialFactory:
377 hgpatches.patch_largefiles_capabilities()
387 hgpatches.patch_largefiles_capabilities()
378 hgpatches.patch_subrepo_type_mapping()
388 hgpatches.patch_subrepo_type_mapping()
379 app = HTTPApplication(settings=settings)
389 app = HTTPApplication(settings=settings)
380 return app.wsgi_app()
390 return app.wsgi_app()
General Comments 0
You need to be logged in to leave comments. Login now