##// END OF EJS Templates
svn-support: enable better logging of handled SVN commands.
marcink -
r1217:63dcab29 default
parent child Browse files
Show More
@@ -1,155 +1,157 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 logging
22 22 from urlparse import urljoin
23 23
24 24 import requests
25 25 from webob.exc import HTTPNotAcceptable
26 26
27 27 from rhodecode.lib.middleware import simplevcs
28 28 from rhodecode.lib.utils import is_valid_repo
29 29 from rhodecode.lib.utils2 import str2bool
30 30
31 31 log = logging.getLogger(__name__)
32 32
33 33
34 34 class SimpleSvnApp(object):
35 35 IGNORED_HEADERS = [
36 36 'connection', 'keep-alive', 'content-encoding',
37 37 'transfer-encoding', 'content-length']
38 38
39 39 def __init__(self, config):
40 40 self.config = config
41 41
42 42 def __call__(self, environ, start_response):
43 43 request_headers = self._get_request_headers(environ)
44 44
45 45 data = environ['wsgi.input']
46 46 # johbo: Avoid that we end up with sending the request in chunked
47 47 # transfer encoding (mainly on Gunicorn). If we know the content
48 48 # length, then we should transfer the payload in one request.
49 49 if environ['REQUEST_METHOD'] == 'MKCOL' or 'CONTENT_LENGTH' in environ:
50 50 data = data.read()
51 51
52 log.debug('Calling: %s method via `%s`', environ['REQUEST_METHOD'],
53 self._get_url(environ['PATH_INFO']))
52 54 response = requests.request(
53 55 environ['REQUEST_METHOD'], self._get_url(environ['PATH_INFO']),
54 56 data=data, headers=request_headers)
55 57
56 58 response_headers = self._get_response_headers(response.headers)
57 59 start_response(
58 60 '{} {}'.format(response.status_code, response.reason),
59 61 response_headers)
60 62 return response.iter_content(chunk_size=1024)
61 63
62 64 def _get_url(self, path):
63 65 return urljoin(
64 66 self.config.get('subversion_http_server_url', ''), path)
65 67
66 68 def _get_request_headers(self, environ):
67 69 headers = {}
68 70
69 71 for key in environ:
70 72 if not key.startswith('HTTP_'):
71 73 continue
72 74 new_key = key.split('_')
73 75 new_key = [k.capitalize() for k in new_key[1:]]
74 76 new_key = '-'.join(new_key)
75 77 headers[new_key] = environ[key]
76 78
77 79 if 'CONTENT_TYPE' in environ:
78 80 headers['Content-Type'] = environ['CONTENT_TYPE']
79 81
80 82 if 'CONTENT_LENGTH' in environ:
81 83 headers['Content-Length'] = environ['CONTENT_LENGTH']
82 84
83 85 return headers
84 86
85 87 def _get_response_headers(self, headers):
86 88 headers = [
87 89 (h, headers[h])
88 90 for h in headers
89 91 if h.lower() not in self.IGNORED_HEADERS
90 92 ]
91 93
92 94 return headers
93 95
94 96
95 97 class DisabledSimpleSvnApp(object):
96 98 def __init__(self, config):
97 99 self.config = config
98 100
99 101 def __call__(self, environ, start_response):
100 102 reason = 'Cannot handle SVN call because: SVN HTTP Proxy is not enabled'
101 103 log.warning(reason)
102 104 return HTTPNotAcceptable(reason)(environ, start_response)
103 105
104 106
105 107 class SimpleSvn(simplevcs.SimpleVCS):
106 108
107 109 SCM = 'svn'
108 110 READ_ONLY_COMMANDS = ('OPTIONS', 'PROPFIND', 'GET', 'REPORT')
109 111 DEFAULT_HTTP_SERVER = 'http://localhost:8090'
110 112
111 113 def _get_repository_name(self, environ):
112 114 """
113 115 Gets repository name out of PATH_INFO header
114 116
115 117 :param environ: environ where PATH_INFO is stored
116 118 """
117 119 path = environ['PATH_INFO'].split('!')
118 120 repo_name = path[0].strip('/')
119 121
120 122 # SVN includes the whole path in it's requests, including
121 123 # subdirectories inside the repo. Therefore we have to search for
122 124 # the repo root directory.
123 125 if not is_valid_repo(repo_name, self.basepath, self.SCM):
124 126 current_path = ''
125 127 for component in repo_name.split('/'):
126 128 current_path += component
127 129 if is_valid_repo(current_path, self.basepath, self.SCM):
128 130 return current_path
129 131 current_path += '/'
130 132
131 133 return repo_name
132 134
133 135 def _get_action(self, environ):
134 136 return (
135 137 'pull'
136 138 if environ['REQUEST_METHOD'] in self.READ_ONLY_COMMANDS
137 139 else 'push')
138 140
139 141 def _create_wsgi_app(self, repo_path, repo_name, config):
140 142 if self._is_svn_enabled():
141 143 return SimpleSvnApp(config)
142 144 # we don't have http proxy enabled return dummy request handler
143 145 return DisabledSimpleSvnApp(config)
144 146
145 147 def _is_svn_enabled(self):
146 148 conf = self.repo_vcs_config
147 149 return str2bool(conf.get('vcs_svn_proxy', 'http_requests_enabled'))
148 150
149 151 def _create_config(self, extras, repo_name):
150 152 conf = self.repo_vcs_config
151 153 server_url = conf.get('vcs_svn_proxy', 'http_server_url')
152 154 server_url = server_url or self.DEFAULT_HTTP_SERVER
153 155
154 156 extras['subversion_http_server_url'] = server_url
155 157 return extras
General Comments 0
You need to be logged in to leave comments. Login now