##// END OF EJS Templates
commits: add protection against 500 errors on commit children/parents page.
commits: add protection against 500 errors on commit children/parents page.

File last commit:

r1586:23640ce6 default
r2150:82423302 default
Show More
simplesvn.py
169 lines | 5.8 KiB | text/x-python | PythonLexer
project: added all source files and assets
r1 # -*- coding: utf-8 -*-
license: updated copyright year to 2017
r1271 # Copyright (C) 2010-2017 RhodeCode GmbH
project: added all source files and assets
r1 #
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
vcs: added logging into VCS middlewares
r753 import logging
svn: escape special chars to allow interactions with non-standard svn paths....
r1586 import urllib
project: added all source files and assets
r1 from urlparse import urljoin
svn: escape special chars to allow interactions with non-standard svn paths....
r1586
project: added all source files and assets
r1 import requests
vcs: moved svn proxy settings into vcs related settings...
r754 from webob.exc import HTTPNotAcceptable
project: added all source files and assets
r1
from rhodecode.lib.middleware import simplevcs
from rhodecode.lib.utils import is_valid_repo
vcs: moved svn proxy settings into vcs related settings...
r754 from rhodecode.lib.utils2 import str2bool
project: added all source files and assets
r1
vcs: added logging into VCS middlewares
r753 log = logging.getLogger(__name__)
project: added all source files and assets
r1
class SimpleSvnApp(object):
IGNORED_HEADERS = [
'connection', 'keep-alive', 'content-encoding',
Martin Bornhold
svn: Ignore the content length header from response, fixes #4112...
r473 'transfer-encoding', 'content-length']
project: added all source files and assets
r1
def __init__(self, config):
self.config = config
def __call__(self, environ, start_response):
request_headers = self._get_request_headers(environ)
data = environ['wsgi.input']
svn: Avoid chunked transfer for Subversion
r282 # johbo: Avoid that we end up with sending the request in chunked
# transfer encoding (mainly on Gunicorn). If we know the content
# length, then we should transfer the payload in one request.
if environ['REQUEST_METHOD'] == 'MKCOL' or 'CONTENT_LENGTH' in environ:
project: added all source files and assets
r1 data = data.read()
svn-support: enable better logging of handled SVN commands.
r1217 log.debug('Calling: %s method via `%s`', environ['REQUEST_METHOD'],
self._get_url(environ['PATH_INFO']))
project: added all source files and assets
r1 response = requests.request(
environ['REQUEST_METHOD'], self._get_url(environ['PATH_INFO']),
data=data, headers=request_headers)
svn: escape special chars to allow interactions with non-standard svn paths....
r1586 if response.status_code not in [200, 401]:
if response.status_code >= 500:
log.error('Got SVN response:%s with text:`%s`',
response, response.text)
else:
log.debug('Got SVN response:%s with text:`%s`',
response, response.text)
project: added all source files and assets
r1 response_headers = self._get_response_headers(response.headers)
start_response(
'{} {}'.format(response.status_code, response.reason),
response_headers)
return response.iter_content(chunk_size=1024)
def _get_url(self, path):
svn: escape special chars to allow interactions with non-standard svn paths....
r1586 url_path = urljoin(
project: added all source files and assets
r1 self.config.get('subversion_http_server_url', ''), path)
svn: escape special chars to allow interactions with non-standard svn paths....
r1586 url_path = urllib.quote(url_path, safe="/:=~+!$,;'")
return url_path
project: added all source files and assets
r1
def _get_request_headers(self, environ):
headers = {}
for key in environ:
if not key.startswith('HTTP_'):
continue
new_key = key.split('_')
new_key = [k.capitalize() for k in new_key[1:]]
new_key = '-'.join(new_key)
headers[new_key] = environ[key]
if 'CONTENT_TYPE' in environ:
headers['Content-Type'] = environ['CONTENT_TYPE']
if 'CONTENT_LENGTH' in environ:
headers['Content-Length'] = environ['CONTENT_LENGTH']
return headers
def _get_response_headers(self, headers):
Martin Bornhold
vcs: Add custom response header 'X-RhodeCode-Backend' to indicate VCS responses and which backend is in use.
r608 headers = [
project: added all source files and assets
r1 (h, headers[h])
for h in headers
if h.lower() not in self.IGNORED_HEADERS
]
Martin Bornhold
vcs: Add custom response header 'X-RhodeCode-Backend' to indicate VCS responses and which backend is in use.
r608 return headers
project: added all source files and assets
r1
vcs: moved svn proxy settings into vcs related settings...
r754 class DisabledSimpleSvnApp(object):
def __init__(self, config):
self.config = config
def __call__(self, environ, start_response):
reason = 'Cannot handle SVN call because: SVN HTTP Proxy is not enabled'
log.warning(reason)
return HTTPNotAcceptable(reason)(environ, start_response)
project: added all source files and assets
r1 class SimpleSvn(simplevcs.SimpleVCS):
SCM = 'svn'
READ_ONLY_COMMANDS = ('OPTIONS', 'PROPFIND', 'GET', 'REPORT')
vcs: moved svn proxy settings into vcs related settings...
r754 DEFAULT_HTTP_SERVER = 'http://localhost:8090'
project: added all source files and assets
r1
def _get_repository_name(self, environ):
"""
Gets repository name out of PATH_INFO header
:param environ: environ where PATH_INFO is stored
"""
path = environ['PATH_INFO'].split('!')
repo_name = path[0].strip('/')
# SVN includes the whole path in it's requests, including
# subdirectories inside the repo. Therefore we have to search for
# the repo root directory.
if not is_valid_repo(repo_name, self.basepath, self.SCM):
current_path = ''
for component in repo_name.split('/'):
current_path += component
if is_valid_repo(current_path, self.basepath, self.SCM):
return current_path
current_path += '/'
return repo_name
def _get_action(self, environ):
return (
'pull'
if environ['REQUEST_METHOD'] in self.READ_ONLY_COMMANDS
else 'push')
def _create_wsgi_app(self, repo_path, repo_name, config):
vcs: moved svn proxy settings into vcs related settings...
r754 if self._is_svn_enabled():
return SimpleSvnApp(config)
# we don't have http proxy enabled return dummy request handler
return DisabledSimpleSvnApp(config)
def _is_svn_enabled(self):
conf = self.repo_vcs_config
return str2bool(conf.get('vcs_svn_proxy', 'http_requests_enabled'))
project: added all source files and assets
r1
def _create_config(self, extras, repo_name):
vcs: moved svn proxy settings into vcs related settings...
r754 conf = self.repo_vcs_config
server_url = conf.get('vcs_svn_proxy', 'http_server_url')
server_url = server_url or self.DEFAULT_HTTP_SERVER
extras['subversion_http_server_url'] = server_url
project: added all source files and assets
r1 return extras