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