<LI><A HREF="core.html#include">Include</A>
<LI><A HREF="mod_autoindex.html#indexignore">IndexIgnore</A>
<LI><A HREF="mod_autoindex.html#indexoptions">IndexOptions</A>
+<LI><A HREF="mod_isapi.html#isapiappendlogtoerrors">ISAPIAppendLogToErrors</A>
+<LI><A HREF="mod_isapi.html#isapiappendlogtoquery">ISAPIAppendLogToQuery</A>
+<LI><A HREF="mod_isapi.html#isapifilecache">ISAPIFileCache</A>
+<LI><A HREF="mod_isapi.html#isapilognotsupported">ISAPILogNotSupported</A>
+<LI><A HREF="mod_isapi.html#isapireadaheadbuffer">ISAPIReadAheadBuffer</A>
<LI><A HREF="core.html#keepalive">KeepAlive</A>
<LI><A HREF="core.html#keepalivetimeout">KeepAliveTimeout</A>
<LI><A HREF="mod_negotiation.html#languagepriority">LanguagePriority</A>
<H1 ALIGN="CENTER">Module mod_isapi</H1>
-<P>This module is contained in the <CODE>mod_isapi.c</CODE> file, and is
- compiled in by default. It provides support for ISAPI Extensions when
- running under Microsoft Windows. Any document with a handler of
- <CODE>isapi-isa</CODE> will be processed by this module.</P>
+<P>This module supports ISAPI Extensions within Apache for Windows.</P>
-<H2>Purpose</H2>
+<P><A
+HREF="module-dict.html#Status"
+REL="Help"
+><STRONG>Status:</STRONG></A> Base
+<BR>
+<A
+HREF="module-dict.html#SourceFile"
+REL="Help"
+><STRONG>Source File:</STRONG></A> mod_isapi.c
+<BR>
+<A
+HREF="module-dict.html#ModuleIdentifier"
+REL="Help"
+><STRONG>Module Identifier:</STRONG></A> isapi_module
+<BR>
+<A
+HREF="module-dict.html#Compatibility"
+REL="Help"
+><STRONG>Compatibility:</STRONG></A> WIN32 only
+</P>
-<P>This module implements the ISAPI handler extension API, version 2.0.
- It allows Internet Server Applications (<EM>i.e.</EM>, ISAPI handlers)
- to be used with Apache for Windows.</P>
+<H2>Summary</H2>
+
+<P>This module implements the Internet Server extension API. It allows
+ Internet Server extensions (<EM>e.g.</EM> ISAPI .dll modules) to be
+ served by Apache for Windows, subject to the noted restrictions.</P>
+
+<P>ISAPI extension modules (.dll files) are written by third parties. The
+ Apache Group does not author these modules, so we provide no support for
+ them. Please contact the ISAPI's author directly if you are experiencing
+ problems running their ISAPI extention. <STRONG>Please <EM>do not</EM>
+ post such problems to Apache's lists or bug reporting pages.</STRONG></P>
<H2>Usage</H2>
-<P>In the server configuration file, add a handler called
- <CODE>isapi-isa</CODE>, and map it to files with a <CODE>.DLL</CODE>
- extension. In other words:</P>
+<P>In the server configuration file, use the AddHandler directive to
+ associate ISAPI files with the <CODE>isapi-isa</CODE> handler, and map
+ it to the with their file extensions. To enable any .dll file to be
+ processed as an ISAPI extention, edit the httpd.conf file and add the
+ following line:</P>
+
<PRE>
AddHandler isapi-isa dll
</PRE>
-<P>Now simply place the ISA DLLs into your document root, and they will
- be loaded when their URLs are accessed.</P>
-<P>ISAPI Extensions are governed by the same restrictions as CGI
- scripts. That is, <CODE>Options ExecCGI</CODE> must be active in the
- directory that contains the ISA.</P>
+<P>There is no capability within the Apache server to leave a requested
+ module loaded. However, you may preload and keep a specific module loaded
+ by using the following syntax in your httpd.conf:
+
+<PRE>
+ ISAPICacheFile c:/WebWork/Scripts/ISAPI/mytest.dll
+</PRE>
+
+<P>Whether or not you have preloaded an ISAPI extension, all ISAPI
+ extensions are governed by the same permissions and restrictions
+ as CGI scripts. That is, <CODE>Options ExecCGI</CODE> must be set for
+ the directory that contains the ISAPI .dll file.</P>
+
+<P>Review the <A HREF="#notes">Additional Notes</A> and the
+ <A HREF="#journal">Programmer's Journal</A> for additional details and
+ clarification of the specific ISAPI support offered by mod_isapi.</P>
+
+<H2>Directives</H2>
+<UL>
+<LI><A HREF="#isapifilecache">ISAPIFileCache</A>
+<LI><A HREF="#isapireadaheadbuffer">ISAPIReadAheadBuffer</A>
+<LI><A HREF="#isapilognotsupported">ISAPILogNotSupported</A>
+<LI><A HREF="#isapiappendlogtoerrors">ISAPIAppendLogToErrors</A>
+<LI><A HREF="#isapiappendlogtoquery">ISAPIAppendLogToQuery</A>
+</UL>
+<HR>
+
+<H2><A NAME="isapifilecache">ISAPIFileCache directive</A></H2>
+<!--%plaintext <?INDEX {\tt ISAPIFileCache} directive> -->
+<A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> ISAPIFileCache <EM>file [file...]</EM><BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> server config<BR>
+<A
+ HREF="directive-dict.html#Override"
+ REL="Help"
+><STRONG>Override:</STRONG></A> None<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> Base<BR>
+<A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+><STRONG>Module:</STRONG></A> mod_isapi<BR>
+<A
+HREF="module-dict.html#Compatibility"
+REL="Help"
+><STRONG>Compatibility:</STRONG></A> Apache 2.0 and later, Win32 only<P>
+
+ Specifies a space-separated list of file names to be loaded
+ when the Apache server is launched, and remain loaded until
+ the server is shut down. This directive may be repeated
+ for every ISAPI .dll file desired. The full path name of
+ each file should be specified.
+ <P>
+<HR>
+
+<H2><A NAME="isapireadaheadbuffer">ISAPIReadAheadBuffer directive</A></H2>
+<!--%plaintext <?INDEX {\tt ISAPIReadAheadBuffer} directive> -->
+<A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> ISAPIReadAheadBuffer <EM>size</EM><BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A> 49152<BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> server config<BR>
+<A
+ HREF="directive-dict.html#Override"
+ REL="Help"
+><STRONG>Override:</STRONG></A> None<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> Base<BR>
+<A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+><STRONG>Module:</STRONG></A> mod_isapi<BR>
+<A
+HREF="module-dict.html#Compatibility"
+REL="Help"
+><STRONG>Compatibility:</STRONG></A> Apache 1.3.13 and later, Win32 only<P>
+
-<H2>Notes</H2>
+ Defines the maximum size of the Read Ahead Buffer sent to
+ ISAPI extentions when they are initally invoked. All
+ remaining data must be retrieved using the ReadClient
+ callback; some ISAPI extensions may not support the
+ ReadClient function. Refer questions to the ISAPI extention's
+ author.
+ <P>
+<HR>
+
+<H2><A NAME="isapilognotsupported">ISAPILogNotSupported directive</A></H2>
+<!--%plaintext <?INDEX {\tt ISAPILogNotSupported} directive> -->
+<A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> ISAPILogNotSupported <EM>on|off</EM><BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A> on<BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> server config<BR>
+<A
+ HREF="directive-dict.html#Override"
+ REL="Help"
+><STRONG>Override:</STRONG></A> None<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> Base<BR>
+<A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+><STRONG>Module:</STRONG></A> mod_isapi<BR>
+<A
+HREF="module-dict.html#Compatibility"
+REL="Help"
+><STRONG>Compatibility:</STRONG></A> Apache 1.3.13 and later, Win32 only<P>
+
+ Logs all requests for unsupported features from ISAPI extentions
+ in the server error log. While this should be turned off once
+ all desired ISAPI modules are functioning, it defaults to on
+ to help administrators track down problems.
+ <P>
+<HR>
+
+<H2><A NAME="isapiappendlogtoerrors">ISAPIAppendLogToErrors directive</A></H2>
+<!--%plaintext <?INDEX {\tt ISAPIAppendLogToErrors} directive> -->
+<A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> ISAPIAppendLogToErrors <EM>on|off</EM><BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A> off<BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> server config<BR>
+<A
+ HREF="directive-dict.html#Override"
+ REL="Help"
+><STRONG>Override:</STRONG></A> None<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> Base<BR>
+<A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+><STRONG>Module:</STRONG></A> mod_isapi<BR>
+<A
+HREF="module-dict.html#Compatibility"
+REL="Help"
+><STRONG>Compatibility:</STRONG></A> Apache 1.3.13 and later, Win32 only<P>
+
+ Record HSE_APPEND_LOG_PARAMETER requests from ISAPI extentions
+ to the server error log.
+ <P>
+<HR>
+
+<H2><A NAME="isapiappendlogtoquery">ISAPIAppendLogToQuery directive</A></H2>
+<!--%plaintext <?INDEX {\tt ISAPIAppendLogToQuery} directive> -->
+<A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> ISAPIAppendLogToQuery <EM>on|off</EM><BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A> off<BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> server config<BR>
+<A
+ HREF="directive-dict.html#Override"
+ REL="Help"
+><STRONG>Override:</STRONG></A> None<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> Base<BR>
+<A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+><STRONG>Module:</STRONG></A> mod_isapi<BR>
+<A
+HREF="module-dict.html#Compatibility"
+REL="Help"
+><STRONG>Compatibility:</STRONG></A> Apache 1.3.13 and later, Win32 only<P>
+
+ Record HSE_APPEND_LOG_PARAMETER requests from ISAPI extentions
+ to the query field (appended to the CustomLog %q component).
+ <P>
+<HR>
+
+<H2><A NAME="notes">Additional Notes</A></H2>
<P>Apache's ISAPI implementation conforms to all of the ISAPI 2.0
- specification, except for the "Microsoft-specific" extensions dealing
- with asynchronous I/O. Apache's I/O model does not allow asynchronous
+ specification, except for some "Microsoft-specific" extensions dealing
+ with asynchronous I/O. Apache's I/O model does not allow asynchronous
reading and writing in a manner that the ISAPI could access. If an ISA
- tries to access async I/O, a message will be place in the error log,
- to help with debugging.
+ tries to access unsupported features, including async I/O, a message is
+ placed in the error log to help with debugging. Since these messages
+ can become a flood, the directive <CODE>ISAPILogNotSupported Off</CODE>
+ exists to quiet this noise.</P>
+
+<P>Some servers, like Microsoft IIS, load the ISAPI extension into the server
+ and keep it loaded until memory usage is too high, or unless configuration
+ options are specified. Apache currently loads and unloads the ISAPI
+ extension each time it is requested, unless the ISAPICacheFile directive
+ is specified. This is inefficient, but Apache's memory model makes this
+ the most effective method. Many ISAPI modules are subtly incompatible
+ with the Apache server, and unloading these modules helps to ensure the
+ stability of the server.</P>
+
+<P>Also, remember that while Apache supports ISAPI Extensions, it
+ <STRONG>does not support ISAPI Filters.</STRONG> Support for filters may
+ be added at a later date, but no support is planned at this time.</P>
+
+<H2><A NAME="journal">Programmer's Journal</A></H2>
+
+<P>If you are programming Apache 2.0 mod_isapi modules, you must limit your
+ calls to ServerSupportFunction to the following directives:</P>
+
+<DL>
+ <DT>HSE_REQ_SEND_URL_REDIRECT_RESP
+ <DD>Redirect the user to another location.<BR>
+ This must be a fully qualified URL (e.g. http://server/location).
+ <DT>HSE_REQ_SEND_URL
+ <DD>Redirect the user to another location.<BR>
+ This cannot be a fully qualified URL, you are not allowed
+ to pass the protocol or a server name (e.g. simply /location).<BR>
+ This redirection is handled by the server, not the browser.<BR>
+ <STRONG>Warning:</STRONG> in their recent documentation, Microsoft
+ appears to have abandoned the distinction between the two
+ HSE_REQ_SEND_URL functions. Apache continues to treat them as two
+ distinct functions with different requirements and behaviors.
+ <DT>HSE_REQ_SEND_RESPONSE_HEADER
+ <DD>Apache accepts a response body following the header if it follows
+ the blank line (two consecutive newlines) in the headers string
+ argument. This body cannot contain NULLs, since the headers
+ argument is NULL terminated.
+ <DT>HSE_REQ_DONE_WITH_SESSION
+ <DD>Apache considers this a no-op, since the session will be finished
+ when the ISAPI returns from processing.
+ <DT>HSE_REQ_MAP_URL_TO_PATH
+ <DD>Apache will translate a virtual name to a physical name.
+ <DT>HSE_APPEND_LOG_PARAMETER
+ <DD>This logged message may be captured in any of the following logs:
+ <UL>
+ <LI>in the \"%{isapi-parameter}n\" component in a CustomLog directive
+ <LI>in the %q log component with the ISAPIAppendLogToQuery On directive
+ <LI>in the error log with the ISAPIAppendLogToErrors On directive
+ </UL>
+ The first option, the %{isapi-parameter}n component, is always available
+ and prefered.
+ <DT>HSE_REQ_IS_KEEP_CONN
+ <DD>Will return the negotiated Keep-Alive status.
+ <DT>HSE_REQ_SEND_RESPONSE_HEADER_EX
+ <DD>Will behave as documented, although the fKeepConn flag is ignored.
+ <DT>HSE_REQ_IS_CONNECTED
+ <DD>Will report false if the request has been aborted.
+</DL>
+
+<P>Apache returns FALSE to any unsupported call to ServerSupportFunction, and
+ sets the GetLastError value to ERROR_INVALID_PARAMETER.</P>
-<P>Some servers, like Microsoft IIS, load the ISA into the server, and
- keep it loaded until memory usage is too high, and it is
- unloaded. Apache currently loads and unloads the ISA for each
- request. This is inefficient, but Apache's request model makes this
- method the only method that currently works. A future release may use
- a more effective loading method.
+<P>ReadClient retrieves the request body exceeding the initial buffer
+ (defined by ISAPIReadAheadBuffer). Based on the ISAPIReadAheadBuffer
+ setting (number of bytes to buffer prior to calling the ISAPI handler)
+ shorter requests are sent complete to the extension when it is invoked.
+ If the request is longer, the ISAPI extension must use ReadClient to
+ retrieve the remaining request body.</P>
-<P>Apache 1.3a1 currently limits POST and PUT input to 48k per
- request. This is to work around a problem with the ISAPI implementation
- that could result in a denial of service attack. It is expected that
- support for larger uploads will be added soon.
+<P>WriteClient is supported, but only with the HSE_IO_SYNC flag or
+ no option flag (value of 0). Any other WriteClient request will
+ be rejected with a return value of FALSE, and a GetLastError
+ value of ERROR_INVALID_PARAMETER.</P>
-<P>Also, remember that while Apache supports ISAPI Extensions, it does
- not support ISAPI Filters. Support for filters may be added at a later
- date, but no support is planned at this time.</P>
+<P>GetServerVariable is supported, although extended server variables do not
+ exist (as defined by other servers.) All the usual Apache CGI environment
+ variables are available from GetServerVariable, as well as the ALL_HTTP
+ and ALL_RAW values.</P>
+<P>Apache 2.0 mod_isapi supports additional features introduced in later
+ versions of the ISAPI specification, as well as limited emulation of
+ async I/O and the TransmitFile semantics. Apache also supports preloading
+ ISAPI .dlls for performance, neither of which were not available under
+ Apache 1.3 mod_isapi.</P>
<!--#include virtual="footer.html" -->
</BODY>
</HTML>
/* Our loaded isapi module description structure */
typedef struct {
+ const char *filename;
HINSTANCE handle;
HSE_VERSION_INFO *pVer;
PFN_GETEXTENSIONVERSION GetExtensionVersion;
HANDLE complete;
} isapi_cid;
-apr_status_t isapi_load(request_rec *r, isapi_loaded** isa);
-BOOL isapi_unload(isapi_loaded* isa, int force);
+static BOOL isapi_unload(isapi_loaded* isa, int force);
static apr_status_t cleanup_isapi_server_config(void *sconfv)
{
isapi_server_conf *sconf = sconfv;
size_t n;
- isapi_loaded *isa;
+ isapi_loaded **isa;
n = sconf->loaded->nelts;
- isa = (isapi_loaded *)sconf->loaded->elts;
+ isa = (isapi_loaded **)sconf->loaded->elts;
while(n--) {
- if (isa->handle)
- isapi_unload(isa, TRUE);
+ if ((*isa)->handle)
+ isapi_unload(*isa, TRUE);
++isa;
}
CloseHandle(sconf->lock);
static void *create_isapi_server_config(apr_pool_t *p, server_rec *s)
{
- isapi_server_conf *sconf = apr_palloc(p, sizeof(isapi_server_conf*));
+ isapi_server_conf *sconf = apr_palloc(p, sizeof(isapi_server_conf));
sconf->loaded = apr_make_array(p, 20, sizeof(isapi_loaded*));
sconf->lock = CreateMutex(NULL, FALSE, NULL);
return sconf;
}
-static apr_status_t isapi_load(request_rec *r, isapi_loaded** isa)
+static int compare_loaded(const void *av, const void *bv)
{
+ const isapi_loaded **a = av;
+ const isapi_loaded **b = bv;
+
+ return strcmp((*a)->filename, (*b)->filename);
+}
+
+static void isapi_post_config(apr_pool_t *p, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+ isapi_server_conf *sconf = ap_get_module_config(s->module_config,
+ &isapi_module);
+ isapi_loaded **elts = (isapi_loaded **)sconf->loaded->elts;
+ int nelts = sconf->loaded->nelts;
+
+ /* sort the elements of the main_server, by filename */
+ qsort(elts, nelts, sizeof(isapi_loaded*), compare_loaded);
+
+ /* and make the virtualhosts share the same thing */
+ for (s = s->next; s; s = s->next) {
+ ap_set_module_config(s->module_config, &isapi_module, sconf);
+ }
+}
+
+static apr_status_t isapi_load(apr_pool_t *p, isapi_server_conf *sconf,
+ request_rec *r, const char *fpath,
+ isapi_loaded** isa)
+{
+ isapi_loaded **found = (isapi_loaded **)sconf->loaded->elts;
char *fspec;
- char *p;
+ char *ch;
+ int n;
+
+ for (n = 0; n < sconf->loaded->nelts; ++n) {
+ if (strcasecmp(fpath, (*found)->filename) == 0) {
+ break;
+ }
+ ++found;
+ }
+
+ if (n < sconf->loaded->nelts)
+ {
+ *isa = *found;
+ if ((*isa)->handle)
+ {
+ ++(*isa)->refcount;
+ return APR_SUCCESS;
+ }
+ /* Otherwise we fall through and have to reload the resource
+ * into this existing mod_isapi cache bucket.
+ */
+ }
+ else
+ {
+ *isa = apr_pcalloc(p, sizeof(isapi_module));
+ (*isa)->filename = fpath;
+ (*isa)->pVer = apr_pcalloc(p, sizeof(HSE_VERSION_INFO));
+
+ /* TODO: These need to become overrideable, so that we
+ * assure a given isapi can be fooled into behaving well.
+ */
+ (*isa)->timeout = INFINITE; /* microsecs */
+ (*isa)->fakeasync = TRUE;
+ (*isa)->reportversion = MAKELONG(0, 5); /* Revision 5.0 */
+ }
+
/* Per PR2555, the LoadLibraryEx function is very picky about slashes.
* Debugging on NT 4 SP 6a reveals First Chance Exception within NTDLL.
* LoadLibrary in the MS PSDK also reveals that it -explicitly- states
*
* Transpose '\' for '/' in the filename.
*/
- p = fspec = apr_pstrdup(r->pool, r->filename);
- while (*p) {
- if (*p == '/')
- *p = '\\';
- ++p;
+ ch = fspec = apr_pstrdup(p, fpath);
+ while (*ch) {
+ if (*ch == '/')
+ *ch = '\\';
+ ++ch;
}
- /* TODO: Critical section
- *
- * Warning: cid should not be allocated from pool if we
- * cache the isapi process in-memory.
- *
- * This code could use cacheing... everything that follows
- * should only be performed on the first isapi dll invocation,
- * not with every HttpExtensionProc()
- */
- *isa = apr_pcalloc(r->pool, sizeof(isapi_module));
- (*isa)->pVer = apr_pcalloc(r->pool, sizeof(HSE_VERSION_INFO));
- (*isa)->refcount = 1;
+ (*isa)->handle = LoadLibraryEx(fspec, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
- /* TODO: These may need to become overrideable, so that we
- * assure a given isapi can be fooled into behaving well.
- */
- (*isa)->timeout = INFINITE; /* microsecs */
- (*isa)->fakeasync = TRUE;
- (*isa)->reportversion = MAKELONG(0, 5); /* Revision 5.0 */
-
- if (!((*isa)->handle = LoadLibraryEx(r->filename, NULL,
- LOAD_WITH_ALTERED_SEARCH_PATH)))
+ if (!(*isa)->handle)
{
apr_status_t rv = GetLastError();
ap_log_rerror(APLOG_MARK, APLOG_ALERT, GetLastError(), r,
- "ISAPI %s failed to load", r->filename);
+ "ISAPI %s failed to load", fpath);
(*isa)->handle = NULL;
- (*isa)->refcount = 0;
return rv;
}
apr_status_t rv = GetLastError();
ap_log_rerror(APLOG_MARK, APLOG_ALERT, rv, r,
"ISAPI %s is missing GetExtensionVersion()",
- r->filename);
+ fpath);
FreeLibrary((*isa)->handle);
(*isa)->handle = NULL;
- (*isa)->refcount = 0;
return rv;
}
apr_status_t rv = GetLastError();
ap_log_rerror(APLOG_MARK, APLOG_ALERT, rv, r,
"ISAPI %s is missing HttpExtensionProc()",
- r->filename);
+ fpath);
FreeLibrary((*isa)->handle);
(*isa)->handle = NULL;
- (*isa)->refcount = 0;
return rv;
}
apr_status_t rv = GetLastError();
ap_log_rerror(APLOG_MARK, APLOG_ALERT, rv, r,
"ISAPI %s call GetExtensionVersion() failed",
- r->filename);
+ fpath);
FreeLibrary((*isa)->handle);
(*isa)->handle = NULL;
- (*isa)->refcount = 0;
return rv;
}
+ ++(*isa)->refcount;
+
return APR_SUCCESS;
}
apr_status_t isapi_handler (request_rec *r)
{
+ isapi_server_conf *sconf = ap_get_module_config(r->server->module_config,
+ &isapi_module);
apr_table_t *e = r->subprocess_env;
apr_status_t rv;
isapi_loaded *isa;
if (r->finfo.filetype != APR_REG)
return HTTP_FORBIDDEN;
- /* Load and cache or retrieve the cached extention */
- if (isapi_load(r, &isa) != APR_SUCCESS)
+ /* Load the isapi extention without caching (sconf == NULL)
+ * but note that we will recover an existing cached module.
+ */
+ if (isapi_load(r->pool, sconf, r, r->filename, &isa) != APR_SUCCESS)
return HTTP_INTERNAL_SERVER_ERROR;
/* Set up variables */
return NULL;
}
+static const char *isapi_cmd_cachefile(cmd_parms *cmd, void *dummy,
+ const char *filename)
+
+{
+ isapi_server_conf *sconf = ap_get_module_config(cmd->server->module_config,
+ &isapi_module);
+ isapi_loaded *isa, **newisa;
+ apr_finfo_t tmp;
+ apr_status_t rv;
+ char *fspec;
+
+ fspec = ap_os_case_canonical_filename(cmd->pool, filename);
+ if (apr_stat(&tmp, fspec, cmd->temp_pool) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
+ "ISAPI: unable to stat(%s), skipping", filename);
+ return NULL;
+ }
+ if (tmp.filetype != APR_REG) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
+ "ISAPI: %s isn't a regular file, skipping", filename);
+ return NULL;
+ }
+
+ /* Load the extention as cached (passing sconf) */
+ rv = isapi_load(cmd->pool, sconf, NULL, fspec, &isa);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, rv, cmd->server,
+ "ISAPI: unable to cache %s, skipping", filename);
+ return NULL;
+ }
+
+ /* Add to cached list of loaded modules */
+ newisa = apr_push_array(sconf->loaded);
+ *newisa = isa;
+
+ return NULL;
+}
+
+static void isapi_hooks(void)
+{
+ ap_hook_post_config(isapi_post_config, NULL, NULL, AP_HOOK_MIDDLE);
+}
+
static const command_rec isapi_cmds[] = {
-{ "ISAPIReadAheadBuffer", isapi_cmd_readaheadbuffer, NULL, RSRC_CONF, TAKE1,
- "Maximum bytes to initially pass to the ISAPI handler" },
-{ "ISAPILogNotSupported", isapi_cmd_lognotsupported, NULL, RSRC_CONF, TAKE1,
- "Log requests not supported by the ISAPI server" },
-{ "ISAPIAppendLogToErrors", isapi_cmd_appendlogtoerrors, NULL, RSRC_CONF, TAKE1,
- "Send all Append Log requests to the error log" },
-{ "ISAPIAppendLogToQuery", isapi_cmd_appendlogtoquery, NULL, RSRC_CONF, TAKE1,
- "Append Log requests are concatinated to the query args" },
+AP_INIT_TAKE1("ISAPIReadAheadBuffer", isapi_cmd_readaheadbuffer, NULL, RSRC_CONF,
+ "Maximum bytes to initially pass to the ISAPI handler"),
+AP_INIT_TAKE1("ISAPILogNotSupported", isapi_cmd_lognotsupported, NULL, RSRC_CONF,
+ "Log requests not supported by the ISAPI server"),
+AP_INIT_TAKE1("ISAPIAppendLogToErrors", isapi_cmd_appendlogtoerrors, NULL, RSRC_CONF,
+ "Send all Append Log requests to the error log"),
+AP_INIT_TAKE1("ISAPIAppendLogToQuery", isapi_cmd_appendlogtoquery, NULL, RSRC_CONF,
+ "Append Log requests are concatinated to the query args"),
+AP_INIT_ITERATE("ISAPICacheFile", isapi_cmd_cachefile, NULL, RSRC_CONF,
+ "Cache the specified ISAPI extension in-process"),
{ NULL }
};
NULL, /* merge server config */
isapi_cmds, /* command apr_table_t */
isapi_handlers, /* handlers */
- NULL /* register hooks */
+ isapi_hooks /* register hooks */
};
/* Our loaded isapi module description structure */
typedef struct {
+ const char *filename;
HINSTANCE handle;
HSE_VERSION_INFO *pVer;
PFN_GETEXTENSIONVERSION GetExtensionVersion;
HANDLE complete;
} isapi_cid;
-apr_status_t isapi_load(request_rec *r, isapi_loaded** isa);
-BOOL isapi_unload(isapi_loaded* isa, int force);
+static BOOL isapi_unload(isapi_loaded* isa, int force);
static apr_status_t cleanup_isapi_server_config(void *sconfv)
{
isapi_server_conf *sconf = sconfv;
size_t n;
- isapi_loaded *isa;
+ isapi_loaded **isa;
n = sconf->loaded->nelts;
- isa = (isapi_loaded *)sconf->loaded->elts;
+ isa = (isapi_loaded **)sconf->loaded->elts;
while(n--) {
- if (isa->handle)
- isapi_unload(isa, TRUE);
+ if ((*isa)->handle)
+ isapi_unload(*isa, TRUE);
++isa;
}
CloseHandle(sconf->lock);
static void *create_isapi_server_config(apr_pool_t *p, server_rec *s)
{
- isapi_server_conf *sconf = apr_palloc(p, sizeof(isapi_server_conf*));
+ isapi_server_conf *sconf = apr_palloc(p, sizeof(isapi_server_conf));
sconf->loaded = apr_make_array(p, 20, sizeof(isapi_loaded*));
sconf->lock = CreateMutex(NULL, FALSE, NULL);
return sconf;
}
-static apr_status_t isapi_load(request_rec *r, isapi_loaded** isa)
+static int compare_loaded(const void *av, const void *bv)
{
+ const isapi_loaded **a = av;
+ const isapi_loaded **b = bv;
+
+ return strcmp((*a)->filename, (*b)->filename);
+}
+
+static void isapi_post_config(apr_pool_t *p, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+ isapi_server_conf *sconf = ap_get_module_config(s->module_config,
+ &isapi_module);
+ isapi_loaded **elts = (isapi_loaded **)sconf->loaded->elts;
+ int nelts = sconf->loaded->nelts;
+
+ /* sort the elements of the main_server, by filename */
+ qsort(elts, nelts, sizeof(isapi_loaded*), compare_loaded);
+
+ /* and make the virtualhosts share the same thing */
+ for (s = s->next; s; s = s->next) {
+ ap_set_module_config(s->module_config, &isapi_module, sconf);
+ }
+}
+
+static apr_status_t isapi_load(apr_pool_t *p, isapi_server_conf *sconf,
+ request_rec *r, const char *fpath,
+ isapi_loaded** isa)
+{
+ isapi_loaded **found = (isapi_loaded **)sconf->loaded->elts;
char *fspec;
- char *p;
+ char *ch;
+ int n;
+
+ for (n = 0; n < sconf->loaded->nelts; ++n) {
+ if (strcasecmp(fpath, (*found)->filename) == 0) {
+ break;
+ }
+ ++found;
+ }
+
+ if (n < sconf->loaded->nelts)
+ {
+ *isa = *found;
+ if ((*isa)->handle)
+ {
+ ++(*isa)->refcount;
+ return APR_SUCCESS;
+ }
+ /* Otherwise we fall through and have to reload the resource
+ * into this existing mod_isapi cache bucket.
+ */
+ }
+ else
+ {
+ *isa = apr_pcalloc(p, sizeof(isapi_module));
+ (*isa)->filename = fpath;
+ (*isa)->pVer = apr_pcalloc(p, sizeof(HSE_VERSION_INFO));
+
+ /* TODO: These need to become overrideable, so that we
+ * assure a given isapi can be fooled into behaving well.
+ */
+ (*isa)->timeout = INFINITE; /* microsecs */
+ (*isa)->fakeasync = TRUE;
+ (*isa)->reportversion = MAKELONG(0, 5); /* Revision 5.0 */
+ }
+
/* Per PR2555, the LoadLibraryEx function is very picky about slashes.
* Debugging on NT 4 SP 6a reveals First Chance Exception within NTDLL.
* LoadLibrary in the MS PSDK also reveals that it -explicitly- states
*
* Transpose '\' for '/' in the filename.
*/
- p = fspec = apr_pstrdup(r->pool, r->filename);
- while (*p) {
- if (*p == '/')
- *p = '\\';
- ++p;
+ ch = fspec = apr_pstrdup(p, fpath);
+ while (*ch) {
+ if (*ch == '/')
+ *ch = '\\';
+ ++ch;
}
- /* TODO: Critical section
- *
- * Warning: cid should not be allocated from pool if we
- * cache the isapi process in-memory.
- *
- * This code could use cacheing... everything that follows
- * should only be performed on the first isapi dll invocation,
- * not with every HttpExtensionProc()
- */
- *isa = apr_pcalloc(r->pool, sizeof(isapi_module));
- (*isa)->pVer = apr_pcalloc(r->pool, sizeof(HSE_VERSION_INFO));
- (*isa)->refcount = 1;
+ (*isa)->handle = LoadLibraryEx(fspec, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
- /* TODO: These may need to become overrideable, so that we
- * assure a given isapi can be fooled into behaving well.
- */
- (*isa)->timeout = INFINITE; /* microsecs */
- (*isa)->fakeasync = TRUE;
- (*isa)->reportversion = MAKELONG(0, 5); /* Revision 5.0 */
-
- if (!((*isa)->handle = LoadLibraryEx(r->filename, NULL,
- LOAD_WITH_ALTERED_SEARCH_PATH)))
+ if (!(*isa)->handle)
{
apr_status_t rv = GetLastError();
ap_log_rerror(APLOG_MARK, APLOG_ALERT, GetLastError(), r,
- "ISAPI %s failed to load", r->filename);
+ "ISAPI %s failed to load", fpath);
(*isa)->handle = NULL;
- (*isa)->refcount = 0;
return rv;
}
apr_status_t rv = GetLastError();
ap_log_rerror(APLOG_MARK, APLOG_ALERT, rv, r,
"ISAPI %s is missing GetExtensionVersion()",
- r->filename);
+ fpath);
FreeLibrary((*isa)->handle);
(*isa)->handle = NULL;
- (*isa)->refcount = 0;
return rv;
}
apr_status_t rv = GetLastError();
ap_log_rerror(APLOG_MARK, APLOG_ALERT, rv, r,
"ISAPI %s is missing HttpExtensionProc()",
- r->filename);
+ fpath);
FreeLibrary((*isa)->handle);
(*isa)->handle = NULL;
- (*isa)->refcount = 0;
return rv;
}
apr_status_t rv = GetLastError();
ap_log_rerror(APLOG_MARK, APLOG_ALERT, rv, r,
"ISAPI %s call GetExtensionVersion() failed",
- r->filename);
+ fpath);
FreeLibrary((*isa)->handle);
(*isa)->handle = NULL;
- (*isa)->refcount = 0;
return rv;
}
+ ++(*isa)->refcount;
+
return APR_SUCCESS;
}
apr_status_t isapi_handler (request_rec *r)
{
+ isapi_server_conf *sconf = ap_get_module_config(r->server->module_config,
+ &isapi_module);
apr_table_t *e = r->subprocess_env;
apr_status_t rv;
isapi_loaded *isa;
if (r->finfo.filetype != APR_REG)
return HTTP_FORBIDDEN;
- /* Load and cache or retrieve the cached extention */
- if (isapi_load(r, &isa) != APR_SUCCESS)
+ /* Load the isapi extention without caching (sconf == NULL)
+ * but note that we will recover an existing cached module.
+ */
+ if (isapi_load(r->pool, sconf, r, r->filename, &isa) != APR_SUCCESS)
return HTTP_INTERNAL_SERVER_ERROR;
/* Set up variables */
return NULL;
}
+static const char *isapi_cmd_cachefile(cmd_parms *cmd, void *dummy,
+ const char *filename)
+
+{
+ isapi_server_conf *sconf = ap_get_module_config(cmd->server->module_config,
+ &isapi_module);
+ isapi_loaded *isa, **newisa;
+ apr_finfo_t tmp;
+ apr_status_t rv;
+ char *fspec;
+
+ fspec = ap_os_case_canonical_filename(cmd->pool, filename);
+ if (apr_stat(&tmp, fspec, cmd->temp_pool) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
+ "ISAPI: unable to stat(%s), skipping", filename);
+ return NULL;
+ }
+ if (tmp.filetype != APR_REG) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
+ "ISAPI: %s isn't a regular file, skipping", filename);
+ return NULL;
+ }
+
+ /* Load the extention as cached (passing sconf) */
+ rv = isapi_load(cmd->pool, sconf, NULL, fspec, &isa);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, rv, cmd->server,
+ "ISAPI: unable to cache %s, skipping", filename);
+ return NULL;
+ }
+
+ /* Add to cached list of loaded modules */
+ newisa = apr_push_array(sconf->loaded);
+ *newisa = isa;
+
+ return NULL;
+}
+
+static void isapi_hooks(void)
+{
+ ap_hook_post_config(isapi_post_config, NULL, NULL, AP_HOOK_MIDDLE);
+}
+
static const command_rec isapi_cmds[] = {
-{ "ISAPIReadAheadBuffer", isapi_cmd_readaheadbuffer, NULL, RSRC_CONF, TAKE1,
- "Maximum bytes to initially pass to the ISAPI handler" },
-{ "ISAPILogNotSupported", isapi_cmd_lognotsupported, NULL, RSRC_CONF, TAKE1,
- "Log requests not supported by the ISAPI server" },
-{ "ISAPIAppendLogToErrors", isapi_cmd_appendlogtoerrors, NULL, RSRC_CONF, TAKE1,
- "Send all Append Log requests to the error log" },
-{ "ISAPIAppendLogToQuery", isapi_cmd_appendlogtoquery, NULL, RSRC_CONF, TAKE1,
- "Append Log requests are concatinated to the query args" },
+AP_INIT_TAKE1("ISAPIReadAheadBuffer", isapi_cmd_readaheadbuffer, NULL, RSRC_CONF,
+ "Maximum bytes to initially pass to the ISAPI handler"),
+AP_INIT_TAKE1("ISAPILogNotSupported", isapi_cmd_lognotsupported, NULL, RSRC_CONF,
+ "Log requests not supported by the ISAPI server"),
+AP_INIT_TAKE1("ISAPIAppendLogToErrors", isapi_cmd_appendlogtoerrors, NULL, RSRC_CONF,
+ "Send all Append Log requests to the error log"),
+AP_INIT_TAKE1("ISAPIAppendLogToQuery", isapi_cmd_appendlogtoquery, NULL, RSRC_CONF,
+ "Append Log requests are concatinated to the query args"),
+AP_INIT_ITERATE("ISAPICacheFile", isapi_cmd_cachefile, NULL, RSRC_CONF,
+ "Cache the specified ISAPI extension in-process"),
{ NULL }
};
NULL, /* merge server config */
isapi_cmds, /* command apr_table_t */
isapi_handlers, /* handlers */
- NULL /* register hooks */
+ isapi_hooks /* register hooks */
};