##// END OF EJS Templates
vcs: Move logging from vcs to simplevcs
Martin Bornhold -
r903:ca0f37f5 default
parent child Browse files
Show More
@@ -1,211 +1,204 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
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 Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import gzip
21 import gzip
22 import re
23 import shutil
22 import shutil
24 import logging
23 import logging
25 import tempfile
24 import tempfile
26 import urlparse
25 import urlparse
27
26
28 from webob.exc import HTTPNotFound
27 from webob.exc import HTTPNotFound
29
28
30 import rhodecode
29 import rhodecode
31 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
30 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
32 from rhodecode.lib.middleware.simplegit import SimpleGit, GIT_PROTO_PAT
31 from rhodecode.lib.middleware.simplegit import SimpleGit, GIT_PROTO_PAT
33 from rhodecode.lib.middleware.simplehg import SimpleHg
32 from rhodecode.lib.middleware.simplehg import SimpleHg
34 from rhodecode.lib.middleware.simplesvn import SimpleSvn
33 from rhodecode.lib.middleware.simplesvn import SimpleSvn
35 from rhodecode.model.settings import VcsSettingsModel
34 from rhodecode.model.settings import VcsSettingsModel
36
35
37 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
38
37
39
38
40 def is_git(environ):
39 def is_git(environ):
41 """
40 """
42 Returns True if requests should be handled by GIT wsgi middleware
41 Returns True if requests should be handled by GIT wsgi middleware
43 """
42 """
44 is_git_path = GIT_PROTO_PAT.match(environ['PATH_INFO'])
43 is_git_path = GIT_PROTO_PAT.match(environ['PATH_INFO'])
45 log.debug(
44 log.debug(
46 'request path: `%s` detected as GIT PROTOCOL %s', environ['PATH_INFO'],
45 'request path: `%s` detected as GIT PROTOCOL %s', environ['PATH_INFO'],
47 is_git_path is not None)
46 is_git_path is not None)
48
47
49 return is_git_path
48 return is_git_path
50
49
51
50
52 def is_hg(environ):
51 def is_hg(environ):
53 """
52 """
54 Returns True if requests target is mercurial server - header
53 Returns True if requests target is mercurial server - header
55 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
54 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
56 """
55 """
57 is_hg_path = False
56 is_hg_path = False
58
57
59 http_accept = environ.get('HTTP_ACCEPT')
58 http_accept = environ.get('HTTP_ACCEPT')
60
59
61 if http_accept and http_accept.startswith('application/mercurial'):
60 if http_accept and http_accept.startswith('application/mercurial'):
62 query = urlparse.parse_qs(environ['QUERY_STRING'])
61 query = urlparse.parse_qs(environ['QUERY_STRING'])
63 if 'cmd' in query:
62 if 'cmd' in query:
64 is_hg_path = True
63 is_hg_path = True
65
64
66 log.debug(
65 log.debug(
67 'request path: `%s` detected as HG PROTOCOL %s', environ['PATH_INFO'],
66 'request path: `%s` detected as HG PROTOCOL %s', environ['PATH_INFO'],
68 is_hg_path)
67 is_hg_path)
69
68
70 return is_hg_path
69 return is_hg_path
71
70
72
71
73 def is_svn(environ):
72 def is_svn(environ):
74 """
73 """
75 Returns True if requests target is Subversion server
74 Returns True if requests target is Subversion server
76 """
75 """
77 http_dav = environ.get('HTTP_DAV', '')
76 http_dav = environ.get('HTTP_DAV', '')
78 magic_path_segment = rhodecode.CONFIG.get(
77 magic_path_segment = rhodecode.CONFIG.get(
79 'rhodecode_subversion_magic_path', '/!svn')
78 'rhodecode_subversion_magic_path', '/!svn')
80 is_svn_path = (
79 is_svn_path = (
81 'subversion' in http_dav or
80 'subversion' in http_dav or
82 magic_path_segment in environ['PATH_INFO'])
81 magic_path_segment in environ['PATH_INFO'])
83 log.debug(
82 log.debug(
84 'request path: `%s` detected as SVN PROTOCOL %s', environ['PATH_INFO'],
83 'request path: `%s` detected as SVN PROTOCOL %s', environ['PATH_INFO'],
85 is_svn_path)
84 is_svn_path)
86
85
87 return is_svn_path
86 return is_svn_path
88
87
89
88
90 class GunzipMiddleware(object):
89 class GunzipMiddleware(object):
91 """
90 """
92 WSGI middleware that unzips gzip-encoded requests before
91 WSGI middleware that unzips gzip-encoded requests before
93 passing on to the underlying application.
92 passing on to the underlying application.
94 """
93 """
95
94
96 def __init__(self, application):
95 def __init__(self, application):
97 self.app = application
96 self.app = application
98
97
99 def __call__(self, environ, start_response):
98 def __call__(self, environ, start_response):
100 accepts_encoding_header = environ.get('HTTP_CONTENT_ENCODING', b'')
99 accepts_encoding_header = environ.get('HTTP_CONTENT_ENCODING', b'')
101
100
102 if b'gzip' in accepts_encoding_header:
101 if b'gzip' in accepts_encoding_header:
103 log.debug('gzip detected, now running gunzip wrapper')
102 log.debug('gzip detected, now running gunzip wrapper')
104 wsgi_input = environ['wsgi.input']
103 wsgi_input = environ['wsgi.input']
105
104
106 if not hasattr(environ['wsgi.input'], 'seek'):
105 if not hasattr(environ['wsgi.input'], 'seek'):
107 # The gzip implementation in the standard library of Python 2.x
106 # The gzip implementation in the standard library of Python 2.x
108 # requires the '.seek()' and '.tell()' methods to be available
107 # requires the '.seek()' and '.tell()' methods to be available
109 # on the input stream. Read the data into a temporary file to
108 # on the input stream. Read the data into a temporary file to
110 # work around this limitation.
109 # work around this limitation.
111
110
112 wsgi_input = tempfile.SpooledTemporaryFile(64 * 1024 * 1024)
111 wsgi_input = tempfile.SpooledTemporaryFile(64 * 1024 * 1024)
113 shutil.copyfileobj(environ['wsgi.input'], wsgi_input)
112 shutil.copyfileobj(environ['wsgi.input'], wsgi_input)
114 wsgi_input.seek(0)
113 wsgi_input.seek(0)
115
114
116 environ['wsgi.input'] = gzip.GzipFile(fileobj=wsgi_input, mode='r')
115 environ['wsgi.input'] = gzip.GzipFile(fileobj=wsgi_input, mode='r')
117 # since we "Ungzipped" the content we say now it's no longer gzip
116 # since we "Ungzipped" the content we say now it's no longer gzip
118 # content encoding
117 # content encoding
119 del environ['HTTP_CONTENT_ENCODING']
118 del environ['HTTP_CONTENT_ENCODING']
120
119
121 # content length has changes ? or i'm not sure
120 # content length has changes ? or i'm not sure
122 if 'CONTENT_LENGTH' in environ:
121 if 'CONTENT_LENGTH' in environ:
123 del environ['CONTENT_LENGTH']
122 del environ['CONTENT_LENGTH']
124 else:
123 else:
125 log.debug('content not gzipped, gzipMiddleware passing '
124 log.debug('content not gzipped, gzipMiddleware passing '
126 'request further')
125 'request further')
127 return self.app(environ, start_response)
126 return self.app(environ, start_response)
128
127
129
128
130 class VCSMiddleware(object):
129 class VCSMiddleware(object):
131
130
132 def __init__(self, app, config, appenlight_client, registry):
131 def __init__(self, app, config, appenlight_client, registry):
133 self.application = app
132 self.application = app
134 self.config = config
133 self.config = config
135 self.appenlight_client = appenlight_client
134 self.appenlight_client = appenlight_client
136 self.registry = registry
135 self.registry = registry
137 self.use_gzip = True
136 self.use_gzip = True
138 # order in which we check the middlewares, based on vcs.backends config
137 # order in which we check the middlewares, based on vcs.backends config
139 self.check_middlewares = config['vcs.backends']
138 self.check_middlewares = config['vcs.backends']
140 self.checks = {
139 self.checks = {
141 'hg': (is_hg, SimpleHg),
140 'hg': (is_hg, SimpleHg),
142 'git': (is_git, SimpleGit),
141 'git': (is_git, SimpleGit),
143 'svn': (is_svn, SimpleSvn),
142 'svn': (is_svn, SimpleSvn),
144 }
143 }
145
144
146 def vcs_config(self, repo_name=None):
145 def vcs_config(self, repo_name=None):
147 """
146 """
148 returns serialized VcsSettings
147 returns serialized VcsSettings
149 """
148 """
150 return VcsSettingsModel(repo=repo_name).get_ui_settings_as_config_obj()
149 return VcsSettingsModel(repo=repo_name).get_ui_settings_as_config_obj()
151
150
152 def wrap_in_gzip_if_enabled(self, app, config):
151 def wrap_in_gzip_if_enabled(self, app, config):
153 if self.use_gzip:
152 if self.use_gzip:
154 app = GunzipMiddleware(app)
153 app = GunzipMiddleware(app)
155 return app
154 return app
156
155
157 def _get_handler_app(self, environ):
156 def _get_handler_app(self, environ):
158 app = None
157 app = None
159 log.debug('Checking vcs types in order: %r', self.check_middlewares)
158 log.debug('Checking vcs types in order: %r', self.check_middlewares)
160 for vcs_type in self.check_middlewares:
159 for vcs_type in self.check_middlewares:
161 vcs_check, handler = self.checks[vcs_type]
160 vcs_check, handler = self.checks[vcs_type]
162 if vcs_check(environ):
161 if vcs_check(environ):
163 log.debug(
162 log.debug(
164 'Found VCS Middleware to handle the request %s', handler)
163 'Found VCS Middleware to handle the request %s', handler)
165 app = handler(self.application, self.config, self.registry)
164 app = handler(self.application, self.config, self.registry)
166 break
165 break
167
166
168 return app
167 return app
169
168
170 def __call__(self, environ, start_response):
169 def __call__(self, environ, start_response):
171 # check if we handle one of interesting protocols, optionally extract
170 # check if we handle one of interesting protocols, optionally extract
172 # specific vcsSettings and allow changes of how things are wrapped
171 # specific vcsSettings and allow changes of how things are wrapped
173 vcs_handler = self._get_handler_app(environ)
172 vcs_handler = self._get_handler_app(environ)
174 if vcs_handler:
173 if vcs_handler:
175 # translate the _REPO_ID into real repo NAME for usage
174 # translate the _REPO_ID into real repo NAME for usage
176 # in middleware
175 # in middleware
177 environ['PATH_INFO'] = vcs_handler._get_by_id(environ['PATH_INFO'])
176 environ['PATH_INFO'] = vcs_handler._get_by_id(environ['PATH_INFO'])
178
177
179 # Set repo names for permission checks, vcs and web interaction.
178 # Set repo names for permission checks, vcs and web interaction.
180 vcs_handler.set_repo_names(environ)
179 vcs_handler.set_repo_names(environ)
181 log.debug('repo_names %s', {
182 'acl_repo_name': vcs_handler.acl_repo_name,
183 'vcs_repo_name': vcs_handler.vcs_repo_name,
184 'url_repo_name': vcs_handler.url_repo_name,
185 })
186 log.debug('pull_request %s', vcs_handler.pr_id)
187
180
188 # check for type, presence in database and on filesystem
181 # check for type, presence in database and on filesystem
189 if not vcs_handler.is_valid_and_existing_repo(
182 if not vcs_handler.is_valid_and_existing_repo(
190 vcs_handler.acl_repo_name,
183 vcs_handler.acl_repo_name,
191 vcs_handler.basepath,
184 vcs_handler.basepath,
192 vcs_handler.SCM):
185 vcs_handler.SCM):
193 return HTTPNotFound()(environ, start_response)
186 return HTTPNotFound()(environ, start_response)
194
187
195 # TODO: johbo: Needed for the Pyro4 backend and Mercurial only.
188 # TODO: johbo: Needed for the Pyro4 backend and Mercurial only.
196 # Remove once we fully switched to the HTTP backend.
189 # Remove once we fully switched to the HTTP backend.
197 environ['REPO_NAME'] = vcs_handler.url_repo_name
190 environ['REPO_NAME'] = vcs_handler.url_repo_name
198
191
199 # register repo config back to the handler
192 # register repo config back to the handler
200 vcs_handler.repo_vcs_config = self.vcs_config(
193 vcs_handler.repo_vcs_config = self.vcs_config(
201 vcs_handler.acl_repo_name)
194 vcs_handler.acl_repo_name)
202
195
203 # Wrap handler in middlewares if they are enabled.
196 # Wrap handler in middlewares if they are enabled.
204 vcs_handler = self.wrap_in_gzip_if_enabled(
197 vcs_handler = self.wrap_in_gzip_if_enabled(
205 vcs_handler, self.config)
198 vcs_handler, self.config)
206 vcs_handler, _ = wrap_in_appenlight_if_enabled(
199 vcs_handler, _ = wrap_in_appenlight_if_enabled(
207 vcs_handler, self.config, self.appenlight_client)
200 vcs_handler, self.config, self.appenlight_client)
208
201
209 return vcs_handler(environ, start_response)
202 return vcs_handler(environ, start_response)
210
203
211 return self.application(environ, start_response)
204 return self.application(environ, start_response)
General Comments 0
You need to be logged in to leave comments. Login now