0001"""
0002WSGI proxy application that applies a deliverance theme while
0003passing the request to another HTTP server
0004"""
0005
0006import urlparse
0007from cStringIO import StringIO
0008import threading
0009import sys
0010from paste.proxy import TransparentProxy
0011
0012class ForcedProxy(object):
0013
0014    def __init__(self, remote, force_host=True):
0015        self.remote = remote
0016        self.remote_parts = urlparse.urlsplit(remote, 'http')
0017        self.force_host = force_host
0018        if force_host:
0019            proxy_force_host = self.remote_parts[1]
0020        else:
0021            proxy_force_host = None
0022        self.proxy_app = TransparentProxy(
0023            force_host=proxy_force_host)
0024
0025    def __repr__(self):
0026        return '<%s %s remote=%r force_host=%r>' % (
0027            self.__class__.__name__,
0028            hex(id(self)),
0029            self.remote,
0030            self.force_host)
0031
0032    def __call__(self, environ, start_response):
0033        if self.force_host:
0034            environ['HTTP_X_FORWARDED_SERVER'] = environ.get('HTTP_HOST', '')
0035            environ['HTTP_HOST'] = self.remote_parts[1]
0036            environ['HTTP_X_FORWARDED_SCHEME'] = environ['wsgi.url_scheme']
0037            remote_netloc = self.remote_parts[1]
0038            if ':' in remote_netloc:
0039                remote_host, remote_port = remote_netloc.split(':', 1)
0040            else:
0041                remote_host = remote_netloc
0042                if environ['wsgi.url_scheme'] == 'http':
0043                    remote_port = '80'
0044                else:
0045                    remote_port = '443'
0046            environ['SERVER_NAME'] = remote_host
0047            environ['SERVER_PORT'] = remote_port
0048            environ['wsgi.url_scheme'] = self.remote_parts[0]
0049        remote_qs = self.remote_parts[4]
0050        if remote_qs:
0051            cur = environ.get('QUERY_STRING', '')
0052            if cur:
0053                cur += '&' + remote_qs
0054            else:
0055                cur = remote_qs
0056            environ['QUERY_STRING'] = remote_qs
0057        environ['SCRIPT_NAME'] = self.remote_parts[2]
0058        # leave PATH_INFO alone
0059        # @@: Should handle username/password
0060        return self.proxy_app(environ, start_response)
0061
0062
0063class DebugHeaders(object):
0064
0065    translate_keys = {
0066        'CONTENT_LENGTH': 'HTTP_CONTENT_LENGTH',
0067        'CONTENT_TYPE': 'HTTP_CONTENT_TYPE',
0068        }
0069
0070    def __init__(self, app, show_body=False, output=sys.stdout):
0071        self.app = app
0072        self.show_body = show_body
0073        self.output = output or sys.stdout
0074
0075    def __call__(self, environ, start_response):
0076        from paste.request import construct_url
0077        self.output.write(
0078            'Incoming headers: (%s %s)\n' %
0079            (environ['REQUEST_METHOD'], construct_url(environ)))
0080        for name, value in sorted(environ.items()):
0081            name = self.translate_keys.get(name, name)
0082            if not name.startswith('HTTP_'):
0083                continue
0084            name = name[5:].replace('_', '-').title()
0085            self.output.write('  %s: %s\n' % (name, value))
0086        if self.show_body:
0087            self.show_request_body(environ)
0088        def repl_start_response(status, headers, exc_info=None):
0089            self.output.write('Outgoing headers: (%s)\n' % status)
0090            for name, value in headers:
0091                self.output.write('  %s: %s\n' % (name.title(), value))
0092            start_response(status, headers, exc_info)
0093        return self.app(environ, repl_start_response)
0094
0095    def show_request_body(self, environ):
0096        length = int(environ.get('CONTENT_LENGTH') or '0')
0097        body = environ['wsgi.input'].read(length)
0098        environ['wsgi.input'] = StringIO(body)
0099        if body:
0100            for line in body.splitlines():
0101                # This way we won't print out control characters:
0102                self.output.write(line.encode('string_escape')+'\n')
0103            self.output.write('-'*70+'\n')
0104
0105def make_debug_headers(app, global_conf, show_body=False,
0106                       stderr=False):
0107    """
0108    Show all the headers that come to the application.
0109
0110    These are printed to sys.stdout, or sys.stderr if stderr=True.  If
0111    show_body is true, then the body of all requests is also
0112    displayed.
0113    """
0114    from paste.deploy.converters import asbool
0115    if asbool(stderr):
0116        output = sys.stderr
0117    else:
0118        output = sys.stdout
0119    return DebugHeaders(app, show_body=asbool(show_body),
0120                        output=output)