##// END OF EJS Templates
artifacts: skip vcs detection for artifact downloads.
marcink -
r4475:e9be5c19 default
parent child Browse files
Show More
@@ -1,244 +1,267 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 def get_path_elem(route_path):
143 if not route_path:
144 return None
145
146 cleaned_route_path = route_path.lstrip('/')
147 if cleaned_route_path:
148 cleaned_route_path_elems = cleaned_route_path.split('/')
149 if cleaned_route_path_elems:
150 return cleaned_route_path_elems[0]
151 return None
152
153
142 154 def detect_vcs_request(environ, backends):
143 155 checks = {
144 156 'hg': (is_hg, SimpleHg),
145 157 'git': (is_git, SimpleGit),
146 158 'svn': (is_svn, SimpleSvn),
147 159 }
148 160 handler = None
161 # List of path views first chunk we don't do any checks
162 white_list = [
163 # e.g /_file_store/download
164 '_file_store'
165 ]
166
167 path_info = environ['PATH_INFO']
168
169 if get_path_elem(path_info) in white_list:
170 log.debug('path `%s` in whitelist, skipping...', path_info)
171 return handler
149 172
150 173 if VCS_TYPE_KEY in environ:
151 174 raw_type = environ[VCS_TYPE_KEY]
152 175 if raw_type == VCS_TYPE_SKIP:
153 176 log.debug('got `skip` marker for vcs detection, skipping...')
154 177 return handler
155 178
156 179 _check, handler = checks.get(raw_type) or [None, None]
157 180 if handler:
158 181 log.debug('got handler:%s from environ', handler)
159 182
160 183 if not handler:
161 184 log.debug('request start: checking if request is of VCS type in order: %s', backends)
162 185 for vcs_type in backends:
163 186 vcs_check, _handler = checks[vcs_type]
164 187 if vcs_check(environ):
165 188 log.debug('vcs handler found %s', _handler)
166 189 handler = _handler
167 190 break
168 191
169 192 return handler
170 193
171 194
172 195 class VCSMiddleware(object):
173 196
174 197 def __init__(self, app, registry, config, appenlight_client):
175 198 self.application = app
176 199 self.registry = registry
177 200 self.config = config
178 201 self.appenlight_client = appenlight_client
179 202 self.use_gzip = True
180 203 # order in which we check the middlewares, based on vcs.backends config
181 204 self.check_middlewares = config['vcs.backends']
182 205
183 206 def vcs_config(self, repo_name=None):
184 207 """
185 208 returns serialized VcsSettings
186 209 """
187 210 try:
188 211 return VcsSettingsModel(
189 212 repo=repo_name).get_ui_settings_as_config_obj()
190 213 except Exception:
191 214 pass
192 215
193 216 def wrap_in_gzip_if_enabled(self, app, config):
194 217 if self.use_gzip:
195 218 app = GunzipMiddleware(app)
196 219 return app
197 220
198 221 def _get_handler_app(self, environ):
199 222 app = None
200 223 log.debug('VCSMiddleware: detecting vcs type.')
201 224 handler = detect_vcs_request(environ, self.check_middlewares)
202 225 if handler:
203 226 app = handler(self.config, self.registry)
204 227
205 228 return app
206 229
207 230 def __call__(self, environ, start_response):
208 231 # check if we handle one of interesting protocols, optionally extract
209 232 # specific vcsSettings and allow changes of how things are wrapped
210 233 vcs_handler = self._get_handler_app(environ)
211 234 if vcs_handler:
212 235 # translate the _REPO_ID into real repo NAME for usage
213 236 # in middleware
214 237 environ['PATH_INFO'] = vcs_handler._get_by_id(environ['PATH_INFO'])
215 238
216 239 # Set acl, url and vcs repo names.
217 240 vcs_handler.set_repo_names(environ)
218 241
219 242 # register repo config back to the handler
220 243 vcs_conf = self.vcs_config(vcs_handler.acl_repo_name)
221 244 # maybe damaged/non existent settings. We still want to
222 245 # pass that point to validate on is_valid_and_existing_repo
223 246 # and return proper HTTP Code back to client
224 247 if vcs_conf:
225 248 vcs_handler.repo_vcs_config = vcs_conf
226 249
227 250 # check for type, presence in database and on filesystem
228 251 if not vcs_handler.is_valid_and_existing_repo(
229 252 vcs_handler.acl_repo_name,
230 253 vcs_handler.base_path,
231 254 vcs_handler.SCM):
232 255 return HTTPNotFound()(environ, start_response)
233 256
234 257 environ['REPO_NAME'] = vcs_handler.url_repo_name
235 258
236 259 # Wrap handler in middlewares if they are enabled.
237 260 vcs_handler = self.wrap_in_gzip_if_enabled(
238 261 vcs_handler, self.config)
239 262 vcs_handler, _ = wrap_in_appenlight_if_enabled(
240 263 vcs_handler, self.config, self.appenlight_client)
241 264
242 265 return vcs_handler(environ, start_response)
243 266
244 267 return self.application(environ, start_response)
General Comments 0
You need to be logged in to leave comments. Login now