##// END OF EJS Templates
svn: Ignore the content length header from response, fixes #4112...
Martin Bornhold -
r473:7e153d56 default
parent child Browse files
Show More
@@ -1,130 +1,130 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 from urlparse import urljoin
21 from urlparse import urljoin
22
22
23 import requests
23 import requests
24
24
25 import rhodecode
25 import rhodecode
26 from rhodecode.lib.middleware import simplevcs
26 from rhodecode.lib.middleware import simplevcs
27 from rhodecode.lib.utils import is_valid_repo
27 from rhodecode.lib.utils import is_valid_repo
28
28
29
29
30 class SimpleSvnApp(object):
30 class SimpleSvnApp(object):
31 IGNORED_HEADERS = [
31 IGNORED_HEADERS = [
32 'connection', 'keep-alive', 'content-encoding',
32 'connection', 'keep-alive', 'content-encoding',
33 'transfer-encoding']
33 'transfer-encoding', 'content-length']
34
34
35 def __init__(self, config):
35 def __init__(self, config):
36 self.config = config
36 self.config = config
37
37
38 def __call__(self, environ, start_response):
38 def __call__(self, environ, start_response):
39 request_headers = self._get_request_headers(environ)
39 request_headers = self._get_request_headers(environ)
40
40
41 data = environ['wsgi.input']
41 data = environ['wsgi.input']
42 # johbo: Avoid that we end up with sending the request in chunked
42 # johbo: Avoid that we end up with sending the request in chunked
43 # transfer encoding (mainly on Gunicorn). If we know the content
43 # transfer encoding (mainly on Gunicorn). If we know the content
44 # length, then we should transfer the payload in one request.
44 # length, then we should transfer the payload in one request.
45 if environ['REQUEST_METHOD'] == 'MKCOL' or 'CONTENT_LENGTH' in environ:
45 if environ['REQUEST_METHOD'] == 'MKCOL' or 'CONTENT_LENGTH' in environ:
46 data = data.read()
46 data = data.read()
47
47
48 response = requests.request(
48 response = requests.request(
49 environ['REQUEST_METHOD'], self._get_url(environ['PATH_INFO']),
49 environ['REQUEST_METHOD'], self._get_url(environ['PATH_INFO']),
50 data=data, headers=request_headers)
50 data=data, headers=request_headers)
51
51
52 response_headers = self._get_response_headers(response.headers)
52 response_headers = self._get_response_headers(response.headers)
53 start_response(
53 start_response(
54 '{} {}'.format(response.status_code, response.reason),
54 '{} {}'.format(response.status_code, response.reason),
55 response_headers)
55 response_headers)
56 return response.iter_content(chunk_size=1024)
56 return response.iter_content(chunk_size=1024)
57
57
58 def _get_url(self, path):
58 def _get_url(self, path):
59 return urljoin(
59 return urljoin(
60 self.config.get('subversion_http_server_url', ''), path)
60 self.config.get('subversion_http_server_url', ''), path)
61
61
62 def _get_request_headers(self, environ):
62 def _get_request_headers(self, environ):
63 headers = {}
63 headers = {}
64
64
65 for key in environ:
65 for key in environ:
66 if not key.startswith('HTTP_'):
66 if not key.startswith('HTTP_'):
67 continue
67 continue
68 new_key = key.split('_')
68 new_key = key.split('_')
69 new_key = [k.capitalize() for k in new_key[1:]]
69 new_key = [k.capitalize() for k in new_key[1:]]
70 new_key = '-'.join(new_key)
70 new_key = '-'.join(new_key)
71 headers[new_key] = environ[key]
71 headers[new_key] = environ[key]
72
72
73 if 'CONTENT_TYPE' in environ:
73 if 'CONTENT_TYPE' in environ:
74 headers['Content-Type'] = environ['CONTENT_TYPE']
74 headers['Content-Type'] = environ['CONTENT_TYPE']
75
75
76 if 'CONTENT_LENGTH' in environ:
76 if 'CONTENT_LENGTH' in environ:
77 headers['Content-Length'] = environ['CONTENT_LENGTH']
77 headers['Content-Length'] = environ['CONTENT_LENGTH']
78
78
79 return headers
79 return headers
80
80
81 def _get_response_headers(self, headers):
81 def _get_response_headers(self, headers):
82 return [
82 return [
83 (h, headers[h])
83 (h, headers[h])
84 for h in headers
84 for h in headers
85 if h.lower() not in self.IGNORED_HEADERS
85 if h.lower() not in self.IGNORED_HEADERS
86 ]
86 ]
87
87
88
88
89 class SimpleSvn(simplevcs.SimpleVCS):
89 class SimpleSvn(simplevcs.SimpleVCS):
90
90
91 SCM = 'svn'
91 SCM = 'svn'
92 READ_ONLY_COMMANDS = ('OPTIONS', 'PROPFIND', 'GET', 'REPORT')
92 READ_ONLY_COMMANDS = ('OPTIONS', 'PROPFIND', 'GET', 'REPORT')
93
93
94 def _get_repository_name(self, environ):
94 def _get_repository_name(self, environ):
95 """
95 """
96 Gets repository name out of PATH_INFO header
96 Gets repository name out of PATH_INFO header
97
97
98 :param environ: environ where PATH_INFO is stored
98 :param environ: environ where PATH_INFO is stored
99 """
99 """
100 path = environ['PATH_INFO'].split('!')
100 path = environ['PATH_INFO'].split('!')
101 repo_name = path[0].strip('/')
101 repo_name = path[0].strip('/')
102
102
103 # SVN includes the whole path in it's requests, including
103 # SVN includes the whole path in it's requests, including
104 # subdirectories inside the repo. Therefore we have to search for
104 # subdirectories inside the repo. Therefore we have to search for
105 # the repo root directory.
105 # the repo root directory.
106 if not is_valid_repo(repo_name, self.basepath, self.SCM):
106 if not is_valid_repo(repo_name, self.basepath, self.SCM):
107 current_path = ''
107 current_path = ''
108 for component in repo_name.split('/'):
108 for component in repo_name.split('/'):
109 current_path += component
109 current_path += component
110 if is_valid_repo(current_path, self.basepath, self.SCM):
110 if is_valid_repo(current_path, self.basepath, self.SCM):
111 return current_path
111 return current_path
112 current_path += '/'
112 current_path += '/'
113
113
114 return repo_name
114 return repo_name
115
115
116 def _get_action(self, environ):
116 def _get_action(self, environ):
117 return (
117 return (
118 'pull'
118 'pull'
119 if environ['REQUEST_METHOD'] in self.READ_ONLY_COMMANDS
119 if environ['REQUEST_METHOD'] in self.READ_ONLY_COMMANDS
120 else 'push')
120 else 'push')
121
121
122 def _create_wsgi_app(self, repo_path, repo_name, config):
122 def _create_wsgi_app(self, repo_path, repo_name, config):
123 return SimpleSvnApp(config)
123 return SimpleSvnApp(config)
124
124
125 def _create_config(self, extras, repo_name):
125 def _create_config(self, extras, repo_name):
126 server_url = rhodecode.CONFIG.get(
126 server_url = rhodecode.CONFIG.get(
127 'rhodecode_subversion_http_server_url', '')
127 'rhodecode_subversion_http_server_url', '')
128 extras['subversion_http_server_url'] = (
128 extras['subversion_http_server_url'] = (
129 server_url or 'http://localhost/')
129 server_url or 'http://localhost/')
130 return extras
130 return extras
General Comments 0
You need to be logged in to leave comments. Login now