Update of /cvsroot/pywin32/pywin32/isapi/samples
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17001/isapi/samples
Added Files:
.cvsignore README.txt advanced.py redirector.py
Log Message:
Rename pyisapi directory to isapi, so it matches what the user sees, as
part of the process of integrating it with the rest of the build.
--- NEW FILE: .cvsignore ---
*.dll
--- NEW FILE: README.txt ---
In this directory you will find examples of ISAPI filters and extensions.
The filter loading mechanism works like this:
* IIS loads the special Python "loader" DLL. This DLL will generally have a
leading underscore as part of its name.
* This loader DLL looks for a Python module, by removing the first letter of
the DLL base name.
This means that an ISAPI extension module consists of 2 key files - the loader
DLL (eg, "_MyIISModule.dll", and a Python module (which for this example
would be "MyIISModule.py")
When you install an ISAPI extension, the installation code checks to see if
there is a loader DLL for your implementation file - if one does not exist,
or the standard loader is different, it is copied and renamed accordingly.
We use this mechanism to provide the maximum separation between different
Python extensions installed on the same server - otherwise filter order and
other tricky IIS semantics would need to be replicated. Also, each filter
gets its own thread-pool, etc.
--- NEW FILE: advanced.py ---
# This extension demonstrates some advanced features of the Python ISAPI
# framework.
# We demonstrate:
# * Reloading your Python module without shutting down IIS (eg, when your
# .py implementation file changes.)
# * Custom command-line handling - both additional options and commands.
from isapi import isapicon
from isapi.simple import SimpleExtension
import sys, os, stat
if hasattr(sys, "isapidllhandle"):
import win32traceutil
# Notes on reloading
# If your HttpFilterProc or HttpExtensionProc functions raises
# 'isapi.InternalReloadException', the framework will not treat it
# as an error but instead will terminate your extension, reload your
# extension module, re-initialize the instance, and re-issue the request.
# The Initialize functions are called with None as their param. The
# return code from the terminate function is ignored.
#
# This is all the framework does to help you. It is up to your code
# when you raise this exception. This sample uses a Win32 "find
# notification". Whenever windows tells us one of the files in the
# directory has changed, we check if the time of our source-file has
# changed, and set a flag. Next imcoming request, we check the flag and
# raise the special exception if set.
#
# The end result is that the module is automatically reloaded whenever
# the source-file changes - you need take no further action to see your
# changes reflected in the running server.
# The framework only reloads your module - if you have libraries you
# depend on and also want reloaded, you must arrange for this yourself.
# One way of doing this would be to special case the import of these
# modules. Eg:
# --
# try:
# my_module = reload(my_module) # module already imported - reload it
# except NameError:
# import my_module # first time around - import it.
# --
# When your module is imported for the first time, the NameError will
# be raised, and the module imported. When the ISAPI framework reloads
# your module, the existing module will avoid the NameError, and allow
# you to reload that module.
from isapi import InternalReloadException
import win32event, win32file, winerror, win32con, threading
try:
reload_counter += 1
except NameError:
reload_counter = 0
# A watcher thread that checks for __file__ changing.
# When it detects it, it simply sets "change_detected" to true.
class ReloadWatcherThread(threading.Thread):
def __init__(self):
self.change_detected = False
self.filename = __file__
if self.filename.endswith("c") or self.filename.endswith("o"):
self.filename = self.filename[:-1]
self.handle = win32file.FindFirstChangeNotification(
os.path.dirname(self.filename),
False, # watch tree?
win32con.FILE_NOTIFY_CHANGE_LAST_WRITE)
threading.Thread.__init__(self)
def run(self):
last_time = os.stat(self.filename)[stat.ST_MTIME]
while 1:
try:
rc = win32event.WaitForSingleObject(self.handle,
win32event.INFINITE)
win32file.FindNextChangeNotification(self.handle)
except win32event.error, details:
# handle closed - thread should terminate.
if details[0] != winerror.ERROR_INVALID_HANDLE:
raise
break
this_time = os.stat(self.filename)[stat.ST_MTIME]
if this_time != last_time:
print "Detected file change - flagging for reload."
self.change_detected = True
last_time = this_time
def stop(self):
win32file.FindCloseChangeNotification(self.handle)
# The ISAPI extension - handles requests in our virtual dir, and sends the
# response to the client.
class Extension(SimpleExtension):
"Python advanced sample Extension"
def __init__(self):
self.reload_watcher = ReloadWatcherThread()
self.reload_watcher.start()
def HttpExtensionProc(self, ecb):
# NOTE: If you use a ThreadPoolExtension, you must still perform
# this check in HttpExtensionProc - raising the exception from
# The "Dispatch" method will just cause the exception to be
# rendered to the browser.
if self.reload_watcher.change_detected:
print "Doing reload"
raise InternalReloadException
ecb.SendResponseHeaders("200 OK", "Content-Type: text/html\r\n\r\n", 0)
print >> ecb, "<HTML><BODY>"
print >> ecb, "This module has been imported"
print >> ecb, "%d times" % (reload_counter,)
print >> ecb, "</BODY></HTML>"
ecb.close()
return isapicon.HSE_STATUS_SUCCESS
def TerminateExtension(self, status):
self.reload_watcher.stop()
# The entry points for the ISAPI extension.
def __ExtensionFactory__():
return Extension()
# Our special command line customization.
# Pre-install hook for our virtual directory.
def PreInstallDirectory(params, options):
# If the user used our special '--description' option,
# then we override our default.
if options.description:
params.Description = options.description
# Post install hook for our entire script
def PostInstall(params, options):
print
print "The sample has been installed."
print "Point your browser to /AdvancedPythonSample"
print "If you modify the source file and reload the page,"
print "you should see the reload counter increment"
# Handler for our custom 'status' argument.
def status_handler(options, log, arg):
"Query the status of something"
print "Everything seems to be fine!"
custom_arg_handlers = {"status": status_handler}
if __name__=='__main__':
# If run from the command-line, install ourselves.
from isapi.install import *
params = ISAPIParameters(PostInstall = PostInstall)
# Setup the virtual directories - this is a list of directories our
# extension uses - in this case only 1.
# Each extension has a "script map" - this is the mapping of ISAPI
# extensions.
sm = [
ScriptMapParams(Extension="*", Flags=0)
]
vd = VirtualDirParameters(Name="AdvancedPythonSample",
Description = Extension.__doc__,
ScriptMaps = sm,
ScriptMapUpdate = "replace",
# specify the pre-install hook.
PreInstall = PreInstallDirectory
)
params.VirtualDirs = [vd]
# Setup our custom option parser.
from optparse import OptionParser
parser = OptionParser('') # black usage, so isapi sets it.
parser.add_option("", "--description",
action="store",
help="custom description to use for the virtual directory")
HandleCommandLine(params, opt_parser=parser,
custom_arg_handlers = custom_arg_handlers)
--- NEW FILE: redirector.py ---
# This is a sample configuration file for an ISAPI filter and extension
# written in Python.
#
# Please see README.txt in this directory, and specifically the
# information about the "loader" DLL - installing this sample will create
# "_redirector.dll" in the current directory. The readme explains this.
# Executing this script (or any server config script) will install the extension
# into your web server. As the server executes, the PyISAPI framework will load
# this module and create your Extension and Filter objects.
# This sample provides a very simple redirector.
# It is implemented by a filter and an extension.
# * The filter is installed globally, as all filters are.
# * A Virtual Directory named "python" is setup. This dir has our ISAPI
# extension as the only application, mapped to file-extension '*'. Thus, our
# extension handles *all* requests in this directory.
# The basic process is that the filter does URL rewriting, redirecting every
# URL to our Virtual Directory. Our extension then handles this request,
# forwarding the data from the proxied site.
# So, the process is:
# * URL of "index.html" comes in.
# * Filter rewrites this to "/python/index.html"
# * Our extension sees the full "/python/index.html", removes the leading
# portion, and opens and forwards the remote URL.
# This sample is very small - it avoid most error handling, etc. It is for
# demonstration purposes only.
from isapi import isapicon, threaded_extension
from isapi.simple import SimpleFilter
import sys
import traceback
import urllib
# sys.isapidllhandle will exist when we are loaded by the IIS framework.
# In this case we redirect our output to the win32traceutil collector.
if hasattr(sys, "isapidllhandle"):
import win32traceutil
# The site we are proxying.
proxy = "http://www.python.org"
# The name of the virtual directory we install in, and redirect from.
virtualdir = "/python"
# The ISAPI extension - handles requests in our virtual dir, and sends the
# response to the client.
class Extension(threaded_extension.ThreadPoolExtension):
"Python sample Extension"
def Dispatch(self, ecb):
# Note that our ThreadPoolExtension base class will catch exceptions
# in our Dispatch method, and write the traceback to the client.
# That is perfect for this sample, so we don't catch our own.
#print 'IIS dispatching "%s"' % (ecb.GetServerVariable("URL"),)
url = ecb.GetServerVariable("URL")
if url.startswith(virtualdir):
new_url = proxy + url[len(virtualdir):]
print "Opening", new_url
fp = urllib.urlopen(new_url)
headers = fp.info()
ecb.SendResponseHeaders("200 OK", str(headers) + "\r\n", False)
ecb.WriteClient(fp.read())
ecb.DoneWithSession()
print "Returned data from '%s'!" % (new_url,)
else:
# this should never happen - we should only see requests that
# start with our virtual directory name.
print "Not proxying '%s'" % (url,)
# The ISAPI filter.
class Filter(SimpleFilter):
"Sample Python Redirector"
filter_flags = isapicon.SF_NOTIFY_PREPROC_HEADERS | \
isapicon.SF_NOTIFY_ORDER_DEFAULT
def HttpFilterProc(self, fc):
#print "Filter Dispatch"
nt = fc.NotificationType
assert nt == isapicon.SF_NOTIFY_PREPROC_HEADERS
pp = fc.GetData()
url = pp.GetHeader("url")
#print "URL is '%s'" % (url,)
prefix = virtualdir
if not url.startswith(prefix):
new_url = prefix + url
print "New proxied URL is '%s'" % (new_url,)
pp.SetHeader("url", new_url)
return isapicon.SF_STATUS_REQ_HANDLED_NOTIFICATION;
else:
print "Filter ignoring URL '%s'" % (url,)
# Some older code that handled SF_NOTIFY_URL_MAP.
#~ print "Have URL_MAP notify"
#~ urlmap = fc.GetData()
#~ print "URI is", urlmap.URL
#~ print "Path is", urlmap.PhysicalPath
#~ if urlmap.URL.startswith("/UC/"):
#~ # Find the /UC/ in the physical path, and nuke it (except
#~ # as the path is physical, it is \)
#~ p = urlmap.PhysicalPath
#~ pos = p.index("\\UC\\")
#~ p = p[:pos] + p[pos+3:]
#~ p = r"E:\src\pyisapi\webroot\PyTest\formTest.htm"
#~ print "New path is", p
#~ urlmap.PhysicalPath = p
# The entry points for the ISAPI extension.
def __FilterFactory__():
return Filter()
def __ExtensionFactory__():
return Extension()
if __name__=='__main__':
# If run from the command-line, install ourselves.
from isapi.install import *
params = ISAPIParameters()
# Setup all filters - these are global to the site.
params.Filters = [
FilterParameters(Name="PythonRedirector",
Description=Filter.__doc__),
]
# Setup the virtual directories - this is a list of directories our
# extension uses - in this case only 1.
# Each extension has a "script map" - this is the mapping of ISAPI
# extensions.
sm = [
ScriptMapParams(Extension="*", Flags=0)
]
vd = VirtualDirParameters(Name=virtualdir[1:],
Description = Extension.__doc__,
ScriptMaps = sm,
ScriptMapUpdate = "replace"
)
params.VirtualDirs = [vd]
HandleCommandLine(params)
|