##// END OF EJS Templates
caches: set file based cache as default for vcsserver
marcink -
r776:c6b590ae default
parent child Browse files
Show More
@@ -1,87 +1,91 b''
1 1 ################################################################################
2 2 # RhodeCode VCSServer with HTTP Backend - configuration #
3 3 ################################################################################
4 4
5 5
6 6 [server:main]
7 7 ## COMMON ##
8 8 host = 0.0.0.0
9 9 port = 9900
10 10
11 11 use = egg:waitress#main
12 12
13 13
14 14 [app:main]
15 15 use = egg:rhodecode-vcsserver
16 16
17 17 pyramid.default_locale_name = en
18 18 pyramid.includes =
19 19
20 20 ## default locale used by VCS systems
21 21 locale = en_US.UTF-8
22 22
23 23
24 24 ## path to binaries for vcsserver, it should be set by the installer
25 25 ## at installation time, e.g /home/user/vcsserver-1/profile/bin
26 26 core.binary_dir = ""
27 27
28 28 ## Custom exception store path, defaults to TMPDIR
29 29 ## This is used to store exception from RhodeCode in shared directory
30 30 #exception_tracker.store_path =
31 31
32 32 ## Default cache dir for caches. Putting this into a ramdisk
33 33 ## can boost performance, eg. /tmpfs/data_ramdisk, however this directory might require
34 34 ## large amount of space
35 35 cache_dir = %(here)s/rcdev/data
36 36
37 37 ## cache region for storing repo_objects cache
38 rc_cache.repo_object.backend = dogpile.cache.rc.memory_lru
39 ## cache auto-expires after N seconds
40 rc_cache.repo_object.expiration_time = 300
38 rc_cache.repo_object.backend = dogpile.cache.rc.file_namespace
39 ## cache auto-expires after N seconds (2592000 == 30 days)
40 rc_cache.repo_object.expiration_time = 2592000
41
42 ## cache file store path, if empty set automatically to tmp dir location
43 #rc_cache.repo_object.arguments.filename = /tmp/vcsserver_cache.db
44
41 45 ## max size of LRU, old values will be discarded if the size of cache reaches max_size
42 46 rc_cache.repo_object.max_size = 100
43 47
44 48
45 49 ################################
46 50 ### LOGGING CONFIGURATION ####
47 51 ################################
48 52 [loggers]
49 53 keys = root, vcsserver
50 54
51 55 [handlers]
52 56 keys = console
53 57
54 58 [formatters]
55 59 keys = generic
56 60
57 61 #############
58 62 ## LOGGERS ##
59 63 #############
60 64 [logger_root]
61 65 level = NOTSET
62 66 handlers = console
63 67
64 68 [logger_vcsserver]
65 69 level = DEBUG
66 70 handlers =
67 71 qualname = vcsserver
68 72 propagate = 1
69 73
70 74
71 75 ##############
72 76 ## HANDLERS ##
73 77 ##############
74 78
75 79 [handler_console]
76 80 class = StreamHandler
77 81 args = (sys.stderr,)
78 82 level = DEBUG
79 83 formatter = generic
80 84
81 85 ################
82 86 ## FORMATTERS ##
83 87 ################
84 88
85 89 [formatter_generic]
86 90 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
87 91 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,108 +1,112 b''
1 1 ################################################################################
2 2 # RhodeCode VCSServer with HTTP Backend - configuration #
3 3 ################################################################################
4 4
5 5
6 6 [server:main]
7 7 ## COMMON ##
8 8 host = 127.0.0.1
9 9 port = 9900
10 10
11 11
12 12 ##########################
13 13 ## GUNICORN WSGI SERVER ##
14 14 ##########################
15 15 ## run with gunicorn --log-config vcsserver.ini --paste vcsserver.ini
16 16 use = egg:gunicorn#main
17 17 ## Sets the number of process workers. Recommended
18 18 ## value is (2 * NUMBER_OF_CPUS + 1), eg 2CPU = 5 workers
19 19 workers = 2
20 20 ## process name
21 21 proc_name = rhodecode_vcsserver
22 22 ## type of worker class, currently `sync` is the only option allowed.
23 23 worker_class = sync
24 24 ## The maximum number of simultaneous clients. Valid only for Gevent
25 25 #worker_connections = 10
26 26 ## max number of requests that worker will handle before being gracefully
27 27 ## restarted, could prevent memory leaks
28 28 max_requests = 1000
29 29 max_requests_jitter = 30
30 30 ## amount of time a worker can spend with handling a request before it
31 31 ## gets killed and restarted. Set to 6hrs
32 32 timeout = 21600
33 33
34 34
35 35 [app:main]
36 36 use = egg:rhodecode-vcsserver
37 37
38 38 pyramid.default_locale_name = en
39 39 pyramid.includes =
40 40
41 41 ## default locale used by VCS systems
42 42 locale = en_US.UTF-8
43 43
44 44
45 45 ## path to binaries for vcsserver, it should be set by the installer
46 46 ## at installation time, e.g /home/user/vcsserver-1/profile/bin
47 47 core.binary_dir = ""
48 48
49 49 ## Custom exception store path, defaults to TMPDIR
50 50 ## This is used to store exception from RhodeCode in shared directory
51 51 #exception_tracker.store_path =
52 52
53 53 ## Default cache dir for caches. Putting this into a ramdisk
54 54 ## can boost performance, eg. /tmpfs/data_ramdisk, however this directory might require
55 55 ## large amount of space
56 56 cache_dir = %(here)s/rcdev/data
57 57
58 58 ## cache region for storing repo_objects cache
59 rc_cache.repo_object.backend = dogpile.cache.rc.memory_lru
60 ## cache auto-expires after N seconds
61 rc_cache.repo_object.expiration_time = 300
59 rc_cache.repo_object.backend = dogpile.cache.rc.file_namespace
60 ## cache auto-expires after N seconds (2592000 == 30 days)
61 rc_cache.repo_object.expiration_time = 2592000
62
63 ## cache file store path, if empty set automatically to tmp dir location
64 #rc_cache.repo_object.arguments.filename = /tmp/vcsserver_cache.db
65
62 66 ## max size of LRU, old values will be discarded if the size of cache reaches max_size
63 67 rc_cache.repo_object.max_size = 100
64 68
65 69
66 70 ################################
67 71 ### LOGGING CONFIGURATION ####
68 72 ################################
69 73 [loggers]
70 74 keys = root, vcsserver
71 75
72 76 [handlers]
73 77 keys = console
74 78
75 79 [formatters]
76 80 keys = generic
77 81
78 82 #############
79 83 ## LOGGERS ##
80 84 #############
81 85 [logger_root]
82 86 level = NOTSET
83 87 handlers = console
84 88
85 89 [logger_vcsserver]
86 90 level = DEBUG
87 91 handlers =
88 92 qualname = vcsserver
89 93 propagate = 1
90 94
91 95
92 96 ##############
93 97 ## HANDLERS ##
94 98 ##############
95 99
96 100 [handler_console]
97 101 class = StreamHandler
98 102 args = (sys.stderr,)
99 103 level = DEBUG
100 104 formatter = generic
101 105
102 106 ################
103 107 ## FORMATTERS ##
104 108 ################
105 109
106 110 [formatter_generic]
107 111 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
108 112 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,667 +1,667 b''
1 1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 2 # Copyright (C) 2014-2019 RhodeCode 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 os
19 19 import sys
20 20 import base64
21 21 import locale
22 22 import logging
23 23 import uuid
24 24 import wsgiref.util
25 25 import traceback
26 26 import tempfile
27 27 from itertools import chain
28 28 from cStringIO import StringIO
29 29
30 30 import simplejson as json
31 31 import msgpack
32 32 from pyramid.config import Configurator
33 33 from pyramid.settings import asbool, aslist
34 34 from pyramid.wsgi import wsgiapp
35 35 from pyramid.compat import configparser
36 36 from pyramid.response import Response
37 37
38 38 from vcsserver.utils import safe_int
39 39
40 40 log = logging.getLogger(__name__)
41 41
42 42 # due to Mercurial/glibc2.27 problems we need to detect if locale settings are
43 43 # causing problems and "fix" it in case they do and fallback to LC_ALL = C
44 44
45 45 try:
46 46 locale.setlocale(locale.LC_ALL, '')
47 47 except locale.Error as e:
48 48 log.error(
49 49 'LOCALE ERROR: failed to set LC_ALL, fallback to LC_ALL=C, org error: %s', e)
50 50 os.environ['LC_ALL'] = 'C'
51 51
52 52 import vcsserver
53 53 from vcsserver import remote_wsgi, scm_app, settings, hgpatches
54 54 from vcsserver.git_lfs.app import GIT_LFS_CONTENT_TYPE, GIT_LFS_PROTO_PAT
55 55 from vcsserver.echo_stub import remote_wsgi as remote_wsgi_stub
56 56 from vcsserver.echo_stub.echo_app import EchoApp
57 57 from vcsserver.exceptions import HTTPRepoLocked, HTTPRepoBranchProtected
58 58 from vcsserver.lib.exc_tracking import store_exception
59 59 from vcsserver.server import VcsServer
60 60
61 61 try:
62 62 from vcsserver.git import GitFactory, GitRemote
63 63 except ImportError:
64 64 GitFactory = None
65 65 GitRemote = None
66 66
67 67 try:
68 68 from vcsserver.hg import MercurialFactory, HgRemote
69 69 except ImportError:
70 70 MercurialFactory = None
71 71 HgRemote = None
72 72
73 73 try:
74 74 from vcsserver.svn import SubversionFactory, SvnRemote
75 75 except ImportError:
76 76 SubversionFactory = None
77 77 SvnRemote = None
78 78
79 79
80 80 def _is_request_chunked(environ):
81 81 stream = environ.get('HTTP_TRANSFER_ENCODING', '') == 'chunked'
82 82 return stream
83 83
84 84
85 85 def _int_setting(settings, name, default):
86 86 settings[name] = int(settings.get(name, default))
87 87 return settings[name]
88 88
89 89
90 90 def _bool_setting(settings, name, default):
91 91 input_val = settings.get(name, default)
92 92 if isinstance(input_val, unicode):
93 93 input_val = input_val.encode('utf8')
94 94 settings[name] = asbool(input_val)
95 95 return settings[name]
96 96
97 97
98 98 def _list_setting(settings, name, default):
99 99 raw_value = settings.get(name, default)
100 100
101 101 # Otherwise we assume it uses pyramids space/newline separation.
102 102 settings[name] = aslist(raw_value)
103 103 return settings[name]
104 104
105 105
106 106 def _string_setting(settings, name, default, lower=True, default_when_empty=False):
107 107 value = settings.get(name, default)
108 108
109 109 if default_when_empty and not value:
110 110 # use default value when value is empty
111 111 value = default
112 112
113 113 if lower:
114 114 value = value.lower()
115 115 settings[name] = value
116 116 return settings[name]
117 117
118 118
119 119 class VCS(object):
120 120 def __init__(self, locale_conf=None, cache_config=None):
121 121 self.locale = locale_conf
122 122 self.cache_config = cache_config
123 123 self._configure_locale()
124 124
125 125 if GitFactory and GitRemote:
126 126 git_factory = GitFactory()
127 127 self._git_remote = GitRemote(git_factory)
128 128 else:
129 129 log.info("Git client import failed")
130 130
131 131 if MercurialFactory and HgRemote:
132 132 hg_factory = MercurialFactory()
133 133 self._hg_remote = HgRemote(hg_factory)
134 134 else:
135 135 log.info("Mercurial client import failed")
136 136
137 137 if SubversionFactory and SvnRemote:
138 138 svn_factory = SubversionFactory()
139 139
140 140 # hg factory is used for svn url validation
141 141 hg_factory = MercurialFactory()
142 142 self._svn_remote = SvnRemote(svn_factory, hg_factory=hg_factory)
143 143 else:
144 144 log.info("Subversion client import failed")
145 145
146 146 self._vcsserver = VcsServer()
147 147
148 148 def _configure_locale(self):
149 149 if self.locale:
150 150 log.info('Settings locale: `LC_ALL` to %s', self.locale)
151 151 else:
152 152 log.info(
153 153 'Configuring locale subsystem based on environment variables')
154 154 try:
155 155 # If self.locale is the empty string, then the locale
156 156 # module will use the environment variables. See the
157 157 # documentation of the package `locale`.
158 158 locale.setlocale(locale.LC_ALL, self.locale)
159 159
160 160 language_code, encoding = locale.getlocale()
161 161 log.info(
162 162 'Locale set to language code "%s" with encoding "%s".',
163 163 language_code, encoding)
164 164 except locale.Error:
165 165 log.exception(
166 166 'Cannot set locale, not configuring the locale system')
167 167
168 168
169 169 class WsgiProxy(object):
170 170 def __init__(self, wsgi):
171 171 self.wsgi = wsgi
172 172
173 173 def __call__(self, environ, start_response):
174 174 input_data = environ['wsgi.input'].read()
175 175 input_data = msgpack.unpackb(input_data)
176 176
177 177 error = None
178 178 try:
179 179 data, status, headers = self.wsgi.handle(
180 180 input_data['environment'], input_data['input_data'],
181 181 *input_data['args'], **input_data['kwargs'])
182 182 except Exception as e:
183 183 data, status, headers = [], None, None
184 184 error = {
185 185 'message': str(e),
186 186 '_vcs_kind': getattr(e, '_vcs_kind', None)
187 187 }
188 188
189 189 start_response(200, {})
190 190 return self._iterator(error, status, headers, data)
191 191
192 192 def _iterator(self, error, status, headers, data):
193 193 initial_data = [
194 194 error,
195 195 status,
196 196 headers,
197 197 ]
198 198
199 199 for d in chain(initial_data, data):
200 200 yield msgpack.packb(d)
201 201
202 202
203 203 def not_found(request):
204 204 return {'status': '404 NOT FOUND'}
205 205
206 206
207 207 class VCSViewPredicate(object):
208 208 def __init__(self, val, config):
209 209 self.remotes = val
210 210
211 211 def text(self):
212 212 return 'vcs view method = %s' % (self.remotes.keys(),)
213 213
214 214 phash = text
215 215
216 216 def __call__(self, context, request):
217 217 """
218 218 View predicate that returns true if given backend is supported by
219 219 defined remotes.
220 220 """
221 221 backend = request.matchdict.get('backend')
222 222 return backend in self.remotes
223 223
224 224
225 225 class HTTPApplication(object):
226 226 ALLOWED_EXCEPTIONS = ('KeyError', 'URLError')
227 227
228 228 remote_wsgi = remote_wsgi
229 229 _use_echo_app = False
230 230
231 231 def __init__(self, settings=None, global_config=None):
232 232 self._sanitize_settings_and_apply_defaults(settings)
233 233
234 234 self.config = Configurator(settings=settings)
235 235 self.global_config = global_config
236 236 self.config.include('vcsserver.lib.rc_cache')
237 237
238 238 settings_locale = settings.get('locale', '') or 'en_US.UTF-8'
239 239 vcs = VCS(locale_conf=settings_locale, cache_config=settings)
240 240 self._remotes = {
241 241 'hg': vcs._hg_remote,
242 242 'git': vcs._git_remote,
243 243 'svn': vcs._svn_remote,
244 244 'server': vcs._vcsserver,
245 245 }
246 246 if settings.get('dev.use_echo_app', 'false').lower() == 'true':
247 247 self._use_echo_app = True
248 248 log.warning("Using EchoApp for VCS operations.")
249 249 self.remote_wsgi = remote_wsgi_stub
250 250
251 251 self._configure_settings(global_config, settings)
252 252 self._configure()
253 253
254 254 def _configure_settings(self, global_config, app_settings):
255 255 """
256 256 Configure the settings module.
257 257 """
258 258 settings_merged = global_config.copy()
259 259 settings_merged.update(app_settings)
260 260
261 261 git_path = app_settings.get('git_path', None)
262 262 if git_path:
263 263 settings.GIT_EXECUTABLE = git_path
264 264 binary_dir = app_settings.get('core.binary_dir', None)
265 265 if binary_dir:
266 266 settings.BINARY_DIR = binary_dir
267 267
268 268 # Store the settings to make them available to other modules.
269 269 vcsserver.PYRAMID_SETTINGS = settings_merged
270 270 vcsserver.CONFIG = settings_merged
271 271
272 272 def _sanitize_settings_and_apply_defaults(self, settings):
273 273 temp_store = tempfile.gettempdir()
274 274 default_cache_dir = os.path.join(temp_store, 'rc_cache')
275 275
276 276 # save default, cache dir, and use it for all backends later.
277 277 default_cache_dir = _string_setting(
278 278 settings,
279 279 'cache_dir',
280 280 default_cache_dir, lower=False, default_when_empty=True)
281 281
282 282 # ensure we have our dir created
283 283 if not os.path.isdir(default_cache_dir):
284 284 os.makedirs(default_cache_dir, mode=0o755)
285 285
286 286 # exception store cache
287 287 _string_setting(
288 288 settings,
289 289 'exception_tracker.store_path',
290 290 temp_store, lower=False, default_when_empty=True)
291 291
292 292 # repo_object cache
293 293 _string_setting(
294 294 settings,
295 295 'rc_cache.repo_object.backend',
296 'dogpile.cache.rc.memory_lru')
296 'dogpile.cache.rc.file_namespace', lower=False)
297 297 _int_setting(
298 298 settings,
299 299 'rc_cache.repo_object.expiration_time',
300 300)
301 _int_setting(
300 30 * 24 * 60 * 60)
301 _string_setting(
302 302 settings,
303 'rc_cache.repo_object.max_size',
304 1024)
303 'rc_cache.repo_object.arguments.filename',
304 os.path.join(default_cache_dir, 'vcsserver_cache_1'), lower=False)
305 305
306 306 def _configure(self):
307 307 self.config.add_renderer(name='msgpack', factory=self._msgpack_renderer_factory)
308 308
309 309 self.config.add_route('service', '/_service')
310 310 self.config.add_route('status', '/status')
311 311 self.config.add_route('hg_proxy', '/proxy/hg')
312 312 self.config.add_route('git_proxy', '/proxy/git')
313 313
314 314 # rpc methods
315 315 self.config.add_route('vcs', '/{backend}')
316 316
317 317 # streaming rpc remote methods
318 318 self.config.add_route('vcs_stream', '/{backend}/stream')
319 319
320 320 # vcs operations clone/push as streaming
321 321 self.config.add_route('stream_git', '/stream/git/*repo_name')
322 322 self.config.add_route('stream_hg', '/stream/hg/*repo_name')
323 323
324 324 self.config.add_view(self.status_view, route_name='status', renderer='json')
325 325 self.config.add_view(self.service_view, route_name='service', renderer='msgpack')
326 326
327 327 self.config.add_view(self.hg_proxy(), route_name='hg_proxy')
328 328 self.config.add_view(self.git_proxy(), route_name='git_proxy')
329 329 self.config.add_view(self.vcs_view, route_name='vcs', renderer='msgpack',
330 330 vcs_view=self._remotes)
331 331 self.config.add_view(self.vcs_stream_view, route_name='vcs_stream',
332 332 vcs_view=self._remotes)
333 333
334 334 self.config.add_view(self.hg_stream(), route_name='stream_hg')
335 335 self.config.add_view(self.git_stream(), route_name='stream_git')
336 336
337 337 self.config.add_view_predicate('vcs_view', VCSViewPredicate)
338 338
339 339 self.config.add_notfound_view(not_found, renderer='json')
340 340
341 341 self.config.add_view(self.handle_vcs_exception, context=Exception)
342 342
343 343 self.config.add_tween(
344 344 'vcsserver.tweens.request_wrapper.RequestWrapperTween',
345 345 )
346 346 self.config.add_request_method(
347 347 'vcsserver.lib.request_counter.get_request_counter',
348 348 'request_count')
349 349
350 350 def wsgi_app(self):
351 351 return self.config.make_wsgi_app()
352 352
353 353 def _vcs_view_params(self, request):
354 354 remote = self._remotes[request.matchdict['backend']]
355 355 payload = msgpack.unpackb(request.body, use_list=True)
356 356 method = payload.get('method')
357 357 params = payload['params']
358 358 wire = params.get('wire')
359 359 args = params.get('args')
360 360 kwargs = params.get('kwargs')
361 361 context_uid = None
362 362
363 363 if wire:
364 364 try:
365 365 wire['context'] = context_uid = uuid.UUID(wire['context'])
366 366 except KeyError:
367 367 pass
368 368 args.insert(0, wire)
369 369 repo_state_uid = wire.get('repo_state_uid') if wire else None
370 370
371 371 # NOTE(marcink): trading complexity for slight performance
372 372 if log.isEnabledFor(logging.DEBUG):
373 373 no_args_methods = [
374 374 'archive_repo'
375 375 ]
376 376 if method in no_args_methods:
377 377 call_args = ''
378 378 else:
379 379 call_args = args[1:]
380 380
381 381 log.debug('method requested:%s with args:%s kwargs:%s context_uid: %s, repo_state_uid:%s',
382 382 method, call_args, kwargs, context_uid, repo_state_uid)
383 383
384 384 return payload, remote, method, args, kwargs
385 385
386 386 def vcs_view(self, request):
387 387
388 388 payload, remote, method, args, kwargs = self._vcs_view_params(request)
389 389 payload_id = payload.get('id')
390 390
391 391 try:
392 392 resp = getattr(remote, method)(*args, **kwargs)
393 393 except Exception as e:
394 394 exc_info = list(sys.exc_info())
395 395 exc_type, exc_value, exc_traceback = exc_info
396 396
397 397 org_exc = getattr(e, '_org_exc', None)
398 398 org_exc_name = None
399 399 org_exc_tb = ''
400 400 if org_exc:
401 401 org_exc_name = org_exc.__class__.__name__
402 402 org_exc_tb = getattr(e, '_org_exc_tb', '')
403 403 # replace our "faked" exception with our org
404 404 exc_info[0] = org_exc.__class__
405 405 exc_info[1] = org_exc
406 406
407 407 store_exception(id(exc_info), exc_info)
408 408
409 409 tb_info = ''.join(
410 410 traceback.format_exception(exc_type, exc_value, exc_traceback))
411 411
412 412 type_ = e.__class__.__name__
413 413 if type_ not in self.ALLOWED_EXCEPTIONS:
414 414 type_ = None
415 415
416 416 resp = {
417 417 'id': payload_id,
418 418 'error': {
419 419 'message': e.message,
420 420 'traceback': tb_info,
421 421 'org_exc': org_exc_name,
422 422 'org_exc_tb': org_exc_tb,
423 423 'type': type_
424 424 }
425 425 }
426 426 try:
427 427 resp['error']['_vcs_kind'] = getattr(e, '_vcs_kind', None)
428 428 except AttributeError:
429 429 pass
430 430 else:
431 431 resp = {
432 432 'id': payload_id,
433 433 'result': resp
434 434 }
435 435
436 436 return resp
437 437
438 438 def vcs_stream_view(self, request):
439 439 payload, remote, method, args, kwargs = self._vcs_view_params(request)
440 440 # this method has a stream: marker we remove it here
441 441 method = method.split('stream:')[-1]
442 442 chunk_size = safe_int(payload.get('chunk_size')) or 4096
443 443
444 444 try:
445 445 resp = getattr(remote, method)(*args, **kwargs)
446 446 except Exception as e:
447 447 raise
448 448
449 449 def get_chunked_data(method_resp):
450 450 stream = StringIO(method_resp)
451 451 while 1:
452 452 chunk = stream.read(chunk_size)
453 453 if not chunk:
454 454 break
455 455 yield chunk
456 456
457 457 response = Response(app_iter=get_chunked_data(resp))
458 458 response.content_type = 'application/octet-stream'
459 459
460 460 return response
461 461
462 462 def status_view(self, request):
463 463 import vcsserver
464 464 return {'status': 'OK', 'vcsserver_version': vcsserver.__version__,
465 465 'pid': os.getpid()}
466 466
467 467 def service_view(self, request):
468 468 import vcsserver
469 469
470 470 payload = msgpack.unpackb(request.body, use_list=True)
471 471
472 472 try:
473 473 path = self.global_config['__file__']
474 474 config = configparser.ConfigParser()
475 475 config.read(path)
476 476 parsed_ini = config
477 477 if parsed_ini.has_section('server:main'):
478 478 parsed_ini = dict(parsed_ini.items('server:main'))
479 479 except Exception:
480 480 log.exception('Failed to read .ini file for display')
481 481 parsed_ini = {}
482 482
483 483 resp = {
484 484 'id': payload.get('id'),
485 485 'result': dict(
486 486 version=vcsserver.__version__,
487 487 config=parsed_ini,
488 488 payload=payload,
489 489 )
490 490 }
491 491 return resp
492 492
493 493 def _msgpack_renderer_factory(self, info):
494 494 def _render(value, system):
495 495 request = system.get('request')
496 496 if request is not None:
497 497 response = request.response
498 498 ct = response.content_type
499 499 if ct == response.default_content_type:
500 500 response.content_type = 'application/x-msgpack'
501 501 return msgpack.packb(value)
502 502 return _render
503 503
504 504 def set_env_from_config(self, environ, config):
505 505 dict_conf = {}
506 506 try:
507 507 for elem in config:
508 508 if elem[0] == 'rhodecode':
509 509 dict_conf = json.loads(elem[2])
510 510 break
511 511 except Exception:
512 512 log.exception('Failed to fetch SCM CONFIG')
513 513 return
514 514
515 515 username = dict_conf.get('username')
516 516 if username:
517 517 environ['REMOTE_USER'] = username
518 518 # mercurial specific, some extension api rely on this
519 519 environ['HGUSER'] = username
520 520
521 521 ip = dict_conf.get('ip')
522 522 if ip:
523 523 environ['REMOTE_HOST'] = ip
524 524
525 525 if _is_request_chunked(environ):
526 526 # set the compatibility flag for webob
527 527 environ['wsgi.input_terminated'] = True
528 528
529 529 def hg_proxy(self):
530 530 @wsgiapp
531 531 def _hg_proxy(environ, start_response):
532 532 app = WsgiProxy(self.remote_wsgi.HgRemoteWsgi())
533 533 return app(environ, start_response)
534 534 return _hg_proxy
535 535
536 536 def git_proxy(self):
537 537 @wsgiapp
538 538 def _git_proxy(environ, start_response):
539 539 app = WsgiProxy(self.remote_wsgi.GitRemoteWsgi())
540 540 return app(environ, start_response)
541 541 return _git_proxy
542 542
543 543 def hg_stream(self):
544 544 if self._use_echo_app:
545 545 @wsgiapp
546 546 def _hg_stream(environ, start_response):
547 547 app = EchoApp('fake_path', 'fake_name', None)
548 548 return app(environ, start_response)
549 549 return _hg_stream
550 550 else:
551 551 @wsgiapp
552 552 def _hg_stream(environ, start_response):
553 553 log.debug('http-app: handling hg stream')
554 554 repo_path = environ['HTTP_X_RC_REPO_PATH']
555 555 repo_name = environ['HTTP_X_RC_REPO_NAME']
556 556 packed_config = base64.b64decode(
557 557 environ['HTTP_X_RC_REPO_CONFIG'])
558 558 config = msgpack.unpackb(packed_config)
559 559 app = scm_app.create_hg_wsgi_app(
560 560 repo_path, repo_name, config)
561 561
562 562 # Consistent path information for hgweb
563 563 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
564 564 environ['REPO_NAME'] = repo_name
565 565 self.set_env_from_config(environ, config)
566 566
567 567 log.debug('http-app: starting app handler '
568 568 'with %s and process request', app)
569 569 return app(environ, ResponseFilter(start_response))
570 570 return _hg_stream
571 571
572 572 def git_stream(self):
573 573 if self._use_echo_app:
574 574 @wsgiapp
575 575 def _git_stream(environ, start_response):
576 576 app = EchoApp('fake_path', 'fake_name', None)
577 577 return app(environ, start_response)
578 578 return _git_stream
579 579 else:
580 580 @wsgiapp
581 581 def _git_stream(environ, start_response):
582 582 log.debug('http-app: handling git stream')
583 583 repo_path = environ['HTTP_X_RC_REPO_PATH']
584 584 repo_name = environ['HTTP_X_RC_REPO_NAME']
585 585 packed_config = base64.b64decode(
586 586 environ['HTTP_X_RC_REPO_CONFIG'])
587 587 config = msgpack.unpackb(packed_config)
588 588
589 589 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
590 590 self.set_env_from_config(environ, config)
591 591
592 592 content_type = environ.get('CONTENT_TYPE', '')
593 593
594 594 path = environ['PATH_INFO']
595 595 is_lfs_request = GIT_LFS_CONTENT_TYPE in content_type
596 596 log.debug(
597 597 'LFS: Detecting if request `%s` is LFS server path based '
598 598 'on content type:`%s`, is_lfs:%s',
599 599 path, content_type, is_lfs_request)
600 600
601 601 if not is_lfs_request:
602 602 # fallback detection by path
603 603 if GIT_LFS_PROTO_PAT.match(path):
604 604 is_lfs_request = True
605 605 log.debug(
606 606 'LFS: fallback detection by path of: `%s`, is_lfs:%s',
607 607 path, is_lfs_request)
608 608
609 609 if is_lfs_request:
610 610 app = scm_app.create_git_lfs_wsgi_app(
611 611 repo_path, repo_name, config)
612 612 else:
613 613 app = scm_app.create_git_wsgi_app(
614 614 repo_path, repo_name, config)
615 615
616 616 log.debug('http-app: starting app handler '
617 617 'with %s and process request', app)
618 618
619 619 return app(environ, start_response)
620 620
621 621 return _git_stream
622 622
623 623 def handle_vcs_exception(self, exception, request):
624 624 _vcs_kind = getattr(exception, '_vcs_kind', '')
625 625 if _vcs_kind == 'repo_locked':
626 626 # Get custom repo-locked status code if present.
627 627 status_code = request.headers.get('X-RC-Locked-Status-Code')
628 628 return HTTPRepoLocked(
629 629 title=exception.message, status_code=status_code)
630 630
631 631 elif _vcs_kind == 'repo_branch_protected':
632 632 # Get custom repo-branch-protected status code if present.
633 633 return HTTPRepoBranchProtected(title=exception.message)
634 634
635 635 exc_info = request.exc_info
636 636 store_exception(id(exc_info), exc_info)
637 637
638 638 traceback_info = 'unavailable'
639 639 if request.exc_info:
640 640 exc_type, exc_value, exc_tb = request.exc_info
641 641 traceback_info = ''.join(traceback.format_exception(exc_type, exc_value, exc_tb))
642 642
643 643 log.error(
644 644 'error occurred handling this request for path: %s, \n tb: %s',
645 645 request.path, traceback_info)
646 646 raise exception
647 647
648 648
649 649 class ResponseFilter(object):
650 650
651 651 def __init__(self, start_response):
652 652 self._start_response = start_response
653 653
654 654 def __call__(self, status, response_headers, exc_info=None):
655 655 headers = tuple(
656 656 (h, v) for h, v in response_headers
657 657 if not wsgiref.util.is_hop_by_hop(h))
658 658 return self._start_response(status, headers, exc_info)
659 659
660 660
661 661 def main(global_config, **settings):
662 662 if MercurialFactory:
663 663 hgpatches.patch_largefiles_capabilities()
664 664 hgpatches.patch_subrepo_type_mapping()
665 665
666 666 app = HTTPApplication(settings=settings, global_config=global_config)
667 667 return app.wsgi_app()
General Comments 0
You need to be logged in to leave comments. Login now