From 134a70e4bda32bf8d6138cfd2a7b932924d6b998 Mon Sep 17 00:00:00 2001 From: "William A. Rowe Jr" Date: Tue, 3 Oct 2000 06:08:36 +0000 Subject: [PATCH] Ok... I've been waiting to find out what happens when we commit to two trees at once. To that end, this is the caching and rather stable 2.0 mod_isapi, with the docs brought over from 1.3 and freshened a bit. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@86373 13f79535-47bb-0310-9956-ffa450edef68 --- docs/manual/mod/directives.html | 5 + docs/manual/mod/mod_isapi.html | 371 +++++++++++++++++++++++++++++--- modules/arch/win32/mod_isapi.c | 199 ++++++++++++----- os/win32/mod_isapi.c | 199 ++++++++++++----- 4 files changed, 632 insertions(+), 142 deletions(-) diff --git a/docs/manual/mod/directives.html b/docs/manual/mod/directives.html index 11d70813c3..76135a7a33 100644 --- a/docs/manual/mod/directives.html +++ b/docs/manual/mod/directives.html @@ -129,6 +129,11 @@ of the terms used in their descriptions available.
  • Include
  • IndexIgnore
  • IndexOptions +
  • ISAPIAppendLogToErrors +
  • ISAPIAppendLogToQuery +
  • ISAPIFileCache +
  • ISAPILogNotSupported +
  • ISAPIReadAheadBuffer
  • KeepAlive
  • KeepAliveTimeout
  • LanguagePriority diff --git a/docs/manual/mod/mod_isapi.html b/docs/manual/mod/mod_isapi.html index 207611cfc4..aefeb4d950 100644 --- a/docs/manual/mod/mod_isapi.html +++ b/docs/manual/mod/mod_isapi.html @@ -16,57 +16,360 @@

    Module mod_isapi

    -

    This module is contained in the mod_isapi.c file, and is - compiled in by default. It provides support for ISAPI Extensions when - running under Microsoft Windows. Any document with a handler of - isapi-isa will be processed by this module.

    +

    This module supports ISAPI Extensions within Apache for Windows.

    -

    Purpose

    +

    Status: Base +
    +Source File: mod_isapi.c +
    +Module Identifier: isapi_module +
    +Compatibility: WIN32 only +

    -

    This module implements the ISAPI handler extension API, version 2.0. - It allows Internet Server Applications (i.e., ISAPI handlers) - to be used with Apache for Windows.

    +

    Summary

    + +

    This module implements the Internet Server extension API. It allows + Internet Server extensions (e.g. ISAPI .dll modules) to be + served by Apache for Windows, subject to the noted restrictions.

    + +

    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. Please do not + post such problems to Apache's lists or bug reporting pages.

    Usage

    -

    In the server configuration file, add a handler called - isapi-isa, and map it to files with a .DLL - extension. In other words:

    +

    In the server configuration file, use the AddHandler directive to + associate ISAPI files with the isapi-isa 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:

    +
         AddHandler isapi-isa dll
     
    -

    Now simply place the ISA DLLs into your document root, and they will - be loaded when their URLs are accessed.

    -

    ISAPI Extensions are governed by the same restrictions as CGI - scripts. That is, Options ExecCGI must be active in the - directory that contains the ISA.

    +

    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: + +

    +    ISAPICacheFile c:/WebWork/Scripts/ISAPI/mytest.dll
    +
    + +

    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, Options ExecCGI must be set for + the directory that contains the ISAPI .dll file.

    + +

    Review the Additional Notes and the + Programmer's Journal for additional details and + clarification of the specific ISAPI support offered by mod_isapi.

    + +

    Directives

    + +
    + +

    ISAPIFileCache directive

    + +Syntax: ISAPIFileCache file [file...]
    +Context: server config
    +Override: None
    +Status: Base
    +Module: mod_isapi
    +Compatibility: Apache 2.0 and later, Win32 only

    + + 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. +

    +


    + +

    ISAPIReadAheadBuffer directive

    + +Syntax: ISAPIReadAheadBuffer size
    +Default: 49152
    +Context: server config
    +Override: None
    +Status: Base
    +Module: mod_isapi
    +Compatibility: Apache 1.3.13 and later, Win32 only

    + -

    Notes

    + 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. +

    +


    + +

    ISAPILogNotSupported directive

    + +Syntax: ISAPILogNotSupported on|off
    +Default: on
    +Context: server config
    +Override: None
    +Status: Base
    +Module: mod_isapi
    +Compatibility: Apache 1.3.13 and later, Win32 only

    + + 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. +

    +


    + +

    ISAPIAppendLogToErrors directive

    + +Syntax: ISAPIAppendLogToErrors on|off
    +Default: off
    +Context: server config
    +Override: None
    +Status: Base
    +Module: mod_isapi
    +Compatibility: Apache 1.3.13 and later, Win32 only

    + + Record HSE_APPEND_LOG_PARAMETER requests from ISAPI extentions + to the server error log. +

    +


    + +

    ISAPIAppendLogToQuery directive

    + +Syntax: ISAPIAppendLogToQuery on|off
    +Default: off
    +Context: server config
    +Override: None
    +Status: Base
    +Module: mod_isapi
    +Compatibility: Apache 1.3.13 and later, Win32 only

    + + Record HSE_APPEND_LOG_PARAMETER requests from ISAPI extentions + to the query field (appended to the CustomLog %q component). +

    +


    + +

    Additional Notes

    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 ISAPILogNotSupported Off + exists to quiet this noise.

    + +

    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.

    + +

    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.

    + +

    Programmer's Journal

    + +

    If you are programming Apache 2.0 mod_isapi modules, you must limit your + calls to ServerSupportFunction to the following directives:

    + +
    +
    HSE_REQ_SEND_URL_REDIRECT_RESP +
    Redirect the user to another location.
    + This must be a fully qualified URL (e.g. http://server/location). +
    HSE_REQ_SEND_URL +
    Redirect the user to another location.
    + This cannot be a fully qualified URL, you are not allowed + to pass the protocol or a server name (e.g. simply /location).
    + This redirection is handled by the server, not the browser.
    + Warning: 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. +
    HSE_REQ_SEND_RESPONSE_HEADER +
    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. +
    HSE_REQ_DONE_WITH_SESSION +
    Apache considers this a no-op, since the session will be finished + when the ISAPI returns from processing. +
    HSE_REQ_MAP_URL_TO_PATH +
    Apache will translate a virtual name to a physical name. +
    HSE_APPEND_LOG_PARAMETER +
    This logged message may be captured in any of the following logs: +
      +
    • in the \"%{isapi-parameter}n\" component in a CustomLog directive +
    • in the %q log component with the ISAPIAppendLogToQuery On directive +
    • in the error log with the ISAPIAppendLogToErrors On directive +
    + The first option, the %{isapi-parameter}n component, is always available + and prefered. +
    HSE_REQ_IS_KEEP_CONN +
    Will return the negotiated Keep-Alive status. +
    HSE_REQ_SEND_RESPONSE_HEADER_EX +
    Will behave as documented, although the fKeepConn flag is ignored. +
    HSE_REQ_IS_CONNECTED +
    Will report false if the request has been aborted. +
    + +

    Apache returns FALSE to any unsupported call to ServerSupportFunction, and + sets the GetLastError value to ERROR_INVALID_PARAMETER.

    -

    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. +

    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.

    -

    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. +

    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.

    -

    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.

    +

    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.

    +

    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.

    diff --git a/modules/arch/win32/mod_isapi.c b/modules/arch/win32/mod_isapi.c index 21504d3839..6e7e39fb01 100644 --- a/modules/arch/win32/mod_isapi.c +++ b/modules/arch/win32/mod_isapi.c @@ -135,6 +135,7 @@ typedef struct { /* Our loaded isapi module description structure */ typedef struct { + const char *filename; HINSTANCE handle; HSE_VERSION_INFO *pVer; PFN_GETEXTENSIONVERSION GetExtensionVersion; @@ -158,20 +159,19 @@ typedef struct { 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); @@ -180,7 +180,7 @@ static apr_status_t cleanup_isapi_server_config(void *sconfv) 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); @@ -195,10 +195,73 @@ static void *create_isapi_server_config(apr_pool_t *p, server_rec *s) 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 @@ -206,41 +269,21 @@ static apr_status_t isapi_load(request_rec *r, isapi_loaded** isa) * * 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; } @@ -250,10 +293,9 @@ static apr_status_t isapi_load(request_rec *r, isapi_loaded** isa) 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; } @@ -263,10 +305,9 @@ static apr_status_t isapi_load(request_rec *r, isapi_loaded** isa) 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; } @@ -280,13 +321,14 @@ static apr_status_t isapi_load(request_rec *r, isapi_loaded** isa) 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; } @@ -312,6 +354,8 @@ static int isapi_unload(isapi_loaded* isa, int force) 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; @@ -332,8 +376,10 @@ apr_status_t isapi_handler (request_rec *r) 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 */ @@ -1068,15 +1114,60 @@ static const char *isapi_cmd_appendlogtoquery(cmd_parms *cmd, void *config, 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 } }; @@ -1093,5 +1184,5 @@ module isapi_module = { NULL, /* merge server config */ isapi_cmds, /* command apr_table_t */ isapi_handlers, /* handlers */ - NULL /* register hooks */ + isapi_hooks /* register hooks */ }; diff --git a/os/win32/mod_isapi.c b/os/win32/mod_isapi.c index 21504d3839..6e7e39fb01 100644 --- a/os/win32/mod_isapi.c +++ b/os/win32/mod_isapi.c @@ -135,6 +135,7 @@ typedef struct { /* Our loaded isapi module description structure */ typedef struct { + const char *filename; HINSTANCE handle; HSE_VERSION_INFO *pVer; PFN_GETEXTENSIONVERSION GetExtensionVersion; @@ -158,20 +159,19 @@ typedef struct { 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); @@ -180,7 +180,7 @@ static apr_status_t cleanup_isapi_server_config(void *sconfv) 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); @@ -195,10 +195,73 @@ static void *create_isapi_server_config(apr_pool_t *p, server_rec *s) 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 @@ -206,41 +269,21 @@ static apr_status_t isapi_load(request_rec *r, isapi_loaded** isa) * * 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; } @@ -250,10 +293,9 @@ static apr_status_t isapi_load(request_rec *r, isapi_loaded** isa) 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; } @@ -263,10 +305,9 @@ static apr_status_t isapi_load(request_rec *r, isapi_loaded** isa) 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; } @@ -280,13 +321,14 @@ static apr_status_t isapi_load(request_rec *r, isapi_loaded** isa) 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; } @@ -312,6 +354,8 @@ static int isapi_unload(isapi_loaded* isa, int force) 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; @@ -332,8 +376,10 @@ apr_status_t isapi_handler (request_rec *r) 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 */ @@ -1068,15 +1114,60 @@ static const char *isapi_cmd_appendlogtoquery(cmd_parms *cmd, void *config, 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 } }; @@ -1093,5 +1184,5 @@ module isapi_module = { NULL, /* merge server config */ isapi_cmds, /* command apr_table_t */ isapi_handlers, /* handlers */ - NULL /* register hooks */ + isapi_hooks /* register hooks */ }; -- 2.50.1