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