##// END OF EJS Templates
svn: extend detection of SVN to PROPFIND/PROPATCH methods....
marcink -
r2405:2f080035 default
parent child Browse files
Show More
@@ -1,241 +1,244
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 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 http_dav = environ.get('HTTP_DAV', '')
80 81 magic_path_segment = rhodecode.CONFIG.get(
81 82 'rhodecode_subversion_magic_path', '/!svn')
82 83 is_svn_path = (
83 84 'subversion' in http_dav or
84 magic_path_segment in environ['PATH_INFO'])
85 magic_path_segment in environ['PATH_INFO']
86 or environ['REQUEST_METHOD'] in ['PROPFIND', 'PROPPATCH']
87 )
85 88 log.debug(
86 89 'request path: `%s` detected as SVN PROTOCOL %s', environ['PATH_INFO'],
87 90 is_svn_path)
88 91
89 92 return is_svn_path
90 93
91 94
92 95 class GunzipMiddleware(object):
93 96 """
94 97 WSGI middleware that unzips gzip-encoded requests before
95 98 passing on to the underlying application.
96 99 """
97 100
98 101 def __init__(self, application):
99 102 self.app = application
100 103
101 104 def __call__(self, environ, start_response):
102 105 accepts_encoding_header = environ.get('HTTP_CONTENT_ENCODING', b'')
103 106
104 107 if b'gzip' in accepts_encoding_header:
105 108 log.debug('gzip detected, now running gunzip wrapper')
106 109 wsgi_input = environ['wsgi.input']
107 110
108 111 if not hasattr(environ['wsgi.input'], 'seek'):
109 112 # The gzip implementation in the standard library of Python 2.x
110 113 # requires the '.seek()' and '.tell()' methods to be available
111 114 # on the input stream. Read the data into a temporary file to
112 115 # work around this limitation.
113 116
114 117 wsgi_input = tempfile.SpooledTemporaryFile(64 * 1024 * 1024)
115 118 shutil.copyfileobj(environ['wsgi.input'], wsgi_input)
116 119 wsgi_input.seek(0)
117 120
118 121 environ['wsgi.input'] = gzip.GzipFile(fileobj=wsgi_input, mode='r')
119 122 # since we "Ungzipped" the content we say now it's no longer gzip
120 123 # content encoding
121 124 del environ['HTTP_CONTENT_ENCODING']
122 125
123 126 # content length has changes ? or i'm not sure
124 127 if 'CONTENT_LENGTH' in environ:
125 128 del environ['CONTENT_LENGTH']
126 129 else:
127 130 log.debug('content not gzipped, gzipMiddleware passing '
128 131 'request further')
129 132 return self.app(environ, start_response)
130 133
131 134
132 135 def is_vcs_call(environ):
133 136 if VCS_TYPE_KEY in environ:
134 137 raw_type = environ[VCS_TYPE_KEY]
135 138 return raw_type and raw_type != VCS_TYPE_SKIP
136 139 return False
137 140
138 141
139 142 def detect_vcs_request(environ, backends):
140 143 checks = {
141 144 'hg': (is_hg, SimpleHg),
142 145 'git': (is_git, SimpleGit),
143 146 'svn': (is_svn, SimpleSvn),
144 147 }
145 148 handler = None
146 149
147 150 if VCS_TYPE_KEY in environ:
148 151 raw_type = environ[VCS_TYPE_KEY]
149 152 if raw_type == VCS_TYPE_SKIP:
150 153 log.debug('got `skip` marker for vcs detection, skipping...')
151 154 return handler
152 155
153 156 _check, handler = checks.get(raw_type) or [None, None]
154 157 if handler:
155 158 log.debug('got handler:%s from environ', handler)
156 159
157 160 if not handler:
158 161 log.debug('checking if request is of VCS type in order: %s', backends)
159 162 for vcs_type in backends:
160 163 vcs_check, _handler = checks[vcs_type]
161 164 if vcs_check(environ):
162 165 log.debug('vcs handler found %s', _handler)
163 166 handler = _handler
164 167 break
165 168
166 169 return handler
167 170
168 171
169 172 class VCSMiddleware(object):
170 173
171 174 def __init__(self, app, registry, config, appenlight_client):
172 175 self.application = app
173 176 self.registry = registry
174 177 self.config = config
175 178 self.appenlight_client = appenlight_client
176 179 self.use_gzip = True
177 180 # order in which we check the middlewares, based on vcs.backends config
178 181 self.check_middlewares = config['vcs.backends']
179 182
180 183 def vcs_config(self, repo_name=None):
181 184 """
182 185 returns serialized VcsSettings
183 186 """
184 187 try:
185 188 return VcsSettingsModel(
186 189 repo=repo_name).get_ui_settings_as_config_obj()
187 190 except Exception:
188 191 pass
189 192
190 193 def wrap_in_gzip_if_enabled(self, app, config):
191 194 if self.use_gzip:
192 195 app = GunzipMiddleware(app)
193 196 return app
194 197
195 198 def _get_handler_app(self, environ):
196 199 app = None
197 200 log.debug('VCSMiddleware: detecting vcs type.')
198 201 handler = detect_vcs_request(environ, self.check_middlewares)
199 202 if handler:
200 203 app = handler(self.config, self.registry)
201 204
202 205 return app
203 206
204 207 def __call__(self, environ, start_response):
205 208 # check if we handle one of interesting protocols, optionally extract
206 209 # specific vcsSettings and allow changes of how things are wrapped
207 210 vcs_handler = self._get_handler_app(environ)
208 211 if vcs_handler:
209 212 # translate the _REPO_ID into real repo NAME for usage
210 213 # in middleware
211 214 environ['PATH_INFO'] = vcs_handler._get_by_id(environ['PATH_INFO'])
212 215
213 216 # Set acl, url and vcs repo names.
214 217 vcs_handler.set_repo_names(environ)
215 218
216 219 # register repo config back to the handler
217 220 vcs_conf = self.vcs_config(vcs_handler.acl_repo_name)
218 221 # maybe damaged/non existent settings. We still want to
219 222 # pass that point to validate on is_valid_and_existing_repo
220 223 # and return proper HTTP Code back to client
221 224 if vcs_conf:
222 225 vcs_handler.repo_vcs_config = vcs_conf
223 226
224 227 # check for type, presence in database and on filesystem
225 228 if not vcs_handler.is_valid_and_existing_repo(
226 229 vcs_handler.acl_repo_name,
227 230 vcs_handler.base_path,
228 231 vcs_handler.SCM):
229 232 return HTTPNotFound()(environ, start_response)
230 233
231 234 environ['REPO_NAME'] = vcs_handler.url_repo_name
232 235
233 236 # Wrap handler in middlewares if they are enabled.
234 237 vcs_handler = self.wrap_in_gzip_if_enabled(
235 238 vcs_handler, self.config)
236 239 vcs_handler, _ = wrap_in_appenlight_if_enabled(
237 240 vcs_handler, self.config, self.appenlight_client)
238 241
239 242 return vcs_handler(environ, start_response)
240 243
241 244 return self.application(environ, start_response)
General Comments 0
You need to be logged in to leave comments. Login now