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)