0001"""
0002This implements the basic framework for making an HTTP proxy app from
0003your filter.
0004
0005This is the basic pattern::
0006
0007    def main(args=None):
0008        if args is None:
0009            args = sys.argv[1:]
0010        parser = make_basic_optparse('MyPackage')
0011        parser.add_option('--foo',
0012                          dest='foo')
0013        options, args = parser.parse_args(args)
0014        wsgi_middleware = make_my_foo_filter(
0015            options.foo)
0016        run_proxy_command(options, args, wsgi_middleware, parser)
0017
0018Note that ``wsgi_middleware`` should be a callable that takes one
0019argument (the application being wrapped) and returns a WSGI
0020application that wraps the given application.
0021"""
0022
0023import sys
0024import re
0025import optparse
0026import pkg_resources
0027import textwrap
0028import urlparse
0029from wsgifilter import proxyapp
0030from wsgifilter.relocateresponse import RelocateMiddleware
0031
0032__all__ = ['make_basic_optparser', 'run_proxy_command',
0033           'run_proxy']
0034
0035scheme_re = re.compile(r'^[a-z]+:', re.I)
0036
0037def make_basic_optparser(
0038    distro_name,
0039    help=None,
0040    usage='%prog [OPTIONS] REMOTE [SERVER]\n\n%(help)s',
0041    ):
0042    """
0043    Creates a parser that parses the options for the run_proxy()
0044    function.  You may modify the parser this returns, to add
0045    option appropriate for building your WSGI middleware.
0046    """
0047    distro = pkg_resources.get_distribution(distro_name)
0048    usage = usage.replace('%prog', '\000 prog \000')
0049    usage = usage % {'help': help or ''}
0050    usage = usage.replace('\000 prog \000', '%prog')
0051    parser = optparse.OptionParser(
0052        version=str(distro),
0053        usage=usage)
0054    parser.add_option(
0055        '--transparent',
0056        help="Do not rewrite the Host header when passing the request on",
0057        action='store_true',
0058        dest='transparent')
0059    parser.add_option(
0060        '--debug',
0061        help="Show tracebacks when an error occurs (use twice for fancy/dangerous traceback)",
0062        action="count",
0063        dest="debug")
0064    parser.add_option(
0065        '--request-log',
0066        help="Show an apache-style log of requests (use twice for more logging)",
0067        action="count",
0068        dest="request_log",
0069        default=0)
0070    parser.add_option(
0071        '--rewrite',
0072        help="Rewrite all headers and links",
0073        action="store_true",
0074        dest="rewrite")
0075    return parser
0076
0077def run_proxy_command(
0078    options, args, wsgi_middleware, parser=None):
0079    """
0080    Run the proxy command, using the options and args and the WSGI
0081    middleware
0082    """
0083    msg = None
0084    if not args:
0085        msg = 'You must provide the remote URL'
0086    elif len(args) > 2:
0087        msg = 'Too many arguments (%s): you may only provide a remote URL and server host.' % (' '.join(args[2:]))
0088        msg = textwrap.fill(msg)
0089    if msg:
0090        print msg
0091        if parser is not None:
0092            parser.print_usage()
0093        sys.exit(2)
0094    if len(args) == 1:
0095        args.append('http://localhost:8080')
0096    remote, server = args
0097    if not scheme_re.search(remote):
0098        remote = 'http://' + remote
0099    if not scheme_re.search(server):
0100        server = 'http://' + server
0101    run_proxy(remote, server, wsgi_middleware,
0102              transparent=options.transparent,
0103              debug=options.debug,
0104              request_log=options.request_log,
0105              rewrite=options.rewrite,
0106              )
0107
0108def run_proxy(remote, server, wsgi_middleware,
0109              transparent=False, debug=0, request_log=0,
0110              rewrite=False):
0111    # just in case...
0112    pkg_resources.require('Paste')
0113    from paste import httpserver
0114    app = proxyapp.ForcedProxy(
0115        remote=remote,
0116        force_host=not transparent)
0117    if request_log > 1:
0118        app = proxyapp.DebugHeaders(app)
0119    if rewrite:
0120        app = RelocateMiddleware(
0121            app, old_href=remote)
0122    if request_log:
0123        from paste.translogger import TransLogger
0124        app = TransLogger(app)
0125    if debug:
0126        if debug > 1:
0127            from paste.evalexception.middleware import EvalException
0128            app = EvalException(app)
0129        else:
0130            from paste.exceptions.errormiddleware import ErrorMiddleware
0131            app = ErrorMiddleware(app, debug=True)
0132    app = wsgi_middleware(app)
0133    print 'Serving on    %s' % server
0134    print 'Remote server %s' % remote
0135    server_parsed = urlparse.urlsplit(server, 'http')
0136    if server_parsed[0] != 'http':
0137        raise ValueError('Only http: is currently supported for the server')
0138    if ((server_parsed[2] and server_parsed[2] != '/')
0139        or server_parsed[4]):
0140        raise ValueError('You may not give a path or query string for the server argument (%r)' % server)
0141    try:
0142        httpserver.serve(app, host=server_parsed[1])
0143    except KeyboardInterrupt:
0144        print 'Exiting.'
0145        sys.exit()