redirector.py SampleΒΆ

  1# This is a sample ISAPI extension written in Python.
  2#
  3# Please see README.txt in this directory, and specifically the
  4# information about the "loader" DLL - installing this sample will create
  5# "_redirector.dll" in the current directory.  The readme explains this.
  6
  7# Executing this script (or any server config script) will install the extension
  8# into your web server. As the server executes, the PyISAPI framework will load
  9# this module and create your Extension and Filter objects.
 10
 11# This is the simplest possible redirector (or proxy) we can write.  The
 12# extension installs with a mask of '*' in the root of the site.
 13# As an added bonus though, we optionally show how, on IIS6 and later, we
 14# can use HSE_ERQ_EXEC_URL to ignore certain requests - in IIS5 and earlier
 15# we can only do this with an ISAPI filter - see redirector_with_filter for
 16# an example.  If this sample is run on IIS5 or earlier it simply ignores
 17# any excludes.
 18
 19from isapi import isapicon, threaded_extension
 20import sys
 21import traceback
 22
 23try:
 24    from urllib.request import urlopen
 25except ImportError:
 26    # py3k spelling...
 27    from urllib.request import urlopen
 28import win32api
 29
 30# sys.isapidllhandle will exist when we are loaded by the IIS framework.
 31# In this case we redirect our output to the win32traceutil collector.
 32if hasattr(sys, "isapidllhandle"):
 33    import win32traceutil
 34
 35# The site we are proxying.
 36proxy = "http://www.python.org"
 37
 38# Urls we exclude (ie, allow IIS to handle itself) - all are lowered,
 39# and these entries exist by default on Vista...
 40excludes = ["/iisstart.htm", "/welcome.png"]
 41
 42# An "io completion" function, called when ecb.ExecURL completes...
 43def io_callback(ecb, url, cbIO, errcode):
 44    # Get the status of our ExecURL
 45    httpstatus, substatus, win32 = ecb.GetExecURLStatus()
 46    print(
 47        "ExecURL of %r finished with http status %d.%d, win32 status %d (%s)"
 48        % (url, httpstatus, substatus, win32, win32api.FormatMessage(win32).strip())
 49    )
 50    # nothing more to do!
 51    ecb.DoneWithSession()
 52
 53
 54# The ISAPI extension - handles all requests in the site.
 55class Extension(threaded_extension.ThreadPoolExtension):
 56    "Python sample Extension"
 57
 58    def Dispatch(self, ecb):
 59        # Note that our ThreadPoolExtension base class will catch exceptions
 60        # in our Dispatch method, and write the traceback to the client.
 61        # That is perfect for this sample, so we don't catch our own.
 62        # print 'IIS dispatching "%s"' % (ecb.GetServerVariable("URL"),)
 63        url = ecb.GetServerVariable("URL").decode("ascii")
 64        for exclude in excludes:
 65            if url.lower().startswith(exclude):
 66                print("excluding %s" % url)
 67                if ecb.Version < 0x60000:
 68                    print("(but this is IIS5 or earlier - can't do 'excludes')")
 69                else:
 70                    ecb.IOCompletion(io_callback, url)
 71                    ecb.ExecURL(
 72                        None,
 73                        None,
 74                        None,
 75                        None,
 76                        None,
 77                        isapicon.HSE_EXEC_URL_IGNORE_CURRENT_INTERCEPTOR,
 78                    )
 79                    return isapicon.HSE_STATUS_PENDING
 80
 81        new_url = proxy + url
 82        print("Opening %s" % new_url)
 83        fp = urlopen(new_url)
 84        headers = fp.info()
 85        # subtle py3k breakage: in py3k, str(headers) has normalized \r\n
 86        # back to \n and also stuck an extra \n term.  py2k leaves the
 87        # \r\n from the server in tact and finishes with a single term.
 88        if sys.version_info < (3, 0):
 89            header_text = str(headers) + "\r\n"
 90        else:
 91            # take *all* trailing \n off, replace remaining with
 92            # \r\n, then add the 2 trailing \r\n.
 93            header_text = str(headers).rstrip("\n").replace("\n", "\r\n") + "\r\n\r\n"
 94        ecb.SendResponseHeaders("200 OK", header_text, False)
 95        ecb.WriteClient(fp.read())
 96        ecb.DoneWithSession()
 97        print("Returned data from '%s'" % (new_url,))
 98        return isapicon.HSE_STATUS_SUCCESS
 99
100
101# The entry points for the ISAPI extension.
102def __ExtensionFactory__():
103    return Extension()
104
105
106if __name__ == "__main__":
107    # If run from the command-line, install ourselves.
108    from isapi.install import *
109
110    params = ISAPIParameters()
111    # Setup the virtual directories - this is a list of directories our
112    # extension uses - in this case only 1.
113    # Each extension has a "script map" - this is the mapping of ISAPI
114    # extensions.
115    sm = [ScriptMapParams(Extension="*", Flags=0)]
116    vd = VirtualDirParameters(
117        Name="/",
118        Description=Extension.__doc__,
119        ScriptMaps=sm,
120        ScriptMapUpdate="replace",
121    )
122    params.VirtualDirs = [vd]
123    HandleCommandLine(params)