123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- # This extension is used mainly for testing purposes - it is not
- # designed to be a simple sample, but instead is a hotch-potch of things
- # that attempts to exercise the framework.
-
- import os
- import stat
- import sys
-
- from isapi import isapicon
- from isapi.simple import SimpleExtension
-
- if hasattr(sys, "isapidllhandle"):
- import win32traceutil
-
- # We use the same reload support as 'advanced.py' demonstrates.
- import threading
-
- import win32con
- import win32event
- import win32file
- import winerror
-
- from isapi import InternalReloadException
-
-
- # 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 as details:
- # handle closed - thread should terminate.
- if details.winerror != 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)
-
-
- def TransmitFileCallback(ecb, hFile, cbIO, errCode):
- print("Transmit complete!")
- ecb.close()
-
-
- # The ISAPI extension - handles requests in our virtual dir, and sends the
- # response to the client.
- class Extension(SimpleExtension):
- "Python test 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
-
- if ecb.GetServerVariable("UNICODE_URL").endswith("test.py"):
- file_flags = (
- win32con.FILE_FLAG_SEQUENTIAL_SCAN | win32con.FILE_FLAG_OVERLAPPED
- )
- hfile = win32file.CreateFile(
- __file__,
- win32con.GENERIC_READ,
- 0,
- None,
- win32con.OPEN_EXISTING,
- file_flags,
- None,
- )
- flags = (
- isapicon.HSE_IO_ASYNC
- | isapicon.HSE_IO_DISCONNECT_AFTER_SEND
- | isapicon.HSE_IO_SEND_HEADERS
- )
- # We pass hFile to the callback simply as a way of keeping it alive
- # for the duration of the transmission
- try:
- ecb.TransmitFile(
- TransmitFileCallback,
- hfile,
- int(hfile),
- "200 OK",
- 0,
- 0,
- None,
- None,
- flags,
- )
- except:
- # Errors keep this source file open!
- hfile.Close()
- raise
- else:
- # default response
- ecb.SendResponseHeaders("200 OK", "Content-Type: text/html\r\n\r\n", 0)
- print("<HTML><BODY>", file=ecb)
- print("The root of this site is at", ecb.MapURLToPath("/"), file=ecb)
- print("</BODY></HTML>", file=ecb)
- 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 /PyISAPITest")
-
-
- # 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="PyISAPITest",
- 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("") # blank 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
- )
|