##// END OF EJS Templates
vcs: added logging into VCS middlewares
marcink -
r753:bb7a4b59 default
parent child Browse files
Show More
@@ -1,81 +1,84 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 """
21 """
22 SimpleGit middleware for handling git protocol request (push/clone etc.)
22 SimpleGit middleware for handling git protocol request (push/clone etc.)
23 It's implemented with basic auth function
23 It's implemented with basic auth function
24 """
24 """
25 import re
25 import re
26 import logging
26 import urlparse
27 import urlparse
27
28
28 import rhodecode
29 import rhodecode
29 from rhodecode.lib import utils2
30 from rhodecode.lib import utils2
30 from rhodecode.lib.middleware import simplevcs
31 from rhodecode.lib.middleware import simplevcs
31
32
33 log = logging.getLogger(__name__)
34
32
35
33 GIT_PROTO_PAT = re.compile(
36 GIT_PROTO_PAT = re.compile(
34 r'^/(.+)/(info/refs|git-upload-pack|git-receive-pack)')
37 r'^/(.+)/(info/refs|git-upload-pack|git-receive-pack)')
35
38
36
39
37 class SimpleGit(simplevcs.SimpleVCS):
40 class SimpleGit(simplevcs.SimpleVCS):
38
41
39 SCM = 'git'
42 SCM = 'git'
40
43
41 def _get_repository_name(self, environ):
44 def _get_repository_name(self, environ):
42 """
45 """
43 Gets repository name out of PATH_INFO header
46 Gets repository name out of PATH_INFO header
44
47
45 :param environ: environ where PATH_INFO is stored
48 :param environ: environ where PATH_INFO is stored
46 """
49 """
47 repo_name = GIT_PROTO_PAT.match(environ['PATH_INFO']).group(1)
50 repo_name = GIT_PROTO_PAT.match(environ['PATH_INFO']).group(1)
48 return repo_name
51 return repo_name
49
52
50 _ACTION_MAPPING = {
53 _ACTION_MAPPING = {
51 'git-receive-pack': 'push',
54 'git-receive-pack': 'push',
52 'git-upload-pack': 'pull',
55 'git-upload-pack': 'pull',
53 }
56 }
54
57
55 def _get_action(self, environ):
58 def _get_action(self, environ):
56 """
59 """
57 Maps git request commands into a pull or push command.
60 Maps git request commands into a pull or push command.
58 In case of unknown/unexpected data, it returns 'pull' to be safe.
61 In case of unknown/unexpected data, it returns 'pull' to be safe.
59
62
60 :param environ:
63 :param environ:
61 """
64 """
62 path = environ['PATH_INFO']
65 path = environ['PATH_INFO']
63
66
64 if path.endswith('/info/refs'):
67 if path.endswith('/info/refs'):
65 query = urlparse.parse_qs(environ['QUERY_STRING'])
68 query = urlparse.parse_qs(environ['QUERY_STRING'])
66 service_cmd = query.get('service', [''])[0]
69 service_cmd = query.get('service', [''])[0]
67 return self._ACTION_MAPPING.get(service_cmd, 'pull')
70 return self._ACTION_MAPPING.get(service_cmd, 'pull')
68 elif path.endswith('/git-receive-pack'):
71 elif path.endswith('/git-receive-pack'):
69 return 'push'
72 return 'push'
70 elif path.endswith('/git-upload-pack'):
73 elif path.endswith('/git-upload-pack'):
71 return 'pull'
74 return 'pull'
72
75
73 return 'pull'
76 return 'pull'
74
77
75 def _create_wsgi_app(self, repo_path, repo_name, config):
78 def _create_wsgi_app(self, repo_path, repo_name, config):
76 return self.scm_app.create_git_wsgi_app(repo_path, repo_name, config)
79 return self.scm_app.create_git_wsgi_app(repo_path, repo_name, config)
77
80
78 def _create_config(self, extras, repo_name):
81 def _create_config(self, extras, repo_name):
79 extras['git_update_server_info'] = utils2.str2bool(
82 extras['git_update_server_info'] = utils2.str2bool(
80 rhodecode.CONFIG.get('git_update_server_info'))
83 rhodecode.CONFIG.get('git_update_server_info'))
81 return extras
84 return extras
@@ -1,77 +1,80 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 """
21 """
22 SimpleHG middleware for handling mercurial protocol request
22 SimpleHG middleware for handling mercurial protocol request
23 (push/clone etc.). It's implemented with basic auth function
23 (push/clone etc.). It's implemented with basic auth function
24 """
24 """
25
25
26 import logging
26 import urlparse
27 import urlparse
27
28
28 from rhodecode.lib import utils
29 from rhodecode.lib import utils
29 from rhodecode.lib.ext_json import json
30 from rhodecode.lib.ext_json import json
30 from rhodecode.lib.middleware import simplevcs
31 from rhodecode.lib.middleware import simplevcs
31
32
33 log = logging.getLogger(__name__)
34
32
35
33 class SimpleHg(simplevcs.SimpleVCS):
36 class SimpleHg(simplevcs.SimpleVCS):
34
37
35 SCM = 'hg'
38 SCM = 'hg'
36
39
37 def _get_repository_name(self, environ):
40 def _get_repository_name(self, environ):
38 """
41 """
39 Gets repository name out of PATH_INFO header
42 Gets repository name out of PATH_INFO header
40
43
41 :param environ: environ where PATH_INFO is stored
44 :param environ: environ where PATH_INFO is stored
42 """
45 """
43 return environ['PATH_INFO'].strip('/')
46 return environ['PATH_INFO'].strip('/')
44
47
45 _ACTION_MAPPING = {
48 _ACTION_MAPPING = {
46 'changegroup': 'pull',
49 'changegroup': 'pull',
47 'changegroupsubset': 'pull',
50 'changegroupsubset': 'pull',
48 'getbundle': 'pull',
51 'getbundle': 'pull',
49 'stream_out': 'pull',
52 'stream_out': 'pull',
50 'listkeys': 'pull',
53 'listkeys': 'pull',
51 'unbundle': 'push',
54 'unbundle': 'push',
52 'pushkey': 'push',
55 'pushkey': 'push',
53 }
56 }
54
57
55 def _get_action(self, environ):
58 def _get_action(self, environ):
56 """
59 """
57 Maps mercurial request commands into a pull or push command.
60 Maps mercurial request commands into a pull or push command.
58 In case of unknown/unexpected data, it returns 'pull' to be safe.
61 In case of unknown/unexpected data, it returns 'pull' to be safe.
59
62
60 :param environ:
63 :param environ:
61 """
64 """
62 query = urlparse.parse_qs(environ['QUERY_STRING'],
65 query = urlparse.parse_qs(environ['QUERY_STRING'],
63 keep_blank_values=True)
66 keep_blank_values=True)
64 if 'cmd' in query:
67 if 'cmd' in query:
65 cmd = query['cmd'][0]
68 cmd = query['cmd'][0]
66 return self._ACTION_MAPPING.get(cmd, 'pull')
69 return self._ACTION_MAPPING.get(cmd, 'pull')
67
70
68 return 'pull'
71 return 'pull'
69
72
70 def _create_wsgi_app(self, repo_path, repo_name, config):
73 def _create_wsgi_app(self, repo_path, repo_name, config):
71 return self.scm_app.create_hg_wsgi_app(repo_path, repo_name, config)
74 return self.scm_app.create_hg_wsgi_app(repo_path, repo_name, config)
72
75
73 def _create_config(self, extras, repo_name):
76 def _create_config(self, extras, repo_name):
74 config = utils.make_db_config(repo=repo_name)
77 config = utils.make_db_config(repo=repo_name)
75 config.set('rhodecode', 'RC_SCM_DATA', json.dumps(extras))
78 config.set('rhodecode', 'RC_SCM_DATA', json.dumps(extras))
76
79
77 return config.serialize()
80 return config.serialize()
@@ -1,136 +1,139 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 logging
21 from urlparse import urljoin
22 from urlparse import urljoin
22
23
23 import requests
24 import requests
24
25
25 import rhodecode
26 import rhodecode
26 from rhodecode.lib.middleware import simplevcs
27 from rhodecode.lib.middleware import simplevcs
27 from rhodecode.lib.utils import is_valid_repo
28 from rhodecode.lib.utils import is_valid_repo
28
29
30 log = logging.getLogger(__name__)
31
29
32
30 class SimpleSvnApp(object):
33 class SimpleSvnApp(object):
31 IGNORED_HEADERS = [
34 IGNORED_HEADERS = [
32 'connection', 'keep-alive', 'content-encoding',
35 'connection', 'keep-alive', 'content-encoding',
33 'transfer-encoding', 'content-length']
36 'transfer-encoding', 'content-length']
34
37
35 def __init__(self, config):
38 def __init__(self, config):
36 self.config = config
39 self.config = config
37
40
38 def __call__(self, environ, start_response):
41 def __call__(self, environ, start_response):
39 request_headers = self._get_request_headers(environ)
42 request_headers = self._get_request_headers(environ)
40
43
41 data = environ['wsgi.input']
44 data = environ['wsgi.input']
42 # johbo: Avoid that we end up with sending the request in chunked
45 # johbo: Avoid that we end up with sending the request in chunked
43 # transfer encoding (mainly on Gunicorn). If we know the content
46 # transfer encoding (mainly on Gunicorn). If we know the content
44 # length, then we should transfer the payload in one request.
47 # length, then we should transfer the payload in one request.
45 if environ['REQUEST_METHOD'] == 'MKCOL' or 'CONTENT_LENGTH' in environ:
48 if environ['REQUEST_METHOD'] == 'MKCOL' or 'CONTENT_LENGTH' in environ:
46 data = data.read()
49 data = data.read()
47
50
48 response = requests.request(
51 response = requests.request(
49 environ['REQUEST_METHOD'], self._get_url(environ['PATH_INFO']),
52 environ['REQUEST_METHOD'], self._get_url(environ['PATH_INFO']),
50 data=data, headers=request_headers)
53 data=data, headers=request_headers)
51
54
52 response_headers = self._get_response_headers(response.headers)
55 response_headers = self._get_response_headers(response.headers)
53 start_response(
56 start_response(
54 '{} {}'.format(response.status_code, response.reason),
57 '{} {}'.format(response.status_code, response.reason),
55 response_headers)
58 response_headers)
56 return response.iter_content(chunk_size=1024)
59 return response.iter_content(chunk_size=1024)
57
60
58 def _get_url(self, path):
61 def _get_url(self, path):
59 return urljoin(
62 return urljoin(
60 self.config.get('subversion_http_server_url', ''), path)
63 self.config.get('subversion_http_server_url', ''), path)
61
64
62 def _get_request_headers(self, environ):
65 def _get_request_headers(self, environ):
63 headers = {}
66 headers = {}
64
67
65 for key in environ:
68 for key in environ:
66 if not key.startswith('HTTP_'):
69 if not key.startswith('HTTP_'):
67 continue
70 continue
68 new_key = key.split('_')
71 new_key = key.split('_')
69 new_key = [k.capitalize() for k in new_key[1:]]
72 new_key = [k.capitalize() for k in new_key[1:]]
70 new_key = '-'.join(new_key)
73 new_key = '-'.join(new_key)
71 headers[new_key] = environ[key]
74 headers[new_key] = environ[key]
72
75
73 if 'CONTENT_TYPE' in environ:
76 if 'CONTENT_TYPE' in environ:
74 headers['Content-Type'] = environ['CONTENT_TYPE']
77 headers['Content-Type'] = environ['CONTENT_TYPE']
75
78
76 if 'CONTENT_LENGTH' in environ:
79 if 'CONTENT_LENGTH' in environ:
77 headers['Content-Length'] = environ['CONTENT_LENGTH']
80 headers['Content-Length'] = environ['CONTENT_LENGTH']
78
81
79 return headers
82 return headers
80
83
81 def _get_response_headers(self, headers):
84 def _get_response_headers(self, headers):
82 headers = [
85 headers = [
83 (h, headers[h])
86 (h, headers[h])
84 for h in headers
87 for h in headers
85 if h.lower() not in self.IGNORED_HEADERS
88 if h.lower() not in self.IGNORED_HEADERS
86 ]
89 ]
87
90
88 # Add custom response header to indicate that this is a VCS response
91 # Add custom response header to indicate that this is a VCS response
89 # and which backend is used.
92 # and which backend is used.
90 headers.append(('X-RhodeCode-Backend', 'svn'))
93 headers.append(('X-RhodeCode-Backend', 'svn'))
91
94
92 return headers
95 return headers
93
96
94
97
95 class SimpleSvn(simplevcs.SimpleVCS):
98 class SimpleSvn(simplevcs.SimpleVCS):
96
99
97 SCM = 'svn'
100 SCM = 'svn'
98 READ_ONLY_COMMANDS = ('OPTIONS', 'PROPFIND', 'GET', 'REPORT')
101 READ_ONLY_COMMANDS = ('OPTIONS', 'PROPFIND', 'GET', 'REPORT')
99
102
100 def _get_repository_name(self, environ):
103 def _get_repository_name(self, environ):
101 """
104 """
102 Gets repository name out of PATH_INFO header
105 Gets repository name out of PATH_INFO header
103
106
104 :param environ: environ where PATH_INFO is stored
107 :param environ: environ where PATH_INFO is stored
105 """
108 """
106 path = environ['PATH_INFO'].split('!')
109 path = environ['PATH_INFO'].split('!')
107 repo_name = path[0].strip('/')
110 repo_name = path[0].strip('/')
108
111
109 # SVN includes the whole path in it's requests, including
112 # SVN includes the whole path in it's requests, including
110 # subdirectories inside the repo. Therefore we have to search for
113 # subdirectories inside the repo. Therefore we have to search for
111 # the repo root directory.
114 # the repo root directory.
112 if not is_valid_repo(repo_name, self.basepath, self.SCM):
115 if not is_valid_repo(repo_name, self.basepath, self.SCM):
113 current_path = ''
116 current_path = ''
114 for component in repo_name.split('/'):
117 for component in repo_name.split('/'):
115 current_path += component
118 current_path += component
116 if is_valid_repo(current_path, self.basepath, self.SCM):
119 if is_valid_repo(current_path, self.basepath, self.SCM):
117 return current_path
120 return current_path
118 current_path += '/'
121 current_path += '/'
119
122
120 return repo_name
123 return repo_name
121
124
122 def _get_action(self, environ):
125 def _get_action(self, environ):
123 return (
126 return (
124 'pull'
127 'pull'
125 if environ['REQUEST_METHOD'] in self.READ_ONLY_COMMANDS
128 if environ['REQUEST_METHOD'] in self.READ_ONLY_COMMANDS
126 else 'push')
129 else 'push')
127
130
128 def _create_wsgi_app(self, repo_path, repo_name, config):
131 def _create_wsgi_app(self, repo_path, repo_name, config):
129 return SimpleSvnApp(config)
132 return SimpleSvnApp(config)
130
133
131 def _create_config(self, extras, repo_name):
134 def _create_config(self, extras, repo_name):
132 server_url = rhodecode.CONFIG.get(
135 server_url = rhodecode.CONFIG.get(
133 'rhodecode_subversion_http_server_url', '')
136 'rhodecode_subversion_http_server_url', '')
134 extras['subversion_http_server_url'] = (
137 extras['subversion_http_server_url'] = (
135 server_url or 'http://localhost/')
138 server_url or 'http://localhost/')
136 return extras
139 return extras
General Comments 0
You need to be logged in to leave comments. Login now