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
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()