##// END OF EJS Templates
vcs middleware: Fix up TODO note...
johbo -
r785:52ca3d84 default
parent child Browse files
Show More
@@ -1,197 +1,198 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 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
39 39 def is_git(environ):
40 40 """
41 41 Returns True if requests should be handled by GIT wsgi middleware
42 42 """
43 43 is_git_path = GIT_PROTO_PAT.match(environ['PATH_INFO'])
44 44 log.debug(
45 45 'request path: `%s` detected as GIT PROTOCOL %s', environ['PATH_INFO'],
46 46 is_git_path is not None)
47 47
48 48 return is_git_path
49 49
50 50
51 51 def is_hg(environ):
52 52 """
53 53 Returns True if requests target is mercurial server - header
54 54 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
55 55 """
56 56 is_hg_path = False
57 57
58 58 http_accept = environ.get('HTTP_ACCEPT')
59 59
60 60 if http_accept and http_accept.startswith('application/mercurial'):
61 61 query = urlparse.parse_qs(environ['QUERY_STRING'])
62 62 if 'cmd' in query:
63 63 is_hg_path = True
64 64
65 65 log.debug(
66 66 'request path: `%s` detected as HG PROTOCOL %s', environ['PATH_INFO'],
67 67 is_hg_path)
68 68
69 69 return is_hg_path
70 70
71 71
72 72 def is_svn(environ):
73 73 """
74 74 Returns True if requests target is Subversion server
75 75 """
76 76 http_dav = environ.get('HTTP_DAV', '')
77 77 magic_path_segment = rhodecode.CONFIG.get(
78 78 'rhodecode_subversion_magic_path', '/!svn')
79 79 is_svn_path = (
80 80 'subversion' in http_dav or
81 81 magic_path_segment in environ['PATH_INFO'])
82 82 log.debug(
83 83 'request path: `%s` detected as SVN PROTOCOL %s', environ['PATH_INFO'],
84 84 is_svn_path)
85 85
86 86 return is_svn_path
87 87
88 88
89 89 class GunzipMiddleware(object):
90 90 """
91 91 WSGI middleware that unzips gzip-encoded requests before
92 92 passing on to the underlying application.
93 93 """
94 94
95 95 def __init__(self, application):
96 96 self.app = application
97 97
98 98 def __call__(self, environ, start_response):
99 99 accepts_encoding_header = environ.get('HTTP_CONTENT_ENCODING', b'')
100 100
101 101 if b'gzip' in accepts_encoding_header:
102 102 log.debug('gzip detected, now running gunzip wrapper')
103 103 wsgi_input = environ['wsgi.input']
104 104
105 105 if not hasattr(environ['wsgi.input'], 'seek'):
106 106 # The gzip implementation in the standard library of Python 2.x
107 107 # requires the '.seek()' and '.tell()' methods to be available
108 108 # on the input stream. Read the data into a temporary file to
109 109 # work around this limitation.
110 110
111 111 wsgi_input = tempfile.SpooledTemporaryFile(64 * 1024 * 1024)
112 112 shutil.copyfileobj(environ['wsgi.input'], wsgi_input)
113 113 wsgi_input.seek(0)
114 114
115 115 environ['wsgi.input'] = gzip.GzipFile(fileobj=wsgi_input, mode='r')
116 116 # since we "Ungzipped" the content we say now it's no longer gzip
117 117 # content encoding
118 118 del environ['HTTP_CONTENT_ENCODING']
119 119
120 120 # content length has changes ? or i'm not sure
121 121 if 'CONTENT_LENGTH' in environ:
122 122 del environ['CONTENT_LENGTH']
123 123 else:
124 124 log.debug('content not gzipped, gzipMiddleware passing '
125 125 'request further')
126 126 return self.app(environ, start_response)
127 127
128 128
129 129 class VCSMiddleware(object):
130 130
131 131 def __init__(self, app, config, appenlight_client, registry):
132 132 self.application = app
133 133 self.config = config
134 134 self.appenlight_client = appenlight_client
135 135 self.registry = registry
136 136 self.use_gzip = True
137 137 # order in which we check the middlewares, based on vcs.backends config
138 138 self.check_middlewares = config['vcs.backends']
139 139 self.checks = {
140 140 'hg': (is_hg, SimpleHg),
141 141 'git': (is_git, SimpleGit),
142 142 'svn': (is_svn, SimpleSvn),
143 143 }
144 144
145 145 def vcs_config(self, repo_name=None):
146 146 """
147 147 returns serialized VcsSettings
148 148 """
149 149 return VcsSettingsModel(repo=repo_name).get_ui_settings_as_config_obj()
150 150
151 151 def wrap_in_gzip_if_enabled(self, app, config):
152 152 if self.use_gzip:
153 153 app = GunzipMiddleware(app)
154 154 return app
155 155
156 156 def _get_handler_app(self, environ):
157 157 app = None
158 158 log.debug('Checking vcs types in order: %r', self.check_middlewares)
159 159 for vcs_type in self.check_middlewares:
160 160 vcs_check, handler = self.checks[vcs_type]
161 161 if vcs_check(environ):
162 162 log.debug(
163 163 'Found VCS Middleware to handle the request %s', handler)
164 164 app = handler(self.application, self.config, self.registry)
165 165 break
166 166
167 167 return app
168 168
169 169 def __call__(self, environ, start_response):
170 170 # check if we handle one of interesting protocols, optionally extract
171 171 # specific vcsSettings and allow changes of how things are wrapped
172 172 vcs_handler = self._get_handler_app(environ)
173 173 if vcs_handler:
174 174 # translate the _REPO_ID into real repo NAME for usage
175 175 # in middleware
176 176 environ['PATH_INFO'] = vcs_handler._get_by_id(environ['PATH_INFO'])
177 177 repo_name = vcs_handler._get_repository_name(environ)
178 178
179 179 # check for type, presence in database and on filesystem
180 180 if not vcs_handler.is_valid_and_existing_repo(
181 181 repo_name, vcs_handler.basepath, vcs_handler.SCM):
182 182 return HTTPNotFound()(environ, start_response)
183 183
184 # TODO(marcink): this is probably not needed anymore
184 # TODO: johbo: Needed for the Pyro4 backend and Mercurial only.
185 # Remove once we fully switched to the HTTP backend.
185 186 environ['REPO_NAME'] = repo_name
186 187
187 188 # register repo_name and it's config back to the handler
188 189 vcs_handler.repo_name = repo_name
189 190 vcs_handler.repo_vcs_config = self.vcs_config(repo_name)
190 191
191 192 vcs_handler = self.wrap_in_gzip_if_enabled(
192 193 vcs_handler, self.config)
193 194 vcs_handler, _ = wrap_in_appenlight_if_enabled(
194 195 vcs_handler, self.config, self.appenlight_client)
195 196 return vcs_handler(environ, start_response)
196 197
197 198 return self.application(environ, start_response)
General Comments 0
You need to be logged in to leave comments. Login now