##// END OF EJS Templates
core: optimize some calls to skip license/scm detection on them
milka -
r4655:009d312e default
parent child Browse files
Show More
@@ -1,267 +1,280 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
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 Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import gzip
22 22 import shutil
23 23 import logging
24 24 import tempfile
25 25 import urlparse
26 26
27 27 from webob.exc import HTTPNotFound
28 28
29 29 import rhodecode
30 30 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
31 31 from rhodecode.lib.middleware.simplegit import SimpleGit, GIT_PROTO_PAT
32 32 from rhodecode.lib.middleware.simplehg import SimpleHg
33 33 from rhodecode.lib.middleware.simplesvn import SimpleSvn
34 34 from rhodecode.model.settings import VcsSettingsModel
35 35
36 36 log = logging.getLogger(__name__)
37 37
38 38 VCS_TYPE_KEY = '_rc_vcs_type'
39 39 VCS_TYPE_SKIP = '_rc_vcs_skip'
40 40
41 41
42 42 def is_git(environ):
43 43 """
44 44 Returns True if requests should be handled by GIT wsgi middleware
45 45 """
46 46 is_git_path = GIT_PROTO_PAT.match(environ['PATH_INFO'])
47 47 log.debug(
48 48 'request path: `%s` detected as GIT PROTOCOL %s', environ['PATH_INFO'],
49 49 is_git_path is not None)
50 50
51 51 return is_git_path
52 52
53 53
54 54 def is_hg(environ):
55 55 """
56 56 Returns True if requests target is mercurial server - header
57 57 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
58 58 """
59 59 is_hg_path = False
60 60
61 61 http_accept = environ.get('HTTP_ACCEPT')
62 62
63 63 if http_accept and http_accept.startswith('application/mercurial'):
64 64 query = urlparse.parse_qs(environ['QUERY_STRING'])
65 65 if 'cmd' in query:
66 66 is_hg_path = True
67 67
68 68 log.debug(
69 69 'request path: `%s` detected as HG PROTOCOL %s', environ['PATH_INFO'],
70 70 is_hg_path)
71 71
72 72 return is_hg_path
73 73
74 74
75 75 def is_svn(environ):
76 76 """
77 77 Returns True if requests target is Subversion server
78 78 """
79 79
80 80 http_dav = environ.get('HTTP_DAV', '')
81 81 magic_path_segment = rhodecode.CONFIG.get(
82 82 'rhodecode_subversion_magic_path', '/!svn')
83 83 is_svn_path = (
84 84 'subversion' in http_dav or
85 85 magic_path_segment in environ['PATH_INFO']
86 86 or environ['REQUEST_METHOD'] in ['PROPFIND', 'PROPPATCH']
87 87 )
88 88 log.debug(
89 89 'request path: `%s` detected as SVN PROTOCOL %s', environ['PATH_INFO'],
90 90 is_svn_path)
91 91
92 92 return is_svn_path
93 93
94 94
95 95 class GunzipMiddleware(object):
96 96 """
97 97 WSGI middleware that unzips gzip-encoded requests before
98 98 passing on to the underlying application.
99 99 """
100 100
101 101 def __init__(self, application):
102 102 self.app = application
103 103
104 104 def __call__(self, environ, start_response):
105 105 accepts_encoding_header = environ.get('HTTP_CONTENT_ENCODING', b'')
106 106
107 107 if b'gzip' in accepts_encoding_header:
108 108 log.debug('gzip detected, now running gunzip wrapper')
109 109 wsgi_input = environ['wsgi.input']
110 110
111 111 if not hasattr(environ['wsgi.input'], 'seek'):
112 112 # The gzip implementation in the standard library of Python 2.x
113 113 # requires the '.seek()' and '.tell()' methods to be available
114 114 # on the input stream. Read the data into a temporary file to
115 115 # work around this limitation.
116 116
117 117 wsgi_input = tempfile.SpooledTemporaryFile(64 * 1024 * 1024)
118 118 shutil.copyfileobj(environ['wsgi.input'], wsgi_input)
119 119 wsgi_input.seek(0)
120 120
121 121 environ['wsgi.input'] = gzip.GzipFile(fileobj=wsgi_input, mode='r')
122 122 # since we "Ungzipped" the content we say now it's no longer gzip
123 123 # content encoding
124 124 del environ['HTTP_CONTENT_ENCODING']
125 125
126 126 # content length has changes ? or i'm not sure
127 127 if 'CONTENT_LENGTH' in environ:
128 128 del environ['CONTENT_LENGTH']
129 129 else:
130 130 log.debug('content not gzipped, gzipMiddleware passing '
131 131 'request further')
132 132 return self.app(environ, start_response)
133 133
134 134
135 135 def is_vcs_call(environ):
136 136 if VCS_TYPE_KEY in environ:
137 137 raw_type = environ[VCS_TYPE_KEY]
138 138 return raw_type and raw_type != VCS_TYPE_SKIP
139 139 return False
140 140
141 141
142 142 def get_path_elem(route_path):
143 143 if not route_path:
144 144 return None
145 145
146 146 cleaned_route_path = route_path.lstrip('/')
147 147 if cleaned_route_path:
148 148 cleaned_route_path_elems = cleaned_route_path.split('/')
149 149 if cleaned_route_path_elems:
150 150 return cleaned_route_path_elems[0]
151 151 return None
152 152
153 153
154 154 def detect_vcs_request(environ, backends):
155 155 checks = {
156 156 'hg': (is_hg, SimpleHg),
157 157 'git': (is_git, SimpleGit),
158 158 'svn': (is_svn, SimpleSvn),
159 159 }
160 160 handler = None
161 161 # List of path views first chunk we don't do any checks
162 162 white_list = [
163 163 # e.g /_file_store/download
164 '_file_store'
164 '_file_store',
165
166 # static files no detection
167 '_static',
168
169 # full channelstream connect should be VCS skipped
170 '_admin/channelstream/connect',
165 171 ]
166 172
167 173 path_info = environ['PATH_INFO']
168 174
169 if get_path_elem(path_info) in white_list:
175 path_elem = get_path_elem(path_info)
176
177 if path_elem in white_list:
170 178 log.debug('path `%s` in whitelist, skipping...', path_info)
171 179 return handler
172 180
181 path_url = path_info.lstrip('/')
182 if path_url in white_list:
183 log.debug('full url path `%s` in whitelist, skipping...', path_url)
184 return handler
185
173 186 if VCS_TYPE_KEY in environ:
174 187 raw_type = environ[VCS_TYPE_KEY]
175 188 if raw_type == VCS_TYPE_SKIP:
176 189 log.debug('got `skip` marker for vcs detection, skipping...')
177 190 return handler
178 191
179 192 _check, handler = checks.get(raw_type) or [None, None]
180 193 if handler:
181 194 log.debug('got handler:%s from environ', handler)
182 195
183 196 if not handler:
184 log.debug('request start: checking if request is of VCS type in order: %s', backends)
197 log.debug('request start: checking if request for `%s` is of VCS type in order: %s', path_elem, backends)
185 198 for vcs_type in backends:
186 199 vcs_check, _handler = checks[vcs_type]
187 200 if vcs_check(environ):
188 201 log.debug('vcs handler found %s', _handler)
189 202 handler = _handler
190 203 break
191 204
192 205 return handler
193 206
194 207
195 208 class VCSMiddleware(object):
196 209
197 210 def __init__(self, app, registry, config, appenlight_client):
198 211 self.application = app
199 212 self.registry = registry
200 213 self.config = config
201 214 self.appenlight_client = appenlight_client
202 215 self.use_gzip = True
203 216 # order in which we check the middlewares, based on vcs.backends config
204 217 self.check_middlewares = config['vcs.backends']
205 218
206 219 def vcs_config(self, repo_name=None):
207 220 """
208 221 returns serialized VcsSettings
209 222 """
210 223 try:
211 224 return VcsSettingsModel(
212 225 repo=repo_name).get_ui_settings_as_config_obj()
213 226 except Exception:
214 227 pass
215 228
216 229 def wrap_in_gzip_if_enabled(self, app, config):
217 230 if self.use_gzip:
218 231 app = GunzipMiddleware(app)
219 232 return app
220 233
221 234 def _get_handler_app(self, environ):
222 235 app = None
223 236 log.debug('VCSMiddleware: detecting vcs type.')
224 237 handler = detect_vcs_request(environ, self.check_middlewares)
225 238 if handler:
226 239 app = handler(self.config, self.registry)
227 240
228 241 return app
229 242
230 243 def __call__(self, environ, start_response):
231 244 # check if we handle one of interesting protocols, optionally extract
232 245 # specific vcsSettings and allow changes of how things are wrapped
233 246 vcs_handler = self._get_handler_app(environ)
234 247 if vcs_handler:
235 248 # translate the _REPO_ID into real repo NAME for usage
236 249 # in middleware
237 250 environ['PATH_INFO'] = vcs_handler._get_by_id(environ['PATH_INFO'])
238 251
239 252 # Set acl, url and vcs repo names.
240 253 vcs_handler.set_repo_names(environ)
241 254
242 255 # register repo config back to the handler
243 256 vcs_conf = self.vcs_config(vcs_handler.acl_repo_name)
244 257 # maybe damaged/non existent settings. We still want to
245 258 # pass that point to validate on is_valid_and_existing_repo
246 259 # and return proper HTTP Code back to client
247 260 if vcs_conf:
248 261 vcs_handler.repo_vcs_config = vcs_conf
249 262
250 263 # check for type, presence in database and on filesystem
251 264 if not vcs_handler.is_valid_and_existing_repo(
252 265 vcs_handler.acl_repo_name,
253 266 vcs_handler.base_path,
254 267 vcs_handler.SCM):
255 268 return HTTPNotFound()(environ, start_response)
256 269
257 270 environ['REPO_NAME'] = vcs_handler.url_repo_name
258 271
259 272 # Wrap handler in middlewares if they are enabled.
260 273 vcs_handler = self.wrap_in_gzip_if_enabled(
261 274 vcs_handler, self.config)
262 275 vcs_handler, _ = wrap_in_appenlight_if_enabled(
263 276 vcs_handler, self.config, self.appenlight_client)
264 277
265 278 return vcs_handler(environ, start_response)
266 279
267 280 return self.application(environ, start_response)
General Comments 0
You need to be logged in to leave comments. Login now