vcs.py
156 lines
| 5.1 KiB
| text/x-python
|
PythonLexer
r1 | # -*- coding: utf-8 -*- | |||
# Copyright (C) 2010-2016 RhodeCode GmbH | ||||
# | ||||
# 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/ | ||||
import gzip | ||||
import shutil | ||||
import logging | ||||
import tempfile | ||||
import urlparse | ||||
import rhodecode | ||||
from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled | ||||
from rhodecode.lib.middleware.simplegit import SimpleGit, GIT_PROTO_PAT | ||||
from rhodecode.lib.middleware.simplehg import SimpleHg | ||||
from rhodecode.lib.middleware.simplesvn import SimpleSvn | ||||
log = logging.getLogger(__name__) | ||||
def is_git(environ): | ||||
""" | ||||
Returns True if requests should be handled by GIT wsgi middleware | ||||
""" | ||||
is_git_path = GIT_PROTO_PAT.match(environ['PATH_INFO']) | ||||
log.debug( | ||||
'request path: `%s` detected as GIT PROTOCOL %s', environ['PATH_INFO'], | ||||
is_git_path is not None) | ||||
return is_git_path | ||||
def is_hg(environ): | ||||
""" | ||||
Returns True if requests target is mercurial server - header | ||||
``HTTP_ACCEPT`` of such request would start with ``application/mercurial``. | ||||
""" | ||||
is_hg_path = False | ||||
http_accept = environ.get('HTTP_ACCEPT') | ||||
if http_accept and http_accept.startswith('application/mercurial'): | ||||
query = urlparse.parse_qs(environ['QUERY_STRING']) | ||||
if 'cmd' in query: | ||||
is_hg_path = True | ||||
log.debug( | ||||
'request path: `%s` detected as HG PROTOCOL %s', environ['PATH_INFO'], | ||||
is_hg_path) | ||||
return is_hg_path | ||||
def is_svn(environ): | ||||
""" | ||||
Returns True if requests target is Subversion server | ||||
""" | ||||
http_dav = environ.get('HTTP_DAV', '') | ||||
is_svn_path = 'subversion' in http_dav | ||||
log.debug( | ||||
'request path: `%s` detected as SVN PROTOCOL %s', environ['PATH_INFO'], | ||||
is_svn_path) | ||||
return is_svn_path | ||||
class GunzipMiddleware(object): | ||||
""" | ||||
WSGI middleware that unzips gzip-encoded requests before | ||||
passing on to the underlying application. | ||||
""" | ||||
def __init__(self, application): | ||||
self.app = application | ||||
def __call__(self, environ, start_response): | ||||
accepts_encoding_header = environ.get('HTTP_CONTENT_ENCODING', b'') | ||||
if b'gzip' in accepts_encoding_header: | ||||
log.debug('gzip detected, now running gunzip wrapper') | ||||
wsgi_input = environ['wsgi.input'] | ||||
if not hasattr(environ['wsgi.input'], 'seek'): | ||||
# The gzip implementation in the standard library of Python 2.x | ||||
# requires the '.seek()' and '.tell()' methods to be available | ||||
# on the input stream. Read the data into a temporary file to | ||||
# work around this limitation. | ||||
wsgi_input = tempfile.SpooledTemporaryFile(64 * 1024 * 1024) | ||||
shutil.copyfileobj(environ['wsgi.input'], wsgi_input) | ||||
wsgi_input.seek(0) | ||||
environ['wsgi.input'] = gzip.GzipFile(fileobj=wsgi_input, mode='r') | ||||
# since we "Ungzipped" the content we say now it's no longer gzip | ||||
# content encoding | ||||
del environ['HTTP_CONTENT_ENCODING'] | ||||
# content length has changes ? or i'm not sure | ||||
if 'CONTENT_LENGTH' in environ: | ||||
del environ['CONTENT_LENGTH'] | ||||
else: | ||||
log.debug('content not gzipped, gzipMiddleware passing ' | ||||
'request further') | ||||
return self.app(environ, start_response) | ||||
class VCSMiddleware(object): | ||||
def __init__(self, app, config, appenlight_client): | ||||
self.application = app | ||||
self.config = config | ||||
self.appenlight_client = appenlight_client | ||||
def _get_handler_app(self, environ): | ||||
app = None | ||||
if is_hg(environ): | ||||
app = SimpleHg(self.application, self.config) | ||||
if is_git(environ): | ||||
app = SimpleGit(self.application, self.config) | ||||
proxy_svn = rhodecode.CONFIG.get( | ||||
'rhodecode_proxy_subversion_http_requests', False) | ||||
if proxy_svn and is_svn(environ): | ||||
app = SimpleSvn(self.application, self.config) | ||||
if app: | ||||
app = GunzipMiddleware(app) | ||||
app, _ = wrap_in_appenlight_if_enabled( | ||||
app, self.config, self.appenlight_client) | ||||
return app | ||||
def __call__(self, environ, start_response): | ||||
# check if we handle one of interesting protocols ? | ||||
vcs_handler = self._get_handler_app(environ) | ||||
if vcs_handler: | ||||
return vcs_handler(environ, start_response) | ||||
return self.application(environ, start_response) | ||||