Uploaded Test files
This commit is contained in:
parent
f584ad9d97
commit
2e81cb7d99
16627 changed files with 2065359 additions and 102444 deletions
BIN
venv/Lib/site-packages/isapi/PyISAPI_loader.dll
Normal file
BIN
venv/Lib/site-packages/isapi/PyISAPI_loader.dll
Normal file
Binary file not shown.
7
venv/Lib/site-packages/isapi/README.txt
Normal file
7
venv/Lib/site-packages/isapi/README.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
A Python ISAPI extension. Contributed by Phillip Frantz, and is
|
||||
Copyright 2002-2003 by Blackdog Software Pty Ltd.
|
||||
|
||||
See the 'samples' directory, and particularly samples\README.txt
|
||||
|
||||
You can find documentation in the PyWin32.chm file that comes with pywin32 -
|
||||
you can open this from Pythonwin->Help, or from the start menu.
|
33
venv/Lib/site-packages/isapi/__init__.py
Normal file
33
venv/Lib/site-packages/isapi/__init__.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
# The Python ISAPI package.
|
||||
|
||||
# Exceptions thrown by the DLL framework.
|
||||
class ISAPIError(Exception):
|
||||
def __init__(self, errno, strerror = None, funcname = None):
|
||||
# named attributes match IOError etc.
|
||||
self.errno = errno
|
||||
self.strerror = strerror
|
||||
self.funcname = funcname
|
||||
Exception.__init__(self, errno, strerror, funcname)
|
||||
def __str__(self):
|
||||
if self.strerror is None:
|
||||
try:
|
||||
import win32api
|
||||
self.strerror = win32api.FormatMessage(self.errno).strip()
|
||||
except:
|
||||
self.strerror = "no error message is available"
|
||||
# str() looks like a win32api error.
|
||||
return str( (self.errno, self.strerror, self.funcname) )
|
||||
|
||||
class FilterError(ISAPIError):
|
||||
pass
|
||||
|
||||
class ExtensionError(ISAPIError):
|
||||
pass
|
||||
|
||||
# A little development aid - a filter or extension callback function can
|
||||
# raise one of these exceptions, and the handler module will be reloaded.
|
||||
# This means you can change your code without restarting IIS.
|
||||
# After a reload, your filter/extension will have the GetFilterVersion/
|
||||
# GetExtensionVersion function called, but with None as the first arg.
|
||||
class InternalReloadException(Exception):
|
||||
pass
|
BIN
venv/Lib/site-packages/isapi/__pycache__/__init__.cpython-36.pyc
Normal file
BIN
venv/Lib/site-packages/isapi/__pycache__/__init__.cpython-36.pyc
Normal file
Binary file not shown.
BIN
venv/Lib/site-packages/isapi/__pycache__/install.cpython-36.pyc
Normal file
BIN
venv/Lib/site-packages/isapi/__pycache__/install.cpython-36.pyc
Normal file
Binary file not shown.
BIN
venv/Lib/site-packages/isapi/__pycache__/isapicon.cpython-36.pyc
Normal file
BIN
venv/Lib/site-packages/isapi/__pycache__/isapicon.cpython-36.pyc
Normal file
Binary file not shown.
BIN
venv/Lib/site-packages/isapi/__pycache__/simple.cpython-36.pyc
Normal file
BIN
venv/Lib/site-packages/isapi/__pycache__/simple.cpython-36.pyc
Normal file
Binary file not shown.
Binary file not shown.
92
venv/Lib/site-packages/isapi/doc/isapi.html
Normal file
92
venv/Lib/site-packages/isapi/doc/isapi.html
Normal file
|
@ -0,0 +1,92 @@
|
|||
<!-- NOTE: This HTML is displayed inside the CHM file - hence some hrefs
|
||||
will only work in that environment
|
||||
-->
|
||||
<HTML>
|
||||
<BODY>
|
||||
<TITLE>Introduction to Python ISAPI support</TITLE>
|
||||
|
||||
<h2>Introduction to Python ISAPI support</h2>
|
||||
|
||||
<h3>See also</h3>
|
||||
<ul>
|
||||
<li><a href="/isapi_modules.html">The isapi related modules</a>
|
||||
</li>
|
||||
<li><a href="/isapi_objects.html">The isapi related objects</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p><i>Note: if you are viewing this documentation directly from disk,
|
||||
most links in this document will fail - you can also find this document in the
|
||||
CHM file that comes with pywin32, where the links will work</i>
|
||||
|
||||
<h3>Introduction</h3>
|
||||
This documents Python support for hosting ISAPI exensions and filters inside
|
||||
Microsoft Internet Information Server (IIS). It assumes a basic understanding
|
||||
of the ISAPI filter and extension mechanism.
|
||||
<p>
|
||||
In summary, to implement a filter or extension, you provide a Python module
|
||||
which defines a Filter and/or Extension class. Once your class has been
|
||||
loaded, IIS/ISAPI will, via an extension DLL, call methods on your class.
|
||||
<p>
|
||||
A filter and a class instance need only provide 3 methods - for filters they
|
||||
are called <code>GetFilterVersion</code>, <code>HttpFilterProc</code> and
|
||||
<code>TerminateFilter</code>. For extensions they
|
||||
are named <code>GetExtensionVersion</code>, <code>HttpExtensionProc</code> and
|
||||
<code>TerminateExtension</code>. If you are familiar with writing ISAPI
|
||||
extensions in C/C++, these names and their purpose will be familiar.
|
||||
<p>
|
||||
Most of the work is done in the <code>HttpFilterProc</code> and
|
||||
<code>HttpExtensionProc</code> methods. These both take a single
|
||||
parameter - an <a href="/HTTP_FILTER_CONTEXT.html">HTTP_FILTER_CONTEXT</a> and
|
||||
<a href="/EXTENSION_CONTROL_BLOCK.html">EXTENSION_CONTROL_BLOCK</a>
|
||||
object respectively.
|
||||
<p>
|
||||
In addition to these components, there is an 'isapi' package, containing
|
||||
support facilities (base-classes, exceptions, etc) which can be leveraged
|
||||
by the extension.
|
||||
|
||||
<h4>Base classes</h4>
|
||||
There are a number of base classes provided to make writing extensions a little
|
||||
simpler. Of particular note is <code>isapi.threaded_extension.ThreadPoolExtension</code>.
|
||||
This implements a thread-pool and informs IIS that the request is progressing
|
||||
in the background. Your sub-class need only provide a <code>Dispatch</code>
|
||||
method, which is called on one of the worker threads rather than the thread
|
||||
that the request came in on.
|
||||
<p>
|
||||
There is base-class for a filter in <code>isapi.simple</code>, but there is no
|
||||
equivilent threaded filter - filters work under a different model, where
|
||||
background processing is not possible.
|
||||
<h4>Samples</h4>
|
||||
Please see the <code>isapi/samples</code> directory for some sample filters
|
||||
and extensions.
|
||||
|
||||
<H3>Implementation</H3>
|
||||
A Python ISAPI filter extension consists of 2 main components:
|
||||
<UL>
|
||||
<LI>A DLL used by ISAPI to interface with Python.</LI>
|
||||
<LI>A Python script used by that DLL to implement the filter or extension
|
||||
functionality</LI>
|
||||
</UL>
|
||||
|
||||
<h4>Extension DLL</h4>
|
||||
The DLL is usually managed automatically by the isapi.install module. As the
|
||||
Python script for the extension is installed, a generic DLL provided with
|
||||
the isapi package is installed next to the script, and IIS configured to
|
||||
use this DLL.
|
||||
<p>
|
||||
The name of the DLL always has the same base name as the Python script, but
|
||||
with a leading underscore (_), and an extension of .dll. For example, the
|
||||
sample "redirector.py" will, when installed, have "_redirector.dll" created
|
||||
in the same directory.
|
||||
<p/>
|
||||
The Python script may provide 2 entry points - methods named __FilterFactory__
|
||||
and __ExtensionFactory__, both taking no arguments and returning a filter or
|
||||
extension object.
|
||||
|
||||
<h3>Using py2exe and the isapi package</h3>
|
||||
You can instruct py2exe to create a 'frozen' Python ISAPI filter/extension.
|
||||
In this case, py2exe will create a package with everything you need in one
|
||||
directory, and the Python source file embedded in the .zip file.
|
||||
<p>
|
||||
In general, you will want to build a seperate installation executable along
|
||||
with the ISAPI extension. This executable will be built from the same script.
|
||||
See the ISAPI sample in the py2exe distribution.
|
730
venv/Lib/site-packages/isapi/install.py
Normal file
730
venv/Lib/site-packages/isapi/install.py
Normal file
|
@ -0,0 +1,730 @@
|
|||
"""Installation utilities for Python ISAPI filters and extensions."""
|
||||
|
||||
# this code adapted from "Tomcat JK2 ISAPI redirector", part of Apache
|
||||
# Created July 2004, Mark Hammond.
|
||||
import sys, os, imp, shutil, stat
|
||||
import operator
|
||||
from win32com.client import GetObject, Dispatch
|
||||
from win32com.client.gencache import EnsureModule, EnsureDispatch
|
||||
import win32api
|
||||
import pythoncom
|
||||
import winerror
|
||||
import traceback
|
||||
|
||||
_APP_INPROC = 0
|
||||
_APP_OUTPROC = 1
|
||||
_APP_POOLED = 2
|
||||
_IIS_OBJECT = "IIS://LocalHost/W3SVC"
|
||||
_IIS_SERVER = "IIsWebServer"
|
||||
_IIS_WEBDIR = "IIsWebDirectory"
|
||||
_IIS_WEBVIRTUALDIR = "IIsWebVirtualDir"
|
||||
_IIS_FILTERS = "IIsFilters"
|
||||
_IIS_FILTER = "IIsFilter"
|
||||
|
||||
_DEFAULT_SERVER_NAME = "Default Web Site"
|
||||
_DEFAULT_HEADERS = "X-Powered-By: Python"
|
||||
_DEFAULT_PROTECTION = _APP_POOLED
|
||||
|
||||
# Default is for 'execute' only access - ie, only the extension
|
||||
# can be used. This can be overridden via your install script.
|
||||
_DEFAULT_ACCESS_EXECUTE = True
|
||||
_DEFAULT_ACCESS_READ = False
|
||||
_DEFAULT_ACCESS_WRITE = False
|
||||
_DEFAULT_ACCESS_SCRIPT = False
|
||||
_DEFAULT_CONTENT_INDEXED = False
|
||||
_DEFAULT_ENABLE_DIR_BROWSING = False
|
||||
_DEFAULT_ENABLE_DEFAULT_DOC = False
|
||||
|
||||
_extensions = [ext for ext, _, _ in imp.get_suffixes()]
|
||||
is_debug_build = '_d.pyd' in _extensions
|
||||
|
||||
this_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
class FilterParameters:
|
||||
Name = None
|
||||
Description = None
|
||||
Path = None
|
||||
Server = None
|
||||
# Params that control if/how AddExtensionFile is called.
|
||||
AddExtensionFile = True
|
||||
AddExtensionFile_Enabled = True
|
||||
AddExtensionFile_GroupID = None # defaults to Name
|
||||
AddExtensionFile_CanDelete = True
|
||||
AddExtensionFile_Description = None # defaults to Description.
|
||||
|
||||
def __init__(self, **kw):
|
||||
self.__dict__.update(kw)
|
||||
|
||||
class VirtualDirParameters:
|
||||
Name = None # Must be provided.
|
||||
Description = None # defaults to Name
|
||||
AppProtection = _DEFAULT_PROTECTION
|
||||
Headers = _DEFAULT_HEADERS
|
||||
Path = None # defaults to WWW root.
|
||||
Type = _IIS_WEBVIRTUALDIR
|
||||
AccessExecute = _DEFAULT_ACCESS_EXECUTE
|
||||
AccessRead = _DEFAULT_ACCESS_READ
|
||||
AccessWrite = _DEFAULT_ACCESS_WRITE
|
||||
AccessScript = _DEFAULT_ACCESS_SCRIPT
|
||||
ContentIndexed = _DEFAULT_CONTENT_INDEXED
|
||||
EnableDirBrowsing = _DEFAULT_ENABLE_DIR_BROWSING
|
||||
EnableDefaultDoc = _DEFAULT_ENABLE_DEFAULT_DOC
|
||||
DefaultDoc = None # Only set in IIS if not None
|
||||
ScriptMaps = []
|
||||
ScriptMapUpdate = "end" # can be 'start', 'end', 'replace'
|
||||
Server = None
|
||||
|
||||
def __init__(self, **kw):
|
||||
self.__dict__.update(kw)
|
||||
|
||||
def is_root(self):
|
||||
"This virtual directory is a root directory if parent and name are blank"
|
||||
parent, name = self.split_path()
|
||||
return not parent and not name
|
||||
|
||||
def split_path(self):
|
||||
return split_path(self.Name)
|
||||
|
||||
class ScriptMapParams:
|
||||
Extension = None
|
||||
Module = None
|
||||
Flags = 5
|
||||
Verbs = ""
|
||||
# Params that control if/how AddExtensionFile is called.
|
||||
AddExtensionFile = True
|
||||
AddExtensionFile_Enabled = True
|
||||
AddExtensionFile_GroupID = None # defaults to Name
|
||||
AddExtensionFile_CanDelete = True
|
||||
AddExtensionFile_Description = None # defaults to Description.
|
||||
def __init__(self, **kw):
|
||||
self.__dict__.update(kw)
|
||||
|
||||
def __str__(self):
|
||||
"Format this parameter suitable for IIS"
|
||||
items = [self.Extension, self.Module, self.Flags]
|
||||
# IIS gets upset if there is a trailing verb comma, but no verbs
|
||||
if self.Verbs:
|
||||
items.append(self.Verbs)
|
||||
items = [str(item) for item in items]
|
||||
return ','.join(items)
|
||||
|
||||
class ISAPIParameters:
|
||||
ServerName = _DEFAULT_SERVER_NAME
|
||||
# Description = None
|
||||
Filters = []
|
||||
VirtualDirs = []
|
||||
def __init__(self, **kw):
|
||||
self.__dict__.update(kw)
|
||||
|
||||
verbose = 1 # The level - 0 is quiet.
|
||||
def log(level, what):
|
||||
if verbose >= level:
|
||||
print(what)
|
||||
|
||||
# Convert an ADSI COM exception to the Win32 error code embedded in it.
|
||||
def _GetWin32ErrorCode(com_exc):
|
||||
hr = com_exc.hresult
|
||||
# If we have more details in the 'excepinfo' struct, use it.
|
||||
if com_exc.excepinfo:
|
||||
hr = com_exc.excepinfo[-1]
|
||||
if winerror.HRESULT_FACILITY(hr) != winerror.FACILITY_WIN32:
|
||||
raise
|
||||
return winerror.SCODE_CODE(hr)
|
||||
|
||||
class InstallationError(Exception): pass
|
||||
class ItemNotFound(InstallationError): pass
|
||||
class ConfigurationError(InstallationError): pass
|
||||
|
||||
def FindPath(options, server, name):
|
||||
if name.lower().startswith("iis://"):
|
||||
return name
|
||||
else:
|
||||
if name and name[0] != "/":
|
||||
name = "/"+name
|
||||
return FindWebServer(options, server)+"/ROOT"+name
|
||||
|
||||
def LocateWebServerPath(description):
|
||||
"""
|
||||
Find an IIS web server whose name or comment matches the provided
|
||||
description (case-insensitive).
|
||||
|
||||
>>> LocateWebServerPath('Default Web Site') # doctest: +SKIP
|
||||
|
||||
or
|
||||
|
||||
>>> LocateWebServerPath('1') #doctest: +SKIP
|
||||
"""
|
||||
assert len(description) >= 1, "Server name or comment is required"
|
||||
iis = GetObject(_IIS_OBJECT)
|
||||
description = description.lower().strip()
|
||||
for site in iis:
|
||||
# Name is generally a number, but no need to assume that.
|
||||
site_attributes = [getattr(site, attr, "").lower().strip()
|
||||
for attr in ("Name", "ServerComment")]
|
||||
if description in site_attributes:
|
||||
return site.AdsPath
|
||||
msg = "No web sites match the description '%s'" % description
|
||||
raise ItemNotFound(msg)
|
||||
|
||||
def GetWebServer(description = None):
|
||||
"""
|
||||
Load the web server instance (COM object) for a given instance
|
||||
or description.
|
||||
If None is specified, the default website is retrieved (indicated
|
||||
by the identifier 1.
|
||||
"""
|
||||
description = description or "1"
|
||||
path = LocateWebServerPath(description)
|
||||
server = LoadWebServer(path)
|
||||
return server
|
||||
|
||||
def LoadWebServer(path):
|
||||
try:
|
||||
server = GetObject(path)
|
||||
except pythoncom.com_error as details:
|
||||
msg = details.strerror
|
||||
if exc.excepinfo and exc.excepinfo[2]:
|
||||
msg = exc.excepinfo[2]
|
||||
msg = "WebServer %s: %s" % (path, msg)
|
||||
raise ItemNotFound(msg)
|
||||
return server
|
||||
|
||||
def FindWebServer(options, server_desc):
|
||||
"""
|
||||
Legacy function to allow options to define a .server property
|
||||
to override the other parameter. Use GetWebServer instead.
|
||||
"""
|
||||
# options takes precedence
|
||||
server_desc = options.server or server_desc
|
||||
# make sure server_desc is unicode (could be mbcs if passed in
|
||||
# sys.argv).
|
||||
if server_desc and not isinstance(server_desc, str):
|
||||
server_desc = server_desc.decode('mbcs')
|
||||
|
||||
# get the server (if server_desc is None, the default site is acquired)
|
||||
server = GetWebServer(server_desc)
|
||||
return server.adsPath
|
||||
|
||||
def split_path(path):
|
||||
"""
|
||||
Get the parent path and basename.
|
||||
|
||||
>>> split_path('/')
|
||||
['', '']
|
||||
|
||||
>>> split_path('')
|
||||
['', '']
|
||||
|
||||
>>> split_path('foo')
|
||||
['', 'foo']
|
||||
|
||||
>>> split_path('/foo')
|
||||
['', 'foo']
|
||||
|
||||
>>> split_path('/foo/bar')
|
||||
['/foo', 'bar']
|
||||
|
||||
>>> split_path('foo/bar')
|
||||
['/foo', 'bar']
|
||||
"""
|
||||
|
||||
if not path.startswith('/'): path = '/' + path
|
||||
return path.rsplit('/', 1)
|
||||
|
||||
def _CreateDirectory(iis_dir, name, params):
|
||||
# We used to go to lengths to keep an existing virtual directory
|
||||
# in place. However, in some cases the existing directories got
|
||||
# into a bad state, and an update failed to get them working.
|
||||
# So we nuke it first. If this is a problem, we could consider adding
|
||||
# a --keep-existing option.
|
||||
try:
|
||||
# Also seen the Class change to a generic IISObject - so nuke
|
||||
# *any* existing object, regardless of Class
|
||||
assert name.strip("/"), "mustn't delete the root!"
|
||||
iis_dir.Delete('', name)
|
||||
log(2, "Deleted old directory '%s'" % (name,))
|
||||
except pythoncom.com_error:
|
||||
pass
|
||||
|
||||
newDir = iis_dir.Create(params.Type, name)
|
||||
log(2, "Creating new directory '%s' in %s..." % (name,iis_dir.Name))
|
||||
|
||||
friendly = params.Description or params.Name
|
||||
newDir.AppFriendlyName = friendly
|
||||
|
||||
# Note that the new directory won't be visible in the IIS UI
|
||||
# unless the directory exists on the filesystem.
|
||||
try:
|
||||
path = params.Path or iis_dir.Path
|
||||
newDir.Path = path
|
||||
except AttributeError:
|
||||
# If params.Type is IIS_WEBDIRECTORY, an exception is thrown
|
||||
pass
|
||||
newDir.AppCreate2(params.AppProtection)
|
||||
# XXX - note that these Headers only work in IIS6 and earlier. IIS7
|
||||
# only supports them on the w3svc node - not even on individial sites,
|
||||
# let alone individual extensions in the site!
|
||||
if params.Headers:
|
||||
newDir.HttpCustomHeaders = params.Headers
|
||||
|
||||
log(2, "Setting directory options...")
|
||||
newDir.AccessExecute = params.AccessExecute
|
||||
newDir.AccessRead = params.AccessRead
|
||||
newDir.AccessWrite = params.AccessWrite
|
||||
newDir.AccessScript = params.AccessScript
|
||||
newDir.ContentIndexed = params.ContentIndexed
|
||||
newDir.EnableDirBrowsing = params.EnableDirBrowsing
|
||||
newDir.EnableDefaultDoc = params.EnableDefaultDoc
|
||||
if params.DefaultDoc is not None:
|
||||
newDir.DefaultDoc = params.DefaultDoc
|
||||
newDir.SetInfo()
|
||||
return newDir
|
||||
|
||||
|
||||
def CreateDirectory(params, options):
|
||||
_CallHook(params, "PreInstall", options)
|
||||
if not params.Name:
|
||||
raise ConfigurationError("No Name param")
|
||||
parent, name = params.split_path()
|
||||
target_dir = GetObject(FindPath(options, params.Server, parent))
|
||||
|
||||
if not params.is_root():
|
||||
target_dir = _CreateDirectory(target_dir, name, params)
|
||||
|
||||
AssignScriptMaps(params.ScriptMaps, target_dir, params.ScriptMapUpdate)
|
||||
|
||||
_CallHook(params, "PostInstall", options, target_dir)
|
||||
log(1, "Configured Virtual Directory: %s" % (params.Name,))
|
||||
return target_dir
|
||||
|
||||
def AssignScriptMaps(script_maps, target, update='replace'):
|
||||
"""Updates IIS with the supplied script map information.
|
||||
|
||||
script_maps is a list of ScriptMapParameter objects
|
||||
|
||||
target is an IIS Virtual Directory to assign the script maps to
|
||||
|
||||
update is a string indicating how to update the maps, one of ('start',
|
||||
'end', or 'replace')
|
||||
"""
|
||||
# determine which function to use to assign script maps
|
||||
script_map_func = '_AssignScriptMaps' + update.capitalize()
|
||||
try:
|
||||
script_map_func = eval(script_map_func)
|
||||
except NameError:
|
||||
msg = "Unknown ScriptMapUpdate option '%s'" % update
|
||||
raise ConfigurationError(msg)
|
||||
# use the str method to format the script maps for IIS
|
||||
script_maps = [str(s) for s in script_maps]
|
||||
# call the correct function
|
||||
script_map_func(target, script_maps)
|
||||
target.SetInfo()
|
||||
|
||||
def get_unique_items(sequence, reference):
|
||||
"Return items in sequence that can't be found in reference."
|
||||
return tuple([item for item in sequence if item not in reference])
|
||||
|
||||
def _AssignScriptMapsReplace(target, script_maps):
|
||||
target.ScriptMaps = script_maps
|
||||
|
||||
def _AssignScriptMapsEnd(target, script_maps):
|
||||
unique_new_maps = get_unique_items(script_maps, target.ScriptMaps)
|
||||
target.ScriptMaps = target.ScriptMaps + unique_new_maps
|
||||
|
||||
def _AssignScriptMapsStart(target, script_maps):
|
||||
unique_new_maps = get_unique_items(script_maps, target.ScriptMaps)
|
||||
target.ScriptMaps = unique_new_maps + target.ScriptMaps
|
||||
|
||||
def CreateISAPIFilter(filterParams, options):
|
||||
server = FindWebServer(options, filterParams.Server)
|
||||
_CallHook(filterParams, "PreInstall", options)
|
||||
try:
|
||||
filters = GetObject(server+"/Filters")
|
||||
except pythoncom.com_error as exc:
|
||||
# Brand new sites don't have the '/Filters' collection - create it.
|
||||
# Any errors other than 'not found' we shouldn't ignore.
|
||||
if winerror.HRESULT_FACILITY(exc.hresult) != winerror.FACILITY_WIN32 or \
|
||||
winerror.HRESULT_CODE(exc.hresult) != winerror.ERROR_PATH_NOT_FOUND:
|
||||
raise
|
||||
server_ob = GetObject(server)
|
||||
filters = server_ob.Create(_IIS_FILTERS, "Filters")
|
||||
filters.FilterLoadOrder = ""
|
||||
filters.SetInfo()
|
||||
|
||||
# As for VirtualDir, delete an existing one.
|
||||
assert filterParams.Name.strip("/"), "mustn't delete the root!"
|
||||
try:
|
||||
filters.Delete(_IIS_FILTER, filterParams.Name)
|
||||
log(2, "Deleted old filter '%s'" % (filterParams.Name,))
|
||||
except pythoncom.com_error:
|
||||
pass
|
||||
newFilter = filters.Create(_IIS_FILTER, filterParams.Name)
|
||||
log(2, "Created new ISAPI filter...")
|
||||
assert os.path.isfile(filterParams.Path)
|
||||
newFilter.FilterPath = filterParams.Path
|
||||
newFilter.FilterDescription = filterParams.Description
|
||||
newFilter.SetInfo()
|
||||
load_order = [b.strip() for b in filters.FilterLoadOrder.split(",") if b]
|
||||
if filterParams.Name not in load_order:
|
||||
load_order.append(filterParams.Name)
|
||||
filters.FilterLoadOrder = ",".join(load_order)
|
||||
filters.SetInfo()
|
||||
_CallHook(filterParams, "PostInstall", options, newFilter)
|
||||
log (1, "Configured Filter: %s" % (filterParams.Name,))
|
||||
return newFilter
|
||||
|
||||
def DeleteISAPIFilter(filterParams, options):
|
||||
_CallHook(filterParams, "PreRemove", options)
|
||||
server = FindWebServer(options, filterParams.Server)
|
||||
ob_path = server+"/Filters"
|
||||
try:
|
||||
filters = GetObject(ob_path)
|
||||
except pythoncom.com_error as details:
|
||||
# failure to open the filters just means a totally clean IIS install
|
||||
# (IIS5 at least has no 'Filters' key when freshly installed).
|
||||
log(2, "ISAPI filter path '%s' did not exist." % (ob_path,))
|
||||
return
|
||||
try:
|
||||
assert filterParams.Name.strip("/"), "mustn't delete the root!"
|
||||
filters.Delete(_IIS_FILTER, filterParams.Name)
|
||||
log(2, "Deleted ISAPI filter '%s'" % (filterParams.Name,))
|
||||
except pythoncom.com_error as details:
|
||||
rc = _GetWin32ErrorCode(details)
|
||||
if rc != winerror.ERROR_PATH_NOT_FOUND:
|
||||
raise
|
||||
log(2, "ISAPI filter '%s' did not exist." % (filterParams.Name,))
|
||||
# Remove from the load order
|
||||
load_order = [b.strip() for b in filters.FilterLoadOrder.split(",") if b]
|
||||
if filterParams.Name in load_order:
|
||||
load_order.remove(filterParams.Name)
|
||||
filters.FilterLoadOrder = ",".join(load_order)
|
||||
filters.SetInfo()
|
||||
_CallHook(filterParams, "PostRemove", options)
|
||||
log (1, "Deleted Filter: %s" % (filterParams.Name,))
|
||||
|
||||
def _AddExtensionFile(module, def_groupid, def_desc, params, options):
|
||||
group_id = params.AddExtensionFile_GroupID or def_groupid
|
||||
desc = params.AddExtensionFile_Description or def_desc
|
||||
try:
|
||||
ob = GetObject(_IIS_OBJECT)
|
||||
ob.AddExtensionFile(module,
|
||||
params.AddExtensionFile_Enabled,
|
||||
group_id,
|
||||
params.AddExtensionFile_CanDelete,
|
||||
desc)
|
||||
log(2, "Added extension file '%s' (%s)" % (module, desc))
|
||||
except (pythoncom.com_error, AttributeError) as details:
|
||||
# IIS5 always fails. Probably should upgrade this to
|
||||
# complain more loudly if IIS6 fails.
|
||||
log(2, "Failed to add extension file '%s': %s" % (module, details))
|
||||
|
||||
def AddExtensionFiles(params, options):
|
||||
"""Register the modules used by the filters/extensions as a trusted
|
||||
'extension module' - required by the default IIS6 security settings."""
|
||||
# Add each module only once.
|
||||
added = {}
|
||||
for vd in params.VirtualDirs:
|
||||
for smp in vd.ScriptMaps:
|
||||
if smp.Module not in added and smp.AddExtensionFile:
|
||||
_AddExtensionFile(smp.Module, vd.Name, vd.Description, smp,
|
||||
options)
|
||||
added[smp.Module] = True
|
||||
|
||||
for fd in params.Filters:
|
||||
if fd.Path not in added and fd.AddExtensionFile:
|
||||
_AddExtensionFile(fd.Path, fd.Name, fd.Description, fd, options)
|
||||
added[fd.Path] = True
|
||||
|
||||
def _DeleteExtensionFileRecord(module, options):
|
||||
try:
|
||||
ob = GetObject(_IIS_OBJECT)
|
||||
ob.DeleteExtensionFileRecord(module)
|
||||
log(2, "Deleted extension file record for '%s'" % module)
|
||||
except (pythoncom.com_error, AttributeError) as details:
|
||||
log(2, "Failed to remove extension file '%s': %s" % (module, details))
|
||||
|
||||
def DeleteExtensionFileRecords(params, options):
|
||||
deleted = {} # only remove each .dll once.
|
||||
for vd in params.VirtualDirs:
|
||||
for smp in vd.ScriptMaps:
|
||||
if smp.Module not in deleted and smp.AddExtensionFile:
|
||||
_DeleteExtensionFileRecord(smp.Module, options)
|
||||
deleted[smp.Module] = True
|
||||
|
||||
for filter_def in params.Filters:
|
||||
if filter_def.Path not in deleted and filter_def.AddExtensionFile:
|
||||
_DeleteExtensionFileRecord(filter_def.Path, options)
|
||||
deleted[filter_def.Path] = True
|
||||
|
||||
def CheckLoaderModule(dll_name):
|
||||
suffix = ""
|
||||
if is_debug_build: suffix = "_d"
|
||||
template = os.path.join(this_dir,
|
||||
"PyISAPI_loader" + suffix + ".dll")
|
||||
if not os.path.isfile(template):
|
||||
raise ConfigurationError(
|
||||
"Template loader '%s' does not exist" % (template,))
|
||||
# We can't do a simple "is newer" check, as the DLL is specific to the
|
||||
# Python version. So we check the date-time and size are identical,
|
||||
# and skip the copy in that case.
|
||||
src_stat = os.stat(template)
|
||||
try:
|
||||
dest_stat = os.stat(dll_name)
|
||||
except os.error:
|
||||
same = 0
|
||||
else:
|
||||
same = src_stat[stat.ST_SIZE]==dest_stat[stat.ST_SIZE] and \
|
||||
src_stat[stat.ST_MTIME]==dest_stat[stat.ST_MTIME]
|
||||
if not same:
|
||||
log(2, "Updating %s->%s" % (template, dll_name))
|
||||
shutil.copyfile(template, dll_name)
|
||||
shutil.copystat(template, dll_name)
|
||||
else:
|
||||
log(2, "%s is up to date." % (dll_name,))
|
||||
|
||||
def _CallHook(ob, hook_name, options, *extra_args):
|
||||
func = getattr(ob, hook_name, None)
|
||||
if func is not None:
|
||||
args = (ob,options) + extra_args
|
||||
func(*args)
|
||||
|
||||
def Install(params, options):
|
||||
_CallHook(params, "PreInstall", options)
|
||||
for vd in params.VirtualDirs:
|
||||
CreateDirectory(vd, options)
|
||||
|
||||
for filter_def in params.Filters:
|
||||
CreateISAPIFilter(filter_def, options)
|
||||
|
||||
AddExtensionFiles(params, options)
|
||||
|
||||
_CallHook(params, "PostInstall", options)
|
||||
|
||||
def RemoveDirectory(params, options):
|
||||
if params.is_root():
|
||||
return
|
||||
try:
|
||||
directory = GetObject(FindPath(options, params.Server, params.Name))
|
||||
except pythoncom.com_error as details:
|
||||
rc = _GetWin32ErrorCode(details)
|
||||
if rc != winerror.ERROR_PATH_NOT_FOUND:
|
||||
raise
|
||||
log(2, "VirtualDirectory '%s' did not exist" % params.Name)
|
||||
directory = None
|
||||
if directory is not None:
|
||||
# Be robust should IIS get upset about unloading.
|
||||
try:
|
||||
directory.AppUnLoad()
|
||||
except:
|
||||
exc_val = sys.exc_info()[1]
|
||||
log(2, "AppUnLoad() for %s failed: %s" % (params.Name, exc_val))
|
||||
# Continue trying to delete it.
|
||||
try:
|
||||
parent = GetObject(directory.Parent)
|
||||
parent.Delete(directory.Class, directory.Name)
|
||||
log (1, "Deleted Virtual Directory: %s" % (params.Name,))
|
||||
except:
|
||||
exc_val = sys.exc_info()[1]
|
||||
log(1, "Failed to remove directory %s: %s" % (params.Name, exc_val))
|
||||
|
||||
def RemoveScriptMaps(vd_params, options):
|
||||
"Remove script maps from the already installed virtual directory"
|
||||
parent, name = vd_params.split_path()
|
||||
target_dir = GetObject(FindPath(options, vd_params.Server, parent))
|
||||
installed_maps = list(target_dir.ScriptMaps)
|
||||
for _map in map(str, vd_params.ScriptMaps):
|
||||
if _map in installed_maps:
|
||||
installed_maps.remove(_map)
|
||||
target_dir.ScriptMaps = installed_maps
|
||||
target_dir.SetInfo()
|
||||
|
||||
def Uninstall(params, options):
|
||||
_CallHook(params, "PreRemove", options)
|
||||
|
||||
DeleteExtensionFileRecords(params, options)
|
||||
|
||||
for vd in params.VirtualDirs:
|
||||
_CallHook(vd, "PreRemove", options)
|
||||
|
||||
RemoveDirectory(vd, options)
|
||||
if vd.is_root():
|
||||
# if this is installed to the root virtual directory, we can't delete it
|
||||
# so remove the script maps.
|
||||
RemoveScriptMaps(vd, options)
|
||||
|
||||
_CallHook(vd, "PostRemove", options)
|
||||
|
||||
for filter_def in params.Filters:
|
||||
DeleteISAPIFilter(filter_def, options)
|
||||
_CallHook(params, "PostRemove", options)
|
||||
|
||||
# Patch up any missing module names in the params, replacing them with
|
||||
# the DLL name that hosts this extension/filter.
|
||||
def _PatchParamsModule(params, dll_name, file_must_exist = True):
|
||||
if file_must_exist:
|
||||
if not os.path.isfile(dll_name):
|
||||
raise ConfigurationError("%s does not exist" % (dll_name,))
|
||||
|
||||
# Patch up all references to the DLL.
|
||||
for f in params.Filters:
|
||||
if f.Path is None: f.Path = dll_name
|
||||
for d in params.VirtualDirs:
|
||||
for sm in d.ScriptMaps:
|
||||
if sm.Module is None: sm.Module = dll_name
|
||||
|
||||
def GetLoaderModuleName(mod_name, check_module = None):
|
||||
# find the name of the DLL hosting us.
|
||||
# By default, this is "_{module_base_name}.dll"
|
||||
if hasattr(sys, "frozen"):
|
||||
# What to do? The .dll knows its name, but this is likely to be
|
||||
# executed via a .exe, which does not know.
|
||||
base, ext = os.path.splitext(mod_name)
|
||||
path, base = os.path.split(base)
|
||||
# handle the common case of 'foo.exe'/'foow.exe'
|
||||
if base.endswith('w'):
|
||||
base = base[:-1]
|
||||
# For py2exe, we have '_foo.dll' as the standard pyisapi loader - but
|
||||
# 'foo.dll' is what we use (it just delegates).
|
||||
# So no leading '_' on the installed name.
|
||||
dll_name = os.path.abspath(os.path.join(path, base + ".dll"))
|
||||
else:
|
||||
base, ext = os.path.splitext(mod_name)
|
||||
path, base = os.path.split(base)
|
||||
dll_name = os.path.abspath(os.path.join(path, "_" + base + ".dll"))
|
||||
# Check we actually have it.
|
||||
if check_module is None: check_module = not hasattr(sys, "frozen")
|
||||
if check_module:
|
||||
CheckLoaderModule(dll_name)
|
||||
return dll_name
|
||||
|
||||
# Note the 'log' params to these 'builtin' args - old versions of pywin32
|
||||
# didn't log at all in this function (by intent; anyone calling this was
|
||||
# responsible). So existing code that calls this function with the old
|
||||
# signature (ie, without a 'log' param) still gets the same behaviour as
|
||||
# before...
|
||||
|
||||
def InstallModule(conf_module_name, params, options, log=lambda *args:None):
|
||||
"Install the extension"
|
||||
if not hasattr(sys, "frozen"):
|
||||
conf_module_name = os.path.abspath(conf_module_name)
|
||||
if not os.path.isfile(conf_module_name):
|
||||
raise ConfigurationError("%s does not exist" % (conf_module_name,))
|
||||
|
||||
loader_dll = GetLoaderModuleName(conf_module_name)
|
||||
_PatchParamsModule(params, loader_dll)
|
||||
Install(params, options)
|
||||
log(1, "Installation complete.")
|
||||
|
||||
def UninstallModule(conf_module_name, params, options, log=lambda *args:None):
|
||||
"Remove the extension"
|
||||
loader_dll = GetLoaderModuleName(conf_module_name, False)
|
||||
_PatchParamsModule(params, loader_dll, False)
|
||||
Uninstall(params, options)
|
||||
log(1, "Uninstallation complete.")
|
||||
|
||||
standard_arguments = {
|
||||
"install" : InstallModule,
|
||||
"remove" : UninstallModule,
|
||||
}
|
||||
|
||||
def build_usage(handler_map):
|
||||
docstrings = [handler.__doc__ for handler in handler_map.values()]
|
||||
all_args = dict(zip(iter(handler_map.keys()), docstrings))
|
||||
arg_names = "|".join(iter(all_args.keys()))
|
||||
usage_string = "%prog [options] [" + arg_names + "]\n"
|
||||
usage_string += "commands:\n"
|
||||
for arg, desc in all_args.items():
|
||||
usage_string += " %-10s: %s" % (arg, desc) + "\n"
|
||||
return usage_string[:-1]
|
||||
|
||||
def MergeStandardOptions(options, params):
|
||||
"""
|
||||
Take an options object generated by the command line and merge
|
||||
the values into the IISParameters object.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
# We support 2 ways of extending our command-line/install support.
|
||||
# * Many of the installation items allow you to specify "PreInstall",
|
||||
# "PostInstall", "PreRemove" and "PostRemove" hooks
|
||||
# All hooks are called with the 'params' object being operated on, and
|
||||
# the 'optparser' options for this session (ie, the command-line options)
|
||||
# PostInstall for VirtualDirectories and Filters both have an additional
|
||||
# param - the ADSI object just created.
|
||||
# * You can pass your own option parser for us to use, and/or define a map
|
||||
# with your own custom arg handlers. It is a map of 'arg'->function.
|
||||
# The function is called with (options, log_fn, arg). The function's
|
||||
# docstring is used in the usage output.
|
||||
def HandleCommandLine(params, argv=None, conf_module_name = None,
|
||||
default_arg = "install",
|
||||
opt_parser = None, custom_arg_handlers = {}):
|
||||
"""Perform installation or removal of an ISAPI filter or extension.
|
||||
|
||||
This module handles standard command-line options and configuration
|
||||
information, and installs, removes or updates the configuration of an
|
||||
ISAPI filter or extension.
|
||||
|
||||
You must pass your configuration information in params - all other
|
||||
arguments are optional, and allow you to configure the installation
|
||||
process.
|
||||
"""
|
||||
global verbose
|
||||
from optparse import OptionParser
|
||||
|
||||
argv = argv or sys.argv
|
||||
if not conf_module_name:
|
||||
conf_module_name = sys.argv[0]
|
||||
# convert to a long name so that if we were somehow registered with
|
||||
# the "short" version but unregistered with the "long" version we
|
||||
# still work (that will depend on exactly how the installer was
|
||||
# started)
|
||||
try:
|
||||
conf_module_name = win32api.GetLongPathName(conf_module_name)
|
||||
except win32api.error as exc:
|
||||
log(2, "Couldn't determine the long name for %r: %s" %
|
||||
(conf_module_name, exc))
|
||||
|
||||
if opt_parser is None:
|
||||
# Build our own parser.
|
||||
parser = OptionParser(usage='')
|
||||
else:
|
||||
# The caller is providing their own filter, presumably with their
|
||||
# own options all setup.
|
||||
parser = opt_parser
|
||||
|
||||
# build a usage string if we don't have one.
|
||||
if not parser.get_usage():
|
||||
all_handlers = standard_arguments.copy()
|
||||
all_handlers.update(custom_arg_handlers)
|
||||
parser.set_usage(build_usage(all_handlers))
|
||||
|
||||
# allow the user to use uninstall as a synonym for remove if it wasn't
|
||||
# defined by the custom arg handlers.
|
||||
all_handlers.setdefault('uninstall', all_handlers['remove'])
|
||||
|
||||
parser.add_option("-q", "--quiet",
|
||||
action="store_false", dest="verbose", default=True,
|
||||
help="don't print status messages to stdout")
|
||||
parser.add_option("-v", "--verbosity", action="count",
|
||||
dest="verbose", default=1,
|
||||
help="increase the verbosity of status messages")
|
||||
parser.add_option("", "--server", action="store",
|
||||
help="Specifies the IIS server to install/uninstall on." \
|
||||
" Default is '%s/1'" % (_IIS_OBJECT,))
|
||||
|
||||
(options, args) = parser.parse_args(argv[1:])
|
||||
MergeStandardOptions(options, params)
|
||||
verbose = options.verbose
|
||||
if not args:
|
||||
args = [default_arg]
|
||||
try:
|
||||
for arg in args:
|
||||
handler = all_handlers[arg]
|
||||
handler(conf_module_name, params, options, log)
|
||||
except (ItemNotFound, InstallationError) as details:
|
||||
if options.verbose > 1:
|
||||
traceback.print_exc()
|
||||
print("%s: %s" % (details.__class__.__name__, details))
|
||||
except KeyError:
|
||||
parser.error("Invalid arg '%s'" % arg)
|
120
venv/Lib/site-packages/isapi/isapicon.py
Normal file
120
venv/Lib/site-packages/isapi/isapicon.py
Normal file
|
@ -0,0 +1,120 @@
|
|||
"""Constants needed by ISAPI filters and extensions."""
|
||||
# ======================================================================
|
||||
# Copyright 2002-2003 by Blackdog Software Pty Ltd.
|
||||
#
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software and
|
||||
# its documentation for any purpose and without fee is hereby
|
||||
# granted, provided that the above copyright notice appear in all
|
||||
# copies and that both that copyright notice and this permission
|
||||
# notice appear in supporting documentation, and that the name of
|
||||
# Blackdog Software not be used in advertising or publicity pertaining to
|
||||
# distribution of the software without specific, written prior
|
||||
# permission.
|
||||
#
|
||||
# BLACKDOG SOFTWARE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
|
||||
# NO EVENT SHALL BLACKDOG SOFTWARE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
# ======================================================================
|
||||
|
||||
# HTTP reply codes
|
||||
|
||||
HTTP_CONTINUE = 100
|
||||
HTTP_SWITCHING_PROTOCOLS = 101
|
||||
HTTP_PROCESSING = 102
|
||||
HTTP_OK = 200
|
||||
HTTP_CREATED = 201
|
||||
HTTP_ACCEPTED = 202
|
||||
HTTP_NON_AUTHORITATIVE = 203
|
||||
HTTP_NO_CONTENT = 204
|
||||
HTTP_RESET_CONTENT = 205
|
||||
HTTP_PARTIAL_CONTENT = 206
|
||||
HTTP_MULTI_STATUS = 207
|
||||
HTTP_MULTIPLE_CHOICES = 300
|
||||
HTTP_MOVED_PERMANENTLY = 301
|
||||
HTTP_MOVED_TEMPORARILY = 302
|
||||
HTTP_SEE_OTHER = 303
|
||||
HTTP_NOT_MODIFIED = 304
|
||||
HTTP_USE_PROXY = 305
|
||||
HTTP_TEMPORARY_REDIRECT = 307
|
||||
HTTP_BAD_REQUEST = 400
|
||||
HTTP_UNAUTHORIZED = 401
|
||||
HTTP_PAYMENT_REQUIRED = 402
|
||||
HTTP_FORBIDDEN = 403
|
||||
HTTP_NOT_FOUND = 404
|
||||
HTTP_METHOD_NOT_ALLOWED = 405
|
||||
HTTP_NOT_ACCEPTABLE = 406
|
||||
HTTP_PROXY_AUTHENTICATION_REQUIRED= 407
|
||||
HTTP_REQUEST_TIME_OUT = 408
|
||||
HTTP_CONFLICT = 409
|
||||
HTTP_GONE = 410
|
||||
HTTP_LENGTH_REQUIRED = 411
|
||||
HTTP_PRECONDITION_FAILED = 412
|
||||
HTTP_REQUEST_ENTITY_TOO_LARGE = 413
|
||||
HTTP_REQUEST_URI_TOO_LARGE = 414
|
||||
HTTP_UNSUPPORTED_MEDIA_TYPE = 415
|
||||
HTTP_RANGE_NOT_SATISFIABLE = 416
|
||||
HTTP_EXPECTATION_FAILED = 417
|
||||
HTTP_UNPROCESSABLE_ENTITY = 422
|
||||
HTTP_INTERNAL_SERVER_ERROR = 500
|
||||
HTTP_NOT_IMPLEMENTED = 501
|
||||
HTTP_BAD_GATEWAY = 502
|
||||
HTTP_SERVICE_UNAVAILABLE = 503
|
||||
HTTP_GATEWAY_TIME_OUT = 504
|
||||
HTTP_VERSION_NOT_SUPPORTED = 505
|
||||
HTTP_VARIANT_ALSO_VARIES = 506
|
||||
|
||||
HSE_STATUS_SUCCESS = 1
|
||||
HSE_STATUS_SUCCESS_AND_KEEP_CONN = 2
|
||||
HSE_STATUS_PENDING = 3
|
||||
HSE_STATUS_ERROR = 4
|
||||
|
||||
SF_NOTIFY_SECURE_PORT = 0x00000001
|
||||
SF_NOTIFY_NONSECURE_PORT = 0x00000002
|
||||
SF_NOTIFY_READ_RAW_DATA = 0x00008000
|
||||
SF_NOTIFY_PREPROC_HEADERS = 0x00004000
|
||||
SF_NOTIFY_AUTHENTICATION = 0x00002000
|
||||
SF_NOTIFY_URL_MAP = 0x00001000
|
||||
SF_NOTIFY_ACCESS_DENIED = 0x00000800
|
||||
SF_NOTIFY_SEND_RESPONSE = 0x00000040
|
||||
SF_NOTIFY_SEND_RAW_DATA = 0x00000400
|
||||
SF_NOTIFY_LOG = 0x00000200
|
||||
SF_NOTIFY_END_OF_REQUEST = 0x00000080
|
||||
SF_NOTIFY_END_OF_NET_SESSION = 0x00000100
|
||||
|
||||
SF_NOTIFY_ORDER_HIGH = 0x00080000
|
||||
SF_NOTIFY_ORDER_MEDIUM = 0x00040000
|
||||
SF_NOTIFY_ORDER_LOW = 0x00020000
|
||||
SF_NOTIFY_ORDER_DEFAULT = SF_NOTIFY_ORDER_LOW
|
||||
|
||||
SF_NOTIFY_ORDER_MASK = (SF_NOTIFY_ORDER_HIGH | \
|
||||
SF_NOTIFY_ORDER_MEDIUM | \
|
||||
SF_NOTIFY_ORDER_LOW)
|
||||
|
||||
SF_STATUS_REQ_FINISHED = 134217728 # 0x8000000
|
||||
SF_STATUS_REQ_FINISHED_KEEP_CONN = 134217728 + 1
|
||||
SF_STATUS_REQ_NEXT_NOTIFICATION = 134217728 + 2
|
||||
SF_STATUS_REQ_HANDLED_NOTIFICATION = 134217728 + 3
|
||||
SF_STATUS_REQ_ERROR = 134217728 + 4
|
||||
SF_STATUS_REQ_READ_NEXT = 134217728 + 5
|
||||
|
||||
HSE_IO_SYNC = 0x00000001 # for WriteClient
|
||||
HSE_IO_ASYNC = 0x00000002 # for WriteClient/TF/EU
|
||||
HSE_IO_DISCONNECT_AFTER_SEND = 0x00000004 # for TF
|
||||
HSE_IO_SEND_HEADERS = 0x00000008 # for TF
|
||||
HSE_IO_NODELAY = 0x00001000 # turn off nagling
|
||||
# These two are only used by VectorSend
|
||||
HSE_IO_FINAL_SEND = 0x00000010
|
||||
HSE_IO_CACHE_RESPONSE = 0x00000020
|
||||
|
||||
HSE_EXEC_URL_NO_HEADERS = 0x02
|
||||
HSE_EXEC_URL_IGNORE_CURRENT_INTERCEPTOR = 0x04
|
||||
HSE_EXEC_URL_IGNORE_VALIDATION_AND_RANGE = 0x10
|
||||
HSE_EXEC_URL_DISABLE_CUSTOM_ERROR = 0x20
|
||||
HSE_EXEC_URL_SSI_CMD = 0x40
|
||||
HSE_EXEC_URL_HTTP_CACHE_ELIGIBLE = 0x80
|
20
venv/Lib/site-packages/isapi/samples/README.txt
Normal file
20
venv/Lib/site-packages/isapi/samples/README.txt
Normal file
|
@ -0,0 +1,20 @@
|
|||
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.
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
196
venv/Lib/site-packages/isapi/samples/advanced.py
Normal file
196
venv/Lib/site-packages/isapi/samples/advanced.py
Normal file
|
@ -0,0 +1,196 @@
|
|||
# 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.
|
||||
# * Using a query string - any part of the URL after a '?' is assumed to
|
||||
# be "variable names" separated by '&' - we will print the values of
|
||||
# these server variables.
|
||||
# * If the tail portion of the URL is "ReportUnhealthy", IIS will be
|
||||
# notified we are unhealthy via a HSE_REQ_REPORT_UNHEALTHY request.
|
||||
# Whether this is acted upon depends on if the IIS health-checking
|
||||
# tools are installed, but you should always see the reason written
|
||||
# to the Windows event log - see the IIS documentation for more.
|
||||
|
||||
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 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)
|
||||
|
||||
# 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
|
||||
|
||||
url = ecb.GetServerVariable("UNICODE_URL")
|
||||
if url.endswith("ReportUnhealthy"):
|
||||
ecb.ReportUnhealthy("I'm a little sick")
|
||||
|
||||
ecb.SendResponseHeaders("200 OK", "Content-Type: text/html\r\n\r\n", 0)
|
||||
print("<HTML><BODY>", file=ecb)
|
||||
|
||||
qs = ecb.GetServerVariable("QUERY_STRING")
|
||||
if qs:
|
||||
queries = qs.split("&")
|
||||
print("<PRE>", file=ecb)
|
||||
for q in queries:
|
||||
val = ecb.GetServerVariable(q, '<no such variable>')
|
||||
print("%s=%r" % (q, val), file=ecb)
|
||||
print("</PRE><P/>", file=ecb)
|
||||
|
||||
print("This module has been imported", file=ecb)
|
||||
print("%d times" % (reload_counter,), 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 /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('') # 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)
|
109
venv/Lib/site-packages/isapi/samples/redirector.py
Normal file
109
venv/Lib/site-packages/isapi/samples/redirector.py
Normal file
|
@ -0,0 +1,109 @@
|
|||
# This is a sample ISAPI 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 is the simplest possible redirector (or proxy) we can write. The
|
||||
# extension installs with a mask of '*' in the root of the site.
|
||||
# As an added bonus though, we optionally show how, on IIS6 and later, we
|
||||
# can use HSE_ERQ_EXEC_URL to ignore certain requests - in IIS5 and earlier
|
||||
# we can only do this with an ISAPI filter - see redirector_with_filter for
|
||||
# an example. If this sample is run on IIS5 or earlier it simply ignores
|
||||
# any excludes.
|
||||
|
||||
from isapi import isapicon, threaded_extension
|
||||
import sys
|
||||
import traceback
|
||||
try:
|
||||
from urllib.request import urlopen
|
||||
except ImportError:
|
||||
# py3k spelling...
|
||||
from urllib.request import urlopen
|
||||
import win32api
|
||||
|
||||
# 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"
|
||||
|
||||
# Urls we exclude (ie, allow IIS to handle itself) - all are lowered,
|
||||
# and these entries exist by default on Vista...
|
||||
excludes = ["/iisstart.htm", "/welcome.png"]
|
||||
|
||||
# An "io completion" function, called when ecb.ExecURL completes...
|
||||
def io_callback(ecb, url, cbIO, errcode):
|
||||
# Get the status of our ExecURL
|
||||
httpstatus, substatus, win32 = ecb.GetExecURLStatus()
|
||||
print("ExecURL of %r finished with http status %d.%d, win32 status %d (%s)" % (
|
||||
url, httpstatus, substatus, win32, win32api.FormatMessage(win32).strip()))
|
||||
# nothing more to do!
|
||||
ecb.DoneWithSession()
|
||||
|
||||
# The ISAPI extension - handles all requests in the site.
|
||||
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").decode("ascii")
|
||||
for exclude in excludes:
|
||||
if url.lower().startswith(exclude):
|
||||
print("excluding %s" % url)
|
||||
if ecb.Version < 0x60000:
|
||||
print("(but this is IIS5 or earlier - can't do 'excludes')")
|
||||
else:
|
||||
ecb.IOCompletion(io_callback, url)
|
||||
ecb.ExecURL(None, None, None, None, None, isapicon.HSE_EXEC_URL_IGNORE_CURRENT_INTERCEPTOR)
|
||||
return isapicon.HSE_STATUS_PENDING
|
||||
|
||||
new_url = proxy + url
|
||||
print("Opening %s" % new_url)
|
||||
fp = urlopen(new_url)
|
||||
headers = fp.info()
|
||||
# subtle py3k breakage: in py3k, str(headers) has normalized \r\n
|
||||
# back to \n and also stuck an extra \n term. py2k leaves the
|
||||
# \r\n from the server in tact and finishes with a single term.
|
||||
if sys.version_info < (3,0):
|
||||
header_text = str(headers) + "\r\n"
|
||||
else:
|
||||
# take *all* trailing \n off, replace remaining with
|
||||
# \r\n, then add the 2 trailing \r\n.
|
||||
header_text = str(headers).rstrip('\n').replace('\n', '\r\n') + '\r\n\r\n'
|
||||
ecb.SendResponseHeaders("200 OK", header_text, False)
|
||||
ecb.WriteClient(fp.read())
|
||||
ecb.DoneWithSession()
|
||||
print("Returned data from '%s'" % (new_url,))
|
||||
return isapicon.HSE_STATUS_SUCCESS
|
||||
|
||||
# The entry points for the ISAPI extension.
|
||||
def __ExtensionFactory__():
|
||||
return Extension()
|
||||
|
||||
if __name__=='__main__':
|
||||
# If run from the command-line, install ourselves.
|
||||
from isapi.install import *
|
||||
params = ISAPIParameters()
|
||||
# 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="/",
|
||||
Description = Extension.__doc__,
|
||||
ScriptMaps = sm,
|
||||
ScriptMapUpdate = "replace"
|
||||
)
|
||||
params.VirtualDirs = [vd]
|
||||
HandleCommandLine(params)
|
78
venv/Lib/site-packages/isapi/samples/redirector_asynch.py
Normal file
78
venv/Lib/site-packages/isapi/samples/redirector_asynch.py
Normal file
|
@ -0,0 +1,78 @@
|
|||
# This is a sample ISAPI extension written in Python.
|
||||
|
||||
# This is like the other 'redirector' samples, but uses asnch IO when writing
|
||||
# back to the client (it does *not* use asynch io talking to the remote
|
||||
# server!)
|
||||
|
||||
from isapi import isapicon, threaded_extension
|
||||
import sys
|
||||
import traceback
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
|
||||
# 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"
|
||||
|
||||
# We synchronously read chunks of this size then asynchronously write them.
|
||||
CHUNK_SIZE=8192
|
||||
|
||||
# The callback made when IIS completes the asynch write.
|
||||
def io_callback(ecb, fp, cbIO, errcode):
|
||||
print("IO callback", ecb, fp, cbIO, errcode)
|
||||
chunk = fp.read(CHUNK_SIZE)
|
||||
if chunk:
|
||||
ecb.WriteClient(chunk, isapicon.HSE_IO_ASYNC)
|
||||
# and wait for the next callback to say this chunk is done.
|
||||
else:
|
||||
# eof - say we are complete.
|
||||
fp.close()
|
||||
ecb.DoneWithSession()
|
||||
|
||||
# The ISAPI extension - handles all requests in the site.
|
||||
class Extension(threaded_extension.ThreadPoolExtension):
|
||||
"Python sample proxy server - asynch version."
|
||||
def Dispatch(self, ecb):
|
||||
print('IIS dispatching "%s"' % (ecb.GetServerVariable("URL"),))
|
||||
url = ecb.GetServerVariable("URL")
|
||||
|
||||
new_url = proxy + url
|
||||
print("Opening %s" % new_url)
|
||||
fp = urllib.request.urlopen(new_url)
|
||||
headers = fp.info()
|
||||
ecb.SendResponseHeaders("200 OK", str(headers) + "\r\n", False)
|
||||
# now send the first chunk asynchronously
|
||||
ecb.ReqIOCompletion(io_callback, fp)
|
||||
chunk = fp.read(CHUNK_SIZE)
|
||||
if chunk:
|
||||
ecb.WriteClient(chunk, isapicon.HSE_IO_ASYNC)
|
||||
return isapicon.HSE_STATUS_PENDING
|
||||
# no data - just close things now.
|
||||
ecb.DoneWithSession()
|
||||
return isapicon.HSE_STATUS_SUCCESS
|
||||
|
||||
# The entry points for the ISAPI extension.
|
||||
def __ExtensionFactory__():
|
||||
return Extension()
|
||||
|
||||
if __name__=='__main__':
|
||||
# If run from the command-line, install ourselves.
|
||||
from isapi.install import *
|
||||
params = ISAPIParameters()
|
||||
# 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="/",
|
||||
Description = Extension.__doc__,
|
||||
ScriptMaps = sm,
|
||||
ScriptMapUpdate = "replace"
|
||||
)
|
||||
params.VirtualDirs = [vd]
|
||||
HandleCommandLine(params)
|
155
venv/Lib/site-packages/isapi/samples/redirector_with_filter.py
Normal file
155
venv/Lib/site-packages/isapi/samples/redirector_with_filter.py
Normal file
|
@ -0,0 +1,155 @@
|
|||
# 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_with_filter.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 sample redirector:
|
||||
# It is implemented by a filter and an extension, so that some requests can
|
||||
# be ignored. Compare with 'redirector_simple' which avoids the filter, but
|
||||
# is unable to selectively ignore certain requests.
|
||||
# The process is sample uses is:
|
||||
# * 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.
|
||||
# For example:
|
||||
# * 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.request, urllib.parse, urllib.error
|
||||
|
||||
# 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 key feature of this redirector over the simple redirector is that it
|
||||
# can choose to ignore certain responses by having the filter not rewrite them
|
||||
# to our virtual dir. For this sample, we just exclude the IIS help directory.
|
||||
|
||||
# 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.request.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
|
||||
if nt != isapicon.SF_NOTIFY_PREPROC_HEADERS:
|
||||
return isapicon.SF_STATUS_REQ_NEXT_NOTIFICATION
|
||||
|
||||
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)
|
||||
# For the sake of demonstration, show how the FilterContext
|
||||
# attribute is used. It always starts out life as None, and
|
||||
# any assignments made are automatically decref'd by the
|
||||
# framework during a SF_NOTIFY_END_OF_NET_SESSION notification.
|
||||
if fc.FilterContext is None:
|
||||
fc.FilterContext = 0
|
||||
fc.FilterContext += 1
|
||||
print("This is request number %d on this connection" % fc.FilterContext)
|
||||
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)
|
154
venv/Lib/site-packages/isapi/samples/test.py
Normal file
154
venv/Lib/site-packages/isapi/samples/test.py
Normal file
|
@ -0,0 +1,154 @@
|
|||
# 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.
|
||||
|
||||
from isapi import isapicon
|
||||
from isapi.simple import SimpleExtension
|
||||
import sys, os, stat
|
||||
|
||||
if hasattr(sys, "isapidllhandle"):
|
||||
import win32traceutil
|
||||
|
||||
# We use the same reload support as 'advanced.py' demonstrates.
|
||||
from isapi import InternalReloadException
|
||||
import win32event, win32file, winerror, win32con, threading
|
||||
|
||||
# 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)
|
68
venv/Lib/site-packages/isapi/simple.py
Normal file
68
venv/Lib/site-packages/isapi/simple.py
Normal file
|
@ -0,0 +1,68 @@
|
|||
"""Simple base-classes for extensions and filters.
|
||||
|
||||
None of the filter and extension functions are considered 'optional' by the
|
||||
framework. These base-classes provide simple implementations for the
|
||||
Initialize and Terminate functions, allowing you to omit them,
|
||||
|
||||
It is not necessary to use these base-classes - but if you don't, you
|
||||
must ensure each of the required methods are implemented.
|
||||
"""
|
||||
|
||||
class SimpleExtension:
|
||||
"Base class for a simple ISAPI extension"
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def GetExtensionVersion(self, vi):
|
||||
"""Called by the ISAPI framework to get the extension version
|
||||
|
||||
The default implementation uses the classes docstring to
|
||||
set the extension description."""
|
||||
# nod to our reload capability - vi is None when we are reloaded.
|
||||
if vi is not None:
|
||||
vi.ExtensionDesc = self.__doc__
|
||||
|
||||
def HttpExtensionProc(self, control_block):
|
||||
"""Called by the ISAPI framework for each extension request.
|
||||
|
||||
sub-classes must provide an implementation for this method.
|
||||
"""
|
||||
raise NotImplementedError("sub-classes should override HttpExtensionProc")
|
||||
|
||||
def TerminateExtension(self, status):
|
||||
"""Called by the ISAPI framework as the extension terminates.
|
||||
"""
|
||||
pass
|
||||
|
||||
class SimpleFilter:
|
||||
"Base class for a a simple ISAPI filter"
|
||||
filter_flags = None
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def GetFilterVersion(self, fv):
|
||||
"""Called by the ISAPI framework to get the extension version
|
||||
|
||||
The default implementation uses the classes docstring to
|
||||
set the extension description, and uses the classes
|
||||
filter_flags attribute to set the ISAPI filter flags - you
|
||||
must specify filter_flags in your class.
|
||||
"""
|
||||
if self.filter_flags is None:
|
||||
raise RuntimeError("You must specify the filter flags")
|
||||
# nod to our reload capability - fv is None when we are reloaded.
|
||||
if fv is not None:
|
||||
fv.Flags = self.filter_flags
|
||||
fv.FilterDesc = self.__doc__
|
||||
|
||||
def HttpFilterProc(self, fc):
|
||||
"""Called by the ISAPI framework for each filter request.
|
||||
|
||||
sub-classes must provide an implementation for this method.
|
||||
"""
|
||||
raise NotImplementedError("sub-classes should override HttpExtensionProc")
|
||||
|
||||
def TerminateFilter(self, status):
|
||||
"""Called by the ISAPI framework as the filter terminates.
|
||||
"""
|
||||
pass
|
3
venv/Lib/site-packages/isapi/test/README.txt
Normal file
3
venv/Lib/site-packages/isapi/test/README.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
This is a directory for tests of the PyISAPI framework.
|
||||
|
||||
For demos, please see the pyisapi 'samples' directory.
|
Binary file not shown.
111
venv/Lib/site-packages/isapi/test/extension_simple.py
Normal file
111
venv/Lib/site-packages/isapi/test/extension_simple.py
Normal file
|
@ -0,0 +1,111 @@
|
|||
# This is an ISAPI extension purely for testing purposes. It is NOT
|
||||
# a 'demo' (even though it may be useful!)
|
||||
#
|
||||
# Install this extension, then point your browser to:
|
||||
# "http://localhost/pyisapi_test/test1"
|
||||
# This will execute the method 'test1' below. See below for the list of
|
||||
# test methods that are acceptable.
|
||||
|
||||
from isapi import isapicon, threaded_extension, ExtensionError
|
||||
from isapi.simple import SimpleFilter
|
||||
import traceback
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
import winerror
|
||||
|
||||
# If we have no console (eg, am running from inside IIS), redirect output
|
||||
# somewhere useful - in this case, the standard win32 trace collector.
|
||||
import win32api
|
||||
try:
|
||||
win32api.GetConsoleTitle()
|
||||
except win32api.error:
|
||||
# No console - redirect
|
||||
import win32traceutil
|
||||
|
||||
# The ISAPI extension - handles requests in our virtual dir, and sends the
|
||||
# response to the client.
|
||||
class Extension(threaded_extension.ThreadPoolExtension):
|
||||
"Python ISAPI Tester"
|
||||
def Dispatch(self, ecb):
|
||||
print('Tester dispatching "%s"' % (ecb.GetServerVariable("URL"),))
|
||||
url = ecb.GetServerVariable("URL")
|
||||
test_name = url.split("/")[-1]
|
||||
meth = getattr(self, test_name, None)
|
||||
if meth is None:
|
||||
raise AttributeError("No test named '%s'" % (test_name,))
|
||||
result = meth(ecb)
|
||||
if result is None:
|
||||
# This means the test finalized everything
|
||||
return
|
||||
ecb.SendResponseHeaders("200 OK", "Content-type: text/html\r\n\r\n",
|
||||
False)
|
||||
print("<HTML><BODY>Finished running test <i>", test_name, "</i>", file=ecb)
|
||||
print("<pre>", file=ecb)
|
||||
print(result, file=ecb)
|
||||
print("</pre>", file=ecb)
|
||||
print("</BODY></HTML>", file=ecb)
|
||||
ecb.DoneWithSession()
|
||||
|
||||
def test1(self, ecb):
|
||||
try:
|
||||
ecb.GetServerVariable("foo bar")
|
||||
raise RuntimeError("should have failed!")
|
||||
except ExtensionError as err:
|
||||
assert err.errno == winerror.ERROR_INVALID_INDEX, err
|
||||
return "worked!"
|
||||
|
||||
def test_long_vars(self, ecb):
|
||||
qs = ecb.GetServerVariable("QUERY_STRING")
|
||||
# Our implementation has a default buffer size of 8k - so we test
|
||||
# the code that handles an overflow by ensuring there are more
|
||||
# than 8k worth of chars in the URL.
|
||||
expected_query = ('x' * 8500)
|
||||
if len(qs)==0:
|
||||
# Just the URL with no query part - redirect to myself, but with
|
||||
# a huge query portion.
|
||||
me = ecb.GetServerVariable("URL")
|
||||
headers = "Location: " + me + "?" + expected_query + "\r\n\r\n"
|
||||
ecb.SendResponseHeaders("301 Moved", headers)
|
||||
ecb.DoneWithSession()
|
||||
return None
|
||||
if qs == expected_query:
|
||||
return "Total length of variable is %d - test worked!" % (len(qs),)
|
||||
else:
|
||||
return "Unexpected query portion! Got %d chars, expected %d" % \
|
||||
(len(qs), len(expected_query))
|
||||
|
||||
def test_unicode_vars(self, ecb):
|
||||
# We need to check that we are running IIS6! This seems the only
|
||||
# effective way from an extension.
|
||||
ver = float(ecb.GetServerVariable("SERVER_SOFTWARE").split('/')[1])
|
||||
if ver < 6.0:
|
||||
return "This is IIS version %g - unicode only works in IIS6 and later" % ver
|
||||
|
||||
us = ecb.GetServerVariable("UNICODE_SERVER_NAME")
|
||||
if not isinstance(us, str):
|
||||
raise RuntimeError("unexpected type!")
|
||||
if us != str(ecb.GetServerVariable("SERVER_NAME")):
|
||||
raise RuntimeError("Unicode and non-unicode values were not the same")
|
||||
return "worked!"
|
||||
|
||||
# The entry points for the ISAPI extension.
|
||||
def __ExtensionFactory__():
|
||||
return Extension()
|
||||
|
||||
if __name__=='__main__':
|
||||
# If run from the command-line, install ourselves.
|
||||
from isapi.install import *
|
||||
params = ISAPIParameters()
|
||||
# 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="pyisapi_test",
|
||||
Description = Extension.__doc__,
|
||||
ScriptMaps = sm,
|
||||
ScriptMapUpdate = "replace"
|
||||
)
|
||||
params.VirtualDirs = [vd]
|
||||
HandleCommandLine(params)
|
171
venv/Lib/site-packages/isapi/threaded_extension.py
Normal file
171
venv/Lib/site-packages/isapi/threaded_extension.py
Normal file
|
@ -0,0 +1,171 @@
|
|||
"""An ISAPI extension base class implemented using a thread-pool."""
|
||||
# $Id$
|
||||
|
||||
import sys
|
||||
import time
|
||||
from isapi import isapicon, ExtensionError
|
||||
import isapi.simple
|
||||
from win32file import GetQueuedCompletionStatus, CreateIoCompletionPort, \
|
||||
PostQueuedCompletionStatus, CloseHandle
|
||||
from win32security import SetThreadToken
|
||||
from win32event import INFINITE
|
||||
from pywintypes import OVERLAPPED
|
||||
|
||||
import threading
|
||||
import traceback
|
||||
|
||||
ISAPI_REQUEST = 1
|
||||
ISAPI_SHUTDOWN = 2
|
||||
|
||||
class WorkerThread(threading.Thread):
|
||||
def __init__(self, extension, io_req_port):
|
||||
self.running = False
|
||||
self.io_req_port = io_req_port
|
||||
self.extension = extension
|
||||
threading.Thread.__init__(self)
|
||||
# We wait 15 seconds for a thread to terminate, but if it fails to,
|
||||
# we don't want the process to hang at exit waiting for it...
|
||||
self.setDaemon(True)
|
||||
|
||||
def run(self):
|
||||
self.running = True
|
||||
while self.running:
|
||||
errCode, bytes, key, overlapped = \
|
||||
GetQueuedCompletionStatus(self.io_req_port, INFINITE)
|
||||
if key == ISAPI_SHUTDOWN and overlapped is None:
|
||||
break
|
||||
|
||||
# Let the parent extension handle the command.
|
||||
dispatcher = self.extension.dispatch_map.get(key)
|
||||
if dispatcher is None:
|
||||
raise RuntimeError("Bad request '%s'" % (key,))
|
||||
|
||||
dispatcher(errCode, bytes, key, overlapped)
|
||||
|
||||
def call_handler(self, cblock):
|
||||
self.extension.Dispatch(cblock)
|
||||
|
||||
# A generic thread-pool based extension, using IO Completion Ports.
|
||||
# Sub-classes can override one method to implement a simple extension, or
|
||||
# may leverage the CompletionPort to queue their own requests, and implement a
|
||||
# fully asynch extension.
|
||||
class ThreadPoolExtension(isapi.simple.SimpleExtension):
|
||||
"Base class for an ISAPI extension based around a thread-pool"
|
||||
max_workers = 20
|
||||
worker_shutdown_wait = 15000 # 15 seconds for workers to quit...
|
||||
def __init__(self):
|
||||
self.workers = []
|
||||
# extensible dispatch map, for sub-classes that need to post their
|
||||
# own requests to the completion port.
|
||||
# Each of these functions is called with the result of
|
||||
# GetQueuedCompletionStatus for our port.
|
||||
self.dispatch_map = {
|
||||
ISAPI_REQUEST: self.DispatchConnection,
|
||||
}
|
||||
|
||||
def GetExtensionVersion(self, vi):
|
||||
isapi.simple.SimpleExtension.GetExtensionVersion(self, vi)
|
||||
# As per Q192800, the CompletionPort should be created with the number
|
||||
# of processors, even if the number of worker threads is much larger.
|
||||
# Passing 0 means the system picks the number.
|
||||
self.io_req_port = CreateIoCompletionPort(-1, None, 0, 0)
|
||||
# start up the workers
|
||||
self.workers = []
|
||||
for i in range(self.max_workers):
|
||||
worker = WorkerThread(self, self.io_req_port)
|
||||
worker.start()
|
||||
self.workers.append(worker)
|
||||
|
||||
def HttpExtensionProc(self, control_block):
|
||||
overlapped = OVERLAPPED()
|
||||
overlapped.object = control_block
|
||||
PostQueuedCompletionStatus(self.io_req_port, 0, ISAPI_REQUEST, overlapped)
|
||||
return isapicon.HSE_STATUS_PENDING
|
||||
|
||||
def TerminateExtension(self, status):
|
||||
for worker in self.workers:
|
||||
worker.running = False
|
||||
for worker in self.workers:
|
||||
PostQueuedCompletionStatus(self.io_req_port, 0, ISAPI_SHUTDOWN, None)
|
||||
# wait for them to terminate - pity we aren't using 'native' threads
|
||||
# as then we could do a smart wait - but now we need to poll....
|
||||
end_time = time.time() + self.worker_shutdown_wait/1000
|
||||
alive = self.workers
|
||||
while alive:
|
||||
if time.time() > end_time:
|
||||
# xxx - might be nice to log something here.
|
||||
break
|
||||
time.sleep(0.2)
|
||||
alive = [w for w in alive if w.isAlive()]
|
||||
self.dispatch_map = {} # break circles
|
||||
CloseHandle(self.io_req_port)
|
||||
|
||||
# This is the one operation the base class supports - a simple
|
||||
# Connection request. We setup the thread-token, and dispatch to the
|
||||
# sub-class's 'Dispatch' method.
|
||||
def DispatchConnection(self, errCode, bytes, key, overlapped):
|
||||
control_block = overlapped.object
|
||||
# setup the correct user for this request
|
||||
hRequestToken = control_block.GetImpersonationToken()
|
||||
SetThreadToken(None, hRequestToken)
|
||||
try:
|
||||
try:
|
||||
self.Dispatch(control_block)
|
||||
except:
|
||||
self.HandleDispatchError(control_block)
|
||||
finally:
|
||||
# reset the security context
|
||||
SetThreadToken(None, None)
|
||||
|
||||
def Dispatch(self, ecb):
|
||||
"""Overridden by the sub-class to handle connection requests.
|
||||
|
||||
This class creates a thread-pool using a Windows completion port,
|
||||
and dispatches requests via this port. Sub-classes can generally
|
||||
implement each connection request using blocking reads and writes, and
|
||||
the thread-pool will still provide decent response to the end user.
|
||||
|
||||
The sub-class can set a max_workers attribute (default is 20). Note
|
||||
that this generally does *not* mean 20 threads will all be concurrently
|
||||
running, via the magic of Windows completion ports.
|
||||
|
||||
There is no default implementation - sub-classes must implement this.
|
||||
"""
|
||||
raise NotImplementedError("sub-classes should override Dispatch")
|
||||
|
||||
def HandleDispatchError(self, ecb):
|
||||
"""Handles errors in the Dispatch method.
|
||||
|
||||
When a Dispatch method call fails, this method is called to handle
|
||||
the exception. The default implementation formats the traceback
|
||||
in the browser.
|
||||
"""
|
||||
ecb.HttpStatusCode = isapicon.HSE_STATUS_ERROR
|
||||
#control_block.LogData = "we failed!"
|
||||
exc_typ, exc_val, exc_tb = sys.exc_info()
|
||||
limit = None
|
||||
try:
|
||||
try:
|
||||
import cgi
|
||||
ecb.SendResponseHeaders("200 OK", "Content-type: text/html\r\n\r\n",
|
||||
False)
|
||||
print(file=ecb)
|
||||
print("<H3>Traceback (most recent call last):</H3>", file=ecb)
|
||||
list = traceback.format_tb(exc_tb, limit) + \
|
||||
traceback.format_exception_only(exc_typ, exc_val)
|
||||
print("<PRE>%s<B>%s</B></PRE>" % (
|
||||
cgi.escape("".join(list[:-1])), cgi.escape(list[-1]),), file=ecb)
|
||||
except ExtensionError:
|
||||
# The client disconnected without reading the error body -
|
||||
# its probably not a real browser at the other end, ignore it.
|
||||
pass
|
||||
except:
|
||||
print("FAILED to render the error message!")
|
||||
traceback.print_exc()
|
||||
print("ORIGINAL extension error:")
|
||||
traceback.print_exception(exc_typ, exc_val, exc_tb)
|
||||
finally:
|
||||
# holding tracebacks in a local of a frame that may itself be
|
||||
# part of a traceback used to be evil and cause leaks!
|
||||
exc_tb = None
|
||||
ecb.DoneWithSession()
|
Loading…
Add table
Add a link
Reference in a new issue