You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by wr...@apache.org on 2002/05/14 07:23:54 UTC

cvs commit: httpd-2.0/modules/arch/win32 mod_isapi.h mod_isapi.c mod_isapi.dsp

wrowe       02/05/13 22:23:54

  Modified:    modules/arch/win32 mod_isapi.c mod_isapi.dsp
  Added:       modules/arch/win32 mod_isapi.h
  Log:
    Now for the major refactoring.  Introduce mod_isapi.h, as we have had
    untold problems with compatibility between different Visual Studio and
    PlatformSDK users [based on their generation of the MS ISAPI header.]
    This header is coded from scratch in native APR types.
  
    Replace the apr_array_t with an apr_hash_t, mutex protected, to allow us
    to preload and late-load isapi modules.  This closes a significant bug
    with trafficed sites using uncached isapi modules, where one would be
    closing the module just as another request is opening it [as if for the
    first time.]  This would cause some ISAPI modules to crash.  Now that
    we load modules for the lifetime of the server, a simple graceful restart
    is all that's required to unload all the non-precached modules.
  
    Crack out all FAKE_ASYNC code while we complete it.  Total support for
    the Completion Context callback and end-of-request termination is required
    before we can toggle this on.
  
    My next commit; complete fake async support.  But this was all I'd had
    in me for one night.
  
  Revision  Changes    Path
  1.68      +447 -285  httpd-2.0/modules/arch/win32/mod_isapi.c
  
  Index: mod_isapi.c
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/arch/win32/mod_isapi.c,v
  retrieving revision 1.67
  retrieving revision 1.68
  diff -u -r1.67 -r1.68
  --- mod_isapi.c	13 May 2002 22:44:40 -0000	1.67
  +++ mod_isapi.c	14 May 2002 05:23:54 -0000	1.68
  @@ -58,26 +58,25 @@
   
   /*
    * mod_isapi.c - Internet Server Application (ISA) module for Apache
  - * by Alexei Kosut <ak...@apache.org>
  + * by Alexei Kosut <ak...@apache.org>, significant overhauls and
  + * redesign by William Rowe <wr...@covalent.net>, and hints from many
  + * other developer/users who have hit on specific flaws.
    *
  - * This module implements Microsoft's ISAPI, allowing Apache (when running
  - * under Windows) to load Internet Server Applications (ISAPI extensions).
  - * It implements all of the ISAPI 2.0 specification, except for the 
  - * "Microsoft-only" extensions dealing with asynchronous I/O. All ISAPI
  - * extensions that use only synchronous I/O and are compatible with the
  - * ISAPI 2.0 specification should work (most ISAPI 1.0 extensions should
  - * function as well).
  + * This module implements the ISAPI Handler architecture, allowing 
  + * Apache to load Internet Server Applications (ISAPI extensions),
  + * similar to the support in IIS, Zope, O'Reilly's WebSite and others.
    *
  - * To load, simply place the ISA in a location in the document tree.
  - * Then add an "AddHandler isapi-isa dll" into your config file.
  - * You should now be able to load ISAPI DLLs just be reffering to their
  - * URLs. Make sure the ExecCGI option is active in the directory
  - * the ISA is in.
  + * It is a complete implementation of the ISAPI 2.0 specification, 
  + * except for "Microsoft extensions" to the API which provide 
  + * asynchronous I/O.  It is further extended to include additional
  + * "Microsoft extentions" through IIS 5.0, with some deficiencies
  + * where one-to-one mappings don't exist.
  + *
  + * Refer to /manual/mod/mod_isapi.html for additional details on
  + * configuration and use, but check this source for specific support
  + * of the API, 
    */
   
  -#include "apr_strings.h"
  -#include "apr_portable.h"
  -#include "apr_buckets.h"
   #include "ap_config.h"
   #include "httpd.h"
   #include "http_config.h"
  @@ -87,18 +86,17 @@
   #include "http_log.h"
   #include "util_script.h"
   #include "mod_core.h"
  +#include "apr_strings.h"
  +#include "apr_portable.h"
  +#include "apr_buckets.h"
  +#include "apr_thread_mutex.h"
  +#include "apr_thread_rwlock.h"
  +#include "apr_hash.h"
  +#include "mod_isapi.h"
   
  -/* We use the exact same header file as the original */
  -#include <HttpExt.h>
  +/* Retry frequency for a failed-to-load isapi .dll */
  +#define ISAPI_RETRY ( 30 * APR_USEC_PER_SEC )
   
  -#if !defined(HSE_REQ_MAP_URL_TO_PATH_EX) \
  - || !defined(HSE_REQ_SEND_RESPONSE_HEADER_EX)
  -#pragma message("WARNING: This build of Apache is missing the recent changes")
  -#pragma message("in the Microsoft Win32 Platform SDK; some mod_isapi features")
  -#pragma message("will be disabled.  To obtain the latest Platform SDK files,")
  -#pragma message("please refer to:")
  -#pragma message("http://msdn.microsoft.com/downloads/sdks/platform/platform.asp")
  -#endif
   
   /**********************************************************
    *
  @@ -110,12 +108,6 @@
   
   #define ISAPI_UNDEF -1
   
  -/* Our isapi global config values */
  -static struct isapi_global_conf {
  -    apr_pool_t         *pool;
  -    apr_array_header_t *modules;
  -} loaded;
  -
   /* Our isapi per-dir config structure */
   typedef struct isapi_dir_conf {
       int read_ahead_buflen;
  @@ -126,8 +118,8 @@
   
   typedef struct isapi_loaded isapi_loaded;
   
  -static apr_status_t isapi_load(apr_pool_t *p, request_rec *r, 
  -                               const char *fpath, isapi_loaded** isa);
  +apr_status_t isapi_lookup(apr_pool_t *p, server_rec *s, request_rec *r, 
  +                          const char *fpath, isapi_loaded** isa);
   
   static void *create_isapi_dir_config(apr_pool_t *p, char *dummy)
   {
  @@ -166,41 +158,45 @@
   static const char *isapi_cmd_cachefile(cmd_parms *cmd, void *dummy, 
                                          const char *filename)
   {
  -    isapi_loaded *isa, **newisa;
  +    isapi_loaded *isa;
       apr_finfo_t tmp;
       apr_status_t rv;
       char *fspec;
  -    
  +
  +    /* ### Just an observation ... it would be terribly cool to be
  +     * able to use this per-dir, relative to the directory block being
  +     * defined.  The hash result remains global, but shorthand of
  +     * <Directory "c:/webapps/isapi">
  +     *     ISAPICacheFile myapp.dll anotherapp.dll thirdapp.dll
  +     * </Directory>
  +     * would be very convienent.
  +     */
       fspec = ap_server_root_relative(cmd->pool, filename);
       if (!fspec) {
   	ap_log_error(APLOG_MARK, APLOG_WARNING, APR_EBADPATH, cmd->server,
  -	             "ISAPI: Invalid module path %s, skipping", filename);
  +	             "ISAPI: invalid module path, skipping %s", filename);
   	return NULL;
       }
       if ((rv = apr_stat(&tmp, fspec, APR_FINFO_TYPE, 
                         cmd->temp_pool)) != APR_SUCCESS) { 
   	ap_log_error(APLOG_MARK, APLOG_WARNING, rv, cmd->server,
  -	    "ISAPI: unable to stat(%s), skipping", fspec);
  +	    "ISAPI: unable to stat, skipping %s", fspec);
   	return NULL;
       }
       if (tmp.filetype != APR_REG) {
   	ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, cmd->server,
  -	    "ISAPI: %s isn't a regular file, skipping", fspec);
  +	    "ISAPI: not a regular file, skipping %s", fspec);
   	return NULL;
       }
   
  -    /* Load the extention as cached (passing NULL for r) */
  -    rv = isapi_load(loaded.pool, NULL, fspec, &isa); 
  +    /* Load the extention as cached (with null request_rec) */
  +    rv = isapi_lookup(cmd->pool, cmd->server, NULL, fspec, &isa); 
       if (rv != APR_SUCCESS) {
           ap_log_error(APLOG_MARK, APLOG_WARNING, rv, cmd->server,
  -                     "ISAPI: unable to cache %s, skipping", fspec);
  +                     "ISAPI: unable to cache, skipping %s", fspec);
   	return NULL;
       }
   
  -    /* Add to cached list of loaded modules */
  -    newisa = apr_array_push(loaded.modules);
  -    *newisa = isa;
  -    
       return NULL;
   }
   
  @@ -228,142 +224,283 @@
    *
    **********************************************************/
   
  +/* Our isapi global config values */
  +static struct isapi_global_conf {
  +    apr_pool_t         *pool;
  +    apr_thread_mutex_t *lock;
  +    apr_hash_t         *hash;
  +} loaded;
  +
   /* Our loaded isapi module description structure */
   struct isapi_loaded {
  -    const char *filename;
  -    apr_dso_handle_t *handle;
  -    HSE_VERSION_INFO *pVer;
  +    const char          *filename;
  +    apr_thread_rwlock_t *in_progress;
  +    apr_status_t         last_load_rv;
  +    apr_time_t           last_load_time;
  +    apr_dso_handle_t    *handle;
  +    HSE_VERSION_INFO    *isapi_version;
  +    apr_uint32_t         report_version;
       PFN_GETEXTENSIONVERSION GetExtensionVersion;
       PFN_HTTPEXTENSIONPROC   HttpExtensionProc;
       PFN_TERMINATEEXTENSION  TerminateExtension;
  -    int   refcount;
  -    DWORD timeout;
  -    BOOL  fakeasync;
  -    DWORD report_version;
  +#ifdef FAKE_ASYNC
  +    int                  fakeasync;
  +    apr_uint32_t         timeout;
  +#endif
   };
   
  -static apr_status_t isapi_unload(isapi_loaded* isa, int force)
  +static apr_status_t isapi_unload(isapi_loaded *isa, int force)
   {
       /* All done with the DLL... get rid of it...
        *
  -     * If optionally cached, pass HSE_TERM_ADVISORY_UNLOAD,
  -     * and if it returns TRUE, unload, otherwise, cache it.
  +     * If optionally cached, and we weren't asked to force the unload,
  +     * pass HSE_TERM_ADVISORY_UNLOAD, and if it returns 1, unload, 
  +     * otherwise, leave it alone (it didn't choose to cooperate.)
        */
  -    if (((--isa->refcount > 0) && !force) || !isa->handle)
  +    if (!isa->handle) {
           return APR_SUCCESS;
  +    }
       if (isa->TerminateExtension) {
  -        if (force)
  +        if (force) {
               (*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD);
  -        else if (!(*isa->TerminateExtension)(HSE_TERM_ADVISORY_UNLOAD))
  +        }
  +        else if (!(*isa->TerminateExtension)(HSE_TERM_ADVISORY_UNLOAD)) {
               return APR_EGENERAL;
  +        }
       }
       apr_dso_unload(isa->handle);
       isa->handle = NULL;
       return APR_SUCCESS;
   }
   
  -static apr_status_t cleanup_isapi(void *isa)
  +static apr_status_t cleanup_isapi(void *isa_)
   {
  -    return isapi_unload((isapi_loaded*) isa, TRUE);
  +    isapi_loaded* isa = (isapi_loaded*) isa_;
  +
  +    /* We must force the module to unload, we are about 
  +     * to lose the isapi structure's allocation entirely.
  +     */
  +    return isapi_unload(isa, 1);
   }
   
  -static apr_status_t isapi_load(apr_pool_t *p, request_rec *r, 
  -                               const char *fpath, isapi_loaded** isa)
  +static apr_status_t isapi_load(apr_pool_t *p, server_rec *s, request_rec *r, isapi_loaded *isa)
   {
  -    isapi_loaded **found = (isapi_loaded **)loaded.modules->elts;
       apr_status_t rv;
  -    int n;
   
  -    for (n = 0; n < loaded.modules->nelts; ++n) {
  -        if (strcasecmp(fpath, (*found)->filename) == 0) {
  -            break;
  -        }
  -        ++found;
  -    }
  -    
  -    if (n < loaded.modules->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)->report_version = MAKELONG(0, 5); /* Revision 5.0 */
  -    }
  +    isa->isapi_version = apr_pcalloc(p, sizeof(HSE_VERSION_INFO));
  +
  +    /* TODO: These aught to become overrideable, so that we
  +     * assure a given isapi can be fooled into behaving well.
  +     *
  +     * The tricky bit, they aren't really a per-dir sort of
  +     * config, they will always be constant across every 
  +     * reference to the .dll no matter what context (vhost,
  +     * location, etc) they apply to.
  +     */
  +    isa->report_version = MAKELONG(0, 5); /* Revision 5.0 */
  +#ifdef FAKE_ASYNC
  +    isa->timeout = INFINITE; /* microsecs */
  +    isa->fakeasync = 1;
  +#endif
       
  -    rv = apr_dso_load(&(*isa)->handle, fpath, p);
  +    rv = apr_dso_load(&isa->handle, isa->filename, p);
       if (rv)
       {
  -        ap_log_rerror(APLOG_MARK, APLOG_ALERT, rv, r,
  -                      "ISAPI: %s failed to load", fpath);
  -        (*isa)->handle = NULL;
  +        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
  +                     "ISAPI: failed to load %s", isa->filename);
  +        isa->handle = NULL;
           return rv;
       }
   
  -    rv = apr_dso_sym((void**)&(*isa)->GetExtensionVersion, (*isa)->handle,
  +    rv = apr_dso_sym((void**)&isa->GetExtensionVersion, isa->handle,
                        "GetExtensionVersion");
       if (rv)
       {
  -        ap_log_rerror(APLOG_MARK, APLOG_ALERT, rv, r,
  -                      "ISAPI: %s is missing GetExtensionVersion()",
  -                      fpath);
  -        apr_dso_unload((*isa)->handle);
  -        (*isa)->handle = NULL;
  +        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
  +                     "ISAPI: missing GetExtensionVersion() in %s",
  +                     isa->filename);
  +        apr_dso_unload(isa->handle);
  +        isa->handle = NULL;
           return rv;
       }
   
  -    rv = apr_dso_sym((void**)&(*isa)->HttpExtensionProc, (*isa)->handle,
  +    rv = apr_dso_sym((void**)&isa->HttpExtensionProc, isa->handle,
                        "HttpExtensionProc");
       if (rv)
       {
  -        ap_log_rerror(APLOG_MARK, APLOG_ALERT, rv, r,
  -                      "ISAPI: %s is missing HttpExtensionProc()",
  -                      fpath);
  -        apr_dso_unload((*isa)->handle);
  -        (*isa)->handle = NULL;
  +        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
  +                     "ISAPI: missing HttpExtensionProc() in %s",
  +                     isa->filename);
  +        apr_dso_unload(isa->handle);
  +        isa->handle = NULL;
           return rv;
       }
   
       /* TerminateExtension() is an optional interface */
  -    rv = apr_dso_sym((void**)&(*isa)->TerminateExtension, (*isa)->handle,
  +    rv = apr_dso_sym((void**)&isa->TerminateExtension, isa->handle,
                        "TerminateExtension");
       SetLastError(0);
   
       /* Run GetExtensionVersion() */
  -    if (!((*isa)->GetExtensionVersion)((*isa)->pVer)) {
  +    if (!(isa->GetExtensionVersion)(isa->isapi_version)) {
           apr_status_t rv = apr_get_os_error();
  -        ap_log_rerror(APLOG_MARK, APLOG_ALERT, rv, r,
  -                      "ISAPI: %s call GetExtensionVersion() failed", 
  -                      fpath);
  -        apr_dso_unload((*isa)->handle);
  -        (*isa)->handle = NULL;
  +        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
  +                     "ISAPI: failed call to GetExtensionVersion() in %s", 
  +                     isa->filename);
  +        apr_dso_unload(isa->handle);
  +        isa->handle = NULL;
           return rv;
       }
   
  -    ++(*isa)->refcount;
  -
  -    apr_pool_cleanup_register(p, *isa, cleanup_isapi, 
  -                                   apr_pool_cleanup_null);
  +    apr_pool_cleanup_register(p, isa, cleanup_isapi, 
  +                              apr_pool_cleanup_null);
   
       return APR_SUCCESS;
   }
   
  +apr_status_t isapi_lookup(apr_pool_t *p, server_rec *s, request_rec *r, 
  +                          const char *fpath, isapi_loaded** isa)
  +{
  +    apr_status_t rv;
  +    const char *key;
  +
  +    if ((rv = apr_thread_mutex_lock(loaded.lock)) != APR_SUCCESS) {
  +        return rv;
  +    }
  +
  +    *isa = apr_hash_get(loaded.hash, fpath, APR_HASH_KEY_STRING);
  +
  +    if (*isa) {
  +
  +        /* If we find this lock exists, use a set-aside copy of gainlock
  +         * to avoid race conditions on NULLing the in_progress variable 
  +         * when the load has completed.  Release the global isapi hash
  +         * lock so other requests can proceed, then rdlock for completion
  +         * of loading our desired dll or wrlock if we would like to retry
  +         * loading the dll (because last_load_rv failed and retry is up.)
  +         */
  +        apr_thread_rwlock_t *gainlock = (*isa)->in_progress;
  +
  +        /* gainlock is NULLed after the module loads successfully.
  +         * This free-threaded module can be used without any locking.
  +         */
  +        if (!gainlock) {
  +            rv = (*isa)->last_load_rv;
  +            apr_thread_mutex_unlock(loaded.lock);
  +            return rv;
  +        }
  +
  +            
  +        if ((*isa)->last_load_rv == APR_SUCCESS) {
  +            apr_thread_mutex_unlock(loaded.lock);
  +            if ((rv = apr_thread_rwlock_rdlock(gainlock)) 
  +                    != APR_SUCCESS) {
  +                return rv;
  +            }
  +            rv = (*isa)->last_load_rv;
  +            apr_thread_rwlock_unlock(gainlock);
  +            return rv;
  +        }
  +
  +        if (apr_time_now() > (*isa)->last_load_time + ISAPI_RETRY) {
  +        
  +            /* Remember last_load_time before releasing the global
  +             * hash lock to avoid colliding with another thread
  +             * that hit this exception at the same time as our
  +             * retry attempt, since we unlock the global mutex
  +             * before attempting a write lock for this module.
  +             */
  +            apr_time_t check_time = (*isa)->last_load_time;
  +            apr_thread_mutex_unlock(loaded.lock);
  +
  +            if ((rv = apr_thread_rwlock_wrlock(gainlock)) 
  +                    != APR_SUCCESS) {
  +                return rv;
  +            }
  +
  +            /* If last_load_time is unchanged, we still own this
  +             * retry, otherwise presume another thread provided 
  +             * our retry (for good or ill).  Relock the global
  +             * hash for updating last_load_ vars, so their update
  +             * is always atomic to the global lock.
  +             */
  +            if (check_time == (*isa)->last_load_time) {
  +
  +                rv = isapi_load(loaded.pool, r->server, r, *isa);
  +
  +                apr_thread_mutex_lock(loaded.lock);
  +                (*isa)->last_load_rv = rv;
  +                (*isa)->last_load_time = apr_time_now();
  +                apr_thread_mutex_unlock(loaded.lock);
  +            }
  +            else {
  +                rv = (*isa)->last_load_rv;
  +            }
  +            apr_thread_rwlock_unlock(gainlock);
  +
  +            return rv;
  +        }
  +
  +        /* We haven't hit timeup on retry, let's grab the last_rv
  +         * within the hash mutex before unlocking.
  +         */
  +        rv = (*isa)->last_load_rv;
  +        apr_thread_mutex_unlock(loaded.lock);
  +
  +        return rv;
  +    }
  +
  +    /* If the module was not found, it's time to create a hash key entry
  +     * before releasing the hash lock to avoid multiple threads from 
  +     * loading the same module.
  +     */
  +    key = apr_pstrdup(loaded.pool, fpath);
  +    *isa = apr_pcalloc(loaded.pool, sizeof(isapi_loaded));
  +    (*isa)->filename = key;
  +    if (r) {
  +        /* A mutex that exists only long enough to attempt to
  +         * load this isapi dll, the release this module to all
  +         * other takers that came along during the one-time
  +         * load process.  Short lifetime for this lock would
  +         * be great, however, using r->pool is nasty if those
  +         * blocked on the lock haven't all unlocked before we
  +         * attempt to destroy.  A nastier race condition than
  +         * I want to deal with at this moment...
  +         */
  +        apr_thread_rwlock_create(&(*isa)->in_progress, loaded.pool);
  +        apr_thread_rwlock_wrlock((*isa)->in_progress);
  +    }
  +
  +    apr_hash_set(loaded.hash, key, APR_HASH_KEY_STRING, *isa);
  +    
  +    /* Now attempt to load the isapi on our own time, 
  +     * allow other isapi processing to resume.
  +     */
  +    apr_thread_mutex_unlock(loaded.lock);
  +
  +    rv = isapi_load(loaded.pool, r->server, r, *isa);
  +    (*isa)->last_load_time = apr_time_now();
  +    (*isa)->last_load_rv = rv;
  +
  +    if (r && (rv == APR_SUCCESS)) {
  +        /* Let others who are blocked on this particular
  +         * module resume their requests, for better or worse.
  +         */
  +        apr_thread_rwlock_t *unlock = (*isa)->in_progress;
  +        (*isa)->in_progress = NULL;
  +        apr_thread_rwlock_unlock(unlock);
  +    }
  +    else if (!r && (rv != APR_SUCCESS)) {
  +        /* We must leave a rwlock around for requests to retry
  +         * loading this dll after timeup... since we were in 
  +         * the setup code we had avoided creating this lock.
  +         */
  +        apr_thread_rwlock_create(&(*isa)->in_progress, loaded.pool);
  +    }
  +
  +    return (*isa)->last_load_rv;
  +}
  +
   /**********************************************************
    *
    *  ISAPI Module request callbacks section
  @@ -372,21 +509,25 @@
   
   /* Our "Connection ID" structure */
   typedef struct isapi_cid {
  -    LPEXTENSION_CONTROL_BLOCK ecb;
  +    EXTENSION_CONTROL_BLOCK *ecb;
       isapi_dir_conf dconf;
       isapi_loaded *isa;
       request_rec  *r;
  +#ifdef FAKE_ASYNC
       PFN_HSE_IO_COMPLETION completion;
       PVOID  completion_arg;
       HANDLE complete;
  +#endif
   } isapi_cid;
   
  -BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
  -                               LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer)
  +int APR_THREAD_FUNC GetServerVariable (isapi_cid *cid, 
  +                                       char *lpszVariableName,
  +                                       void *lpvBuffer, 
  +                                       apr_uint32_t *lpdwSizeofBuffer)
   {
  -    request_rec *r = ((isapi_cid *)hConn)->r;
  +    request_rec *r = cid->r;
       const char *result;
  -    DWORD len;
  +    apr_uint32_t len;
   
       if (!strcmp(lpszVariableName, "ALL_HTTP")) 
       {
  @@ -406,7 +547,7 @@
           if (*lpdwSizeofBuffer < len + 1) {
               *lpdwSizeofBuffer = len + 1;
               SetLastError(ERROR_INSUFFICIENT_BUFFER);
  -            return FALSE;
  +            return 0;
           }
       
           for (i = 0; i < arr->nelts; i++) {
  @@ -422,7 +563,7 @@
   
           *(((char*)lpvBuffer)++) = '\0';
           *lpdwSizeofBuffer = len;
  -        return TRUE;
  +        return 1;
       }
       
       if (!strcmp(lpszVariableName, "ALL_RAW")) 
  @@ -441,7 +582,7 @@
           if (*lpdwSizeofBuffer < len + 1) {
               *lpdwSizeofBuffer = len + 1;
               SetLastError(ERROR_INSUFFICIENT_BUFFER);
  -            return FALSE;
  +            return 0;
           }
       
           for (i = 0; i < arr->nelts; i++) {
  @@ -455,7 +596,7 @@
           }
           *(((char*)lpvBuffer)++) = '\0';
           *lpdwSizeofBuffer = len;
  -        return TRUE;
  +        return 1;
       }
       
       /* Not a special case */
  @@ -466,20 +607,22 @@
           if (*lpdwSizeofBuffer < len + 1) {
               *lpdwSizeofBuffer = len + 1;
               SetLastError(ERROR_INSUFFICIENT_BUFFER);
  -            return FALSE;
  +            return 0;
           }
           strcpy(lpvBuffer, result);
           *lpdwSizeofBuffer = len;
  -        return TRUE;
  +        return 1;
       }
   
       /* Not Found */
       SetLastError(ERROR_INVALID_INDEX);
  -    return FALSE;
  +    return 0;
   }
   
  -BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpdwBytes,
  -                         DWORD dwReserved)
  +int APR_THREAD_FUNC WriteClient(isapi_cid *ConnID, 
  +                                void *Buffer, 
  +                                apr_uint32_t *lpdwBytes, 
  +                                apr_uint32_t dwReserved)
   {
       request_rec *r = ((isapi_cid *)ConnID)->r;
       conn_rec *c = r->connection;
  @@ -496,13 +639,15 @@
       APR_BRIGADE_INSERT_TAIL(bb, b);
       ap_pass_brigade(r->output_filters, bb);
   
  -    return TRUE;
  +    return 1;
   }
   
  -BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize)
  +int APR_THREAD_FUNC ReadClient(isapi_cid *ConnID, 
  +                               void *lpvBuffer, 
  +                               apr_uint32_t *lpdwSize)
   {
       request_rec *r = ((isapi_cid *)ConnID)->r;
  -    DWORD read = 0;
  +    apr_uint32_t read = 0;
       int res;
   
       if (r->remaining < *lpdwSize) {
  @@ -516,11 +661,16 @@
       }
   
       *lpdwSize = read;
  -    return TRUE;
  +    return 1;
   }
   
  -static apr_ssize_t send_response_header(isapi_cid *cid, const char *stat,
  -                                        const char *head, apr_size_t statlen,
  +/* Common code invoked for both HSE_REQ_SEND_RESPONSE_HEADER and 
  + * the newer HSE_REQ_SEND_RESPONSE_HEADER_EX ServerSupportFunction(s)
  + */
  +static apr_ssize_t send_response_header(isapi_cid *cid, 
  +                                        const char *stat,
  +                                        const char *head, 
  +                                        apr_size_t statlen,
                                           apr_size_t headlen)
   {
       int termarg;
  @@ -548,7 +698,12 @@
           }
       }
    
  -    /* Parse them out, or die trying */
  +    /* Seems IIS does not enforce the requirement for \r\n termination 
  +     * on HSE_REQ_SEND_RESPONSE_HEADER, but we won't panic... 
  +     * ap_scan_script_header_err_strs handles this aspect for us.
  +     *
  +     * Parse them out, or die trying 
  +     */
       cid->r->status= ap_scan_script_header_err_strs(cid->r, NULL, &termch,
                                                     &termarg, stat, head, NULL);
       cid->ecb->dwHttpStatusCode = cid->r->status;
  @@ -570,11 +725,12 @@
       return 0;
   }
   
  -BOOL WINAPI ServerSupportFunction(HCONN hConn, DWORD dwHSERequest,
  -                                  LPVOID lpvBuffer, LPDWORD lpdwSize,
  -                                  LPDWORD lpdwDataType)
  +int APR_THREAD_FUNC ServerSupportFunction(isapi_cid   *cid, 
  +                                          apr_uint32_t dwHSERequest,
  +                                          void        *lpvBuffer, 
  +                                          apr_uint32_t *lpdwSize,
  +                                          apr_uint32_t *lpdwDataType)
   {
  -    isapi_cid *cid = (isapi_cid *)hConn;
       request_rec *r = cid->r;
       conn_rec *c = r->connection;
       request_rec *subreq;
  @@ -590,9 +746,9 @@
           apr_table_set (r->headers_out, "Location", lpvBuffer);
           cid->r->status = cid->ecb->dwHttpStatusCode 
                                                  = HTTP_MOVED_TEMPORARILY;
  -        return TRUE;
  +        return 1;
   
  -    case 2: /* HSE_REQ_SEND_URL */
  +    case HSE_REQ_SEND_URL:
           /* Soak up remaining input */
           if (r->remaining > 0) {
               char argsbuffer[HUGE_STRING_LEN];
  @@ -609,9 +765,9 @@
           /* AV fault per PR3598 - redirected path is lost! */
           (char*)lpvBuffer = apr_pstrdup(r->pool, (char*)lpvBuffer);
           ap_internal_redirect((char*)lpvBuffer, r);
  -        return TRUE;
  +        return 1;
   
  -    case 3: /* HSE_REQ_SEND_RESPONSE_HEADER */
  +    case HSE_REQ_SEND_RESPONSE_HEADER:
       {
           /* Parse them out, or die trying */
           apr_size_t statlen = 0, headlen = 0;
  @@ -625,7 +781,7 @@
                                      statlen, headlen);
           if (ate < 0) {
               SetLastError(ERROR_INVALID_PARAMETER);
  -            return FALSE;
  +            return 0;
           }
           else if ((apr_size_t)ate < headlen) {
               apr_bucket_brigade *bb;
  @@ -638,21 +794,23 @@
   	    APR_BRIGADE_INSERT_TAIL(bb, b);
   	    ap_pass_brigade(cid->r->output_filters, bb);
           }
  -        return TRUE;
  +        return 1;
       }
   
  -    case 4: /* HSE_REQ_DONE_WITH_SESSION */
  +    case HSE_REQ_DONE_WITH_SESSION:
           /* Signal to resume the thread completing this request
            */
  +#ifdef FAKE_ASYNC
           if (cid->complete)
  -            SetEvent(cid->complete);            
  -        return TRUE;
  +            SetEvent(cid->complete);
  +#endif
  +        return 1;
   
  -    case 1001: /* HSE_REQ_MAP_URL_TO_PATH */
  +    case HSE_REQ_MAP_URL_TO_PATH:
       {
           /* Map a URL to a filename */
           char *file = (char *)lpvBuffer;
  -        DWORD len;
  +        apr_uint32_t len;
           subreq = ap_sub_req_lookup_uri(apr_pstrndup(r->pool, file, *lpdwSize),
                                          r, NULL);
   
  @@ -667,18 +825,18 @@
               }
           }
           *lpdwSize = len;
  -        return TRUE;
  +        return 1;
       }
   
  -    case 1002: /* HSE_REQ_GET_SSPI_INFO */
  +    case HSE_REQ_GET_SSPI_INFO:
           if (cid->dconf.log_unsupported)
               ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
                              "ISAPI: ServerSupportFunction HSE_REQ_GET_SSPI_INFO "
                              "is not supported: %s", r->filename);
           SetLastError(ERROR_INVALID_PARAMETER);
  -        return FALSE;
  +        return 0;
           
  -    case 1003: /* HSE_APPEND_LOG_PARAMETER */
  +    case HSE_APPEND_LOG_PARAMETER:
           /* Log lpvBuffer, of lpdwSize bytes, in the URI Query (cs-uri-query) field
            */
           apr_table_set(r->notes, "isapi-parameter", (char*) lpvBuffer);
  @@ -692,28 +850,30 @@
               ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, r,
                             "ISAPI: %s: %s", cid->r->filename,
                             (char*) lpvBuffer);
  -        return TRUE;
  +        return 1;
           
  -    case 1005: /* HSE_REQ_IO_COMPLETION */
  +    case HSE_REQ_IO_COMPLETION:
           /* Emulates a completion port...  Record callback address and 
            * user defined arg, we will call this after any async request 
            * (e.g. transmitfile) as if the request executed async.
            * Per MS docs... HSE_REQ_IO_COMPLETION replaces any prior call
            * to HSE_REQ_IO_COMPLETION, and lpvBuffer may be set to NULL.
            */
  -        if (!cid->isa->fakeasync) {
  -            if (cid->dconf.log_unsupported)
  -                ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
  -                          "ISAPI: ServerSupportFunction HSE_REQ_IO_COMPLETION "
  -                          "is not supported: %s", r->filename);
  -            SetLastError(ERROR_INVALID_PARAMETER);
  -            return FALSE;
  +#ifdef FAKE_ASYNC
  +        if (cid->isa->fakeasync) {
  +            cid->completion = (PFN_HSE_IO_COMPLETION) lpvBuffer;
  +            cid->completion_arg = (PVOID) lpdwDataType;
  +            return 1;
           }
  -        cid->completion = (PFN_HSE_IO_COMPLETION) lpvBuffer;
  -        cid->completion_arg = (PVOID) lpdwDataType;
  -        return TRUE;
  +#endif
  +        if (cid->dconf.log_unsupported)
  +            ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
  +                      "ISAPI: ServerSupportFunction HSE_REQ_IO_COMPLETION "
  +                      "is not supported: %s", r->filename);
  +        SetLastError(ERROR_INVALID_PARAMETER);
  +        return 0;
   
  -    case 1006: /* HSE_REQ_TRANSMIT_FILE */
  +    case HSE_REQ_TRANSMIT_FILE:
       {
           HSE_TF_INFO *tf = (HSE_TF_INFO*)lpvBuffer;
           apr_status_t rv;
  @@ -721,17 +881,24 @@
           apr_bucket *b;
           apr_file_t *fd;
   
  -        if (!cid->isa->fakeasync && (tf->dwFlags & HSE_IO_ASYNC)) {
  +#ifdef FAKE_ASYNC
  +        if ((tf->dwFlags & HSE_IO_ASYNC) && cid->isa->fakeasync) {
  +            /* TBD */
  +        }
  +        else /* if (!cid->isa->fakeasync && ... */
  +#endif
  +        if (tf->dwFlags & HSE_IO_ASYNC)
  +        {
               if (cid->dconf.log_unsupported)
                   ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
                            "ISAPI: ServerSupportFunction HSE_REQ_TRANSMIT_FILE "
                            "as HSE_IO_ASYNC is not supported: %s", r->filename);
               SetLastError(ERROR_INVALID_PARAMETER);
  -            return FALSE;
  +            return 0;
           }
           
           if ((rv = apr_os_file_put(&fd, tf->hFile, 0, r->pool)) != APR_SUCCESS) {
  -            return FALSE;
  +            return 0;
           }
           
           /* apr_dupfile_oshandle (&fd, tf->hFile, r->pool); */
  @@ -756,7 +923,7 @@
               {
                   apr_brigade_destroy(bb);
                   SetLastError(ERROR_INVALID_PARAMETER);
  -                return FALSE;
  +                return 0;
               }
               if ((apr_size_t)ate < tf->HeadLength)
               {
  @@ -790,56 +957,61 @@
           /* we do nothing with (tf->dwFlags & HSE_DISCONNECT_AFTER_SEND)
            */
   
  +#ifdef FAKE_ASYNC
           if (tf->dwFlags & HSE_IO_ASYNC) {
               /* XXX: Fake async response,
                * use tf->pfnHseIO, or if NULL, then use cid->fnIOComplete
                * pass pContect to the HseIO callback.
                */
           }
  -        return TRUE;
  +#endif
  +        return 1;
       }
   
  -    case 1007: /* HSE_REQ_REFRESH_ISAPI_ACL */
  +    case HSE_REQ_REFRESH_ISAPI_ACL:
           if (cid->dconf.log_unsupported)
               ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
                             "ISAPI: ServerSupportFunction "
                             "HSE_REQ_REFRESH_ISAPI_ACL "
                             "is not supported: %s", r->filename);
           SetLastError(ERROR_INVALID_PARAMETER);
  -        return FALSE;
  -
  -    case 1008: /* HSE_REQ_IS_KEEP_CONN */
  -        *((LPBOOL) lpvBuffer) = (r->connection->keepalive == 1);
  -        return TRUE;
  +        return 0;
   
  -    case 1010: /* XXX: Fake it : HSE_REQ_ASYNC_READ_CLIENT */
  +    case HSE_REQ_IS_KEEP_CONN:
  +        *((int *)lpvBuffer) = (r->connection->keepalive == 1);
  +        return 1;
  +
  +    case HSE_REQ_ASYNC_READ_CLIENT:
  +#ifdef FAKE_ASYNC
  +        /* TBD: Fake it */
  +#else
           if (cid->dconf.log_unsupported)
               ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
                             "ISAPI: asynchronous I/O not supported: %s", 
                             r->filename);
           SetLastError(ERROR_INVALID_PARAMETER);
  -        return FALSE;
  +        return 0;
  +#endif
   
  -    case 1011: /* HSE_REQ_GET_IMPERSONATION_TOKEN  Added in ISAPI 4.0 */
  +    case HSE_REQ_GET_IMPERSONATION_TOKEN:  /* Added in ISAPI 4.0 */
           if (cid->dconf.log_unsupported)
               ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
                             "ISAPI: ServerSupportFunction "
                             "HSE_REQ_GET_IMPERSONATION_TOKEN "
                             "is not supported: %s", r->filename);
           SetLastError(ERROR_INVALID_PARAMETER);
  -        return FALSE;
  +        return 0;
   
  -#ifdef HSE_REQ_MAP_URL_TO_PATH_EX
  -    case 1012: /* HSE_REQ_MAP_URL_TO_PATH_EX */
  +    case HSE_REQ_MAP_URL_TO_PATH_EX:
       {
           /* Map a URL to a filename */
  -        LPHSE_URL_MAPEX_INFO info = (LPHSE_URL_MAPEX_INFO) lpdwDataType;
  +        HSE_URL_MAPEX_INFO *info = (HSE_URL_MAPEX_INFO*)lpdwDataType;
           char* test_uri = apr_pstrndup(r->pool, (char *)lpvBuffer, *lpdwSize);
   
           subreq = ap_sub_req_lookup_uri(test_uri, r, NULL);
           info->cchMatchingURL = strlen(test_uri);        
           info->cchMatchingPath = apr_cpystrn(info->lpszPath, subreq->filename, 
  -                                            MAX_PATH) - info->lpszPath;
  +                                      sizeof(info->lpszPath)) - info->lpszPath;
   
           /* Mapping started with assuming both strings matched.
            * Now roll on the path_info as a mismatch and handle
  @@ -896,41 +1068,40 @@
           info->dwFlags = (subreq->finfo.protection & APR_UREAD    ? 0x001 : 0)
                         | (subreq->finfo.protection & APR_UWRITE   ? 0x002 : 0)
                         | (subreq->finfo.protection & APR_UEXECUTE ? 0x204 : 0);
  -        return TRUE;
  +        return 1;
       }
  -#endif
   
  -    case 1014: /* HSE_REQ_ABORTIVE_CLOSE */
  +    case HSE_REQ_ABORTIVE_CLOSE:
           if (cid->dconf.log_unsupported)
               ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
                             "ISAPI: ServerSupportFunction HSE_REQ_ABORTIVE_CLOSE"
                             " is not supported: %s", r->filename);
           SetLastError(ERROR_INVALID_PARAMETER);
  -        return FALSE;
  +        return 0;
   
  -    case 1015: /* HSE_REQ_GET_CERT_INFO_EX  Added in ISAPI 4.0 */
  +    case HSE_REQ_GET_CERT_INFO_EX:  /* Added in ISAPI 4.0 */
           if (cid->dconf.log_unsupported)
               ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
                             "ISAPI: ServerSupportFunction "
                             "HSE_REQ_GET_CERT_INFO_EX "
                             "is not supported: %s", r->filename);        
           SetLastError(ERROR_INVALID_PARAMETER);
  -        return FALSE;
  +        return 0;
   
  -#ifdef HSE_REQ_SEND_RESPONSE_HEADER_EX
  -    case 1016: /* HSE_REQ_SEND_RESPONSE_HEADER_EX  Added in ISAPI 4.0 */
  +    case HSE_REQ_SEND_RESPONSE_HEADER_EX:  /* Added in ISAPI 4.0 */
       {
  -        LPHSE_SEND_HEADER_EX_INFO shi
  -                                  = (LPHSE_SEND_HEADER_EX_INFO) lpvBuffer;
  -        /* XXX: ignore shi->fKeepConn?  We shouldn't need the advise */
  -        /* r->connection->keepalive = shi->fKeepConn; */
  +        HSE_SEND_HEADER_EX_INFO *shi = (HSE_SEND_HEADER_EX_INFO*)lpvBuffer;
  +
  +    /*  XXX: ignore shi->fKeepConn?  We shouldn't need the advise
  +     *  r->connection->keepalive = shi->fKeepConn; 
  +     */
           apr_ssize_t ate = send_response_header(cid, shi->pszStatus, 
                                                  shi->pszHeader,
                                                  shi->cchStatus, 
                                                  shi->cchHeader);
           if (ate < 0) {
               SetLastError(ERROR_INVALID_PARAMETER);
  -            return FALSE;
  +            return 0;
           }
           else if ((apr_size_t)ate < shi->cchHeader) {
               apr_bucket_brigade *bb;
  @@ -944,28 +1115,26 @@
   	    APR_BRIGADE_INSERT_TAIL(bb, b);
   	    ap_pass_brigade(cid->r->output_filters, bb);
           }
  -        return TRUE;
  -
  +        return 1;
       }
  -#endif
   
  -    case 1017: /* HSE_REQ_CLOSE_CONNECTION  Added after ISAPI 4.0 */
  +    case HSE_REQ_CLOSE_CONNECTION:  /* Added after ISAPI 4.0 */
           if (cid->dconf.log_unsupported)
               ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
                             "ISAPI: ServerSupportFunction "
                             "HSE_REQ_CLOSE_CONNECTION "
                             "is not supported: %s", r->filename);
           SetLastError(ERROR_INVALID_PARAMETER);
  -        return FALSE;
  +        return 0;
   
  -    case 1018: /* HSE_REQ_IS_CONNECTED  Added after ISAPI 4.0 */
  +    case HSE_REQ_IS_CONNECTED:  /* Added after ISAPI 4.0 */
           /* Returns True if client is connected c.f. MSKB Q188346
            * assuming the identical return mechanism as HSE_REQ_IS_KEEP_CONN
            */
  -        *((LPBOOL) lpvBuffer) = (r->connection->aborted == 0);
  -        return TRUE;
  +        *((int *)lpvBuffer) = (r->connection->aborted == 0);
  +        return 1;
   
  -    case 1020: /* HSE_REQ_EXTENSION_TRIGGER  Added after ISAPI 4.0 */
  +    case HSE_REQ_EXTENSION_TRIGGER:  /* Added after ISAPI 4.0 */
           /*  Undocumented - defined by the Microsoft Jan '00 Platform SDK
            */
           if (cid->dconf.log_unsupported)
  @@ -974,7 +1143,7 @@
                             "HSE_REQ_EXTENSION_TRIGGER "
                             "is not supported: %s", r->filename);
           SetLastError(ERROR_INVALID_PARAMETER);
  -        return FALSE;
  +        return 0;
   
       default:
           if (cid->dconf.log_unsupported)
  @@ -982,7 +1151,7 @@
                             "ISAPI: ServerSupportFunction (%d) not supported: "
                             "%s", dwHSERequest, r->filename);
           SetLastError(ERROR_INVALID_PARAMETER);
  -        return FALSE;
  +        return 0;
       }
   }
   
  @@ -1000,12 +1169,17 @@
       isapi_loaded *isa;
       isapi_cid *cid;
       const char *val;
  -    DWORD read;
  +    apr_uint32_t read;
       int res;
       
  -    if(strcmp(r->handler, "isapi-isa"))
  +    if(strcmp(r->handler, "isapi-isa") 
  +        && strcmp(r->handler, "isapi-handler")) {
  +        /* Hang on to the isapi-isa for compatibility with older docs
  +         * (wtf did '-isa' mean in the first place?) but introduce
  +         * a newer and clearer "isapi-handler" name.
  +         */
           return DECLINED;    
  -
  +    }
       dconf = ap_get_module_config(r->per_dir_config, &isapi_module);
       e = r->subprocess_env;
   
  @@ -1013,28 +1187,25 @@
        *
        * If this fails, it's pointless to load the isapi dll.
        */
  -    if (!(ap_allow_options(r) & OPT_EXECCGI))
  +    if (!(ap_allow_options(r) & OPT_EXECCGI)) {
           return HTTP_FORBIDDEN;
  -
  -    if (r->finfo.filetype == APR_NOFILE)
  +    }
  +    if (r->finfo.filetype == APR_NOFILE) {
           return HTTP_NOT_FOUND;
  -
  -    if (r->finfo.filetype != APR_REG)
  +    }
  +    if (r->finfo.filetype != APR_REG) {
           return HTTP_FORBIDDEN;
  -
  +    }
       if ((r->used_path_info == AP_REQ_REJECT_PATH_INFO) &&
  -        r->path_info && *r->path_info)
  -    {
  +        r->path_info && *r->path_info) {
           /* default to accept */
           return HTTP_NOT_FOUND;
       }
   
  -    /* Load the isapi extention without caching (pass r value) 
  -     * but note that we will recover an existing cached module.
  -     */
  -    if (isapi_load(loaded.pool, r, r->filename, &isa) != APR_SUCCESS)
  +    if (isapi_lookup(r->pool, r->server, r, r->filename, &isa) 
  +           != APR_SUCCESS) {
           return HTTP_INTERNAL_SERVER_ERROR;
  -        
  +    }
       /* Set up variables */
       ap_add_common_vars(r);
       ap_add_cgi_vars(r);
  @@ -1047,7 +1218,7 @@
   
       /* Set up connection structure and ecb */
       cid = apr_pcalloc(r->pool, sizeof(isapi_cid));
  -
  +    
       /* Fixup defaults for dconf */
       cid->dconf.read_ahead_buflen = (dconf->read_ahead_buflen == ISAPI_UNDEF)
                                        ? 48192 : dconf->read_ahead_buflen;
  @@ -1058,13 +1229,15 @@
       cid->dconf.log_to_query      = (dconf->log_to_query == ISAPI_UNDEF)
                                        ? 1 : dconf->log_to_query;
   
  -    cid->ecb = apr_pcalloc(r->pool, sizeof(struct _EXTENSION_CONTROL_BLOCK));
  -    cid->ecb->ConnID = (HCONN)cid;
  +    cid->ecb = apr_pcalloc(r->pool, sizeof(EXTENSION_CONTROL_BLOCK));
  +    cid->ecb->ConnID = cid;
       cid->isa = isa;
       cid->r = r;
       cid->r->status = 0;
  +#ifdef FAKE_ASYNC
       cid->complete = NULL;
       cid->completion = NULL;
  +#endif
       
       cid->ecb->cbSize = sizeof(EXTENSION_CONTROL_BLOCK);
       cid->ecb->dwVersion = isa->report_version;
  @@ -1090,18 +1263,17 @@
       /* Set up client input */
       res = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR);
       if (res) {
  -        isapi_unload(isa, FALSE);
  +        isapi_unload(isa, 0);
           return res;
       }
   
       if (ap_should_client_block(r)) {
           /* Time to start reading the appropriate amount of data,
            * and allow the administrator to tweak the number
  -         * TODO: add the httpd.conf option for read_ahead_buflen.
            */
           if (r->remaining) {
               cid->ecb->cbTotalBytes = (apr_size_t)r->remaining;
  -            if (cid->ecb->cbTotalBytes > (DWORD)cid->dconf.read_ahead_buflen)
  +            if (cid->ecb->cbTotalBytes > (apr_uint32_t)cid->dconf.read_ahead_buflen)
                   cid->ecb->cbAvailable = cid->dconf.read_ahead_buflen;
               else
                   cid->ecb->cbAvailable = cid->ecb->cbTotalBytes;
  @@ -1122,7 +1294,7 @@
           }
   
           if (res < 0) {
  -            isapi_unload(isa, FALSE);
  +            isapi_unload(isa, 0);
               return HTTP_INTERNAL_SERVER_ERROR;
           }
   
  @@ -1154,7 +1326,7 @@
           case HSE_STATUS_SUCCESS:
           case HSE_STATUS_SUCCESS_AND_KEEP_CONN:
               /* Ignore the keepalive stuff; Apache handles it just fine without
  -             * the ISA's "advice".
  +             * the ISAPI Handler's "advice".
                * Per Microsoft: "In IIS versions 4.0 and later, the return
                * values HSE_STATUS_SUCCESS and HSE_STATUS_SUCCESS_AND_KEEP_CONN
                * are functionally identical: Keep-Alive connections are
  @@ -1163,7 +1335,8 @@
                */
               break;
   
  -        case HSE_STATUS_PENDING:    
  +        case HSE_STATUS_PENDING:
  +#ifdef FAKE_ASYNC
               /* emulating async behavior...
                *
                * Create a cid->completed event and wait on it for some timeout
  @@ -1171,25 +1344,29 @@
                *
                * All async ServerSupportFunction calls will be handled through
                * the registered IO_COMPLETION hook.
  +             *
  +             * This request completes upon a notification through
  +             * ServerSupportFunction(HSE_REQ_DONE_WITH_SESSION)
                */
  -            
  -            if (!isa->fakeasync) {
  -                if (cid->dconf.log_unsupported)
  -                {
  -                     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
  -                                   "ISAPI: %s asynch I/O request refused", 
  -                                   r->filename);
  -                     cid->r->status = HTTP_INTERNAL_SERVER_ERROR;
  -                }
  -            }
  -            else {
  -                cid->complete = CreateEvent(NULL, FALSE, FALSE, NULL);
  +            if (isa->fakeasync) 
  +            {
  +                cid->complete = CreateEvent(NULL, 0, 0, NULL);
                   if (WaitForSingleObject(cid->complete, isa->timeout)
                           == WAIT_TIMEOUT) {
                       /* TODO: Now what... if this hung, then do we kill our own
                        * thread to force its death?  For now leave timeout = -1
                        */
                   }
  +                break;
  +            }
  +#endif
  +            if (cid->dconf.log_unsupported)
  +            {
  +                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, r,
  +                               "ISAPI: asynch I/O result HSE_STATUS_PENDING "
  +                               "from HttpExtensionProc() is not supported: %s",
  +                               r->filename);
  +                 cid->r->status = HTTP_INTERNAL_SERVER_ERROR;
               }
               break;
   
  @@ -1211,9 +1388,6 @@
           cid->r->status = cid->ecb->dwHttpStatusCode;
       }
   
  -    /* All done with the DLL... get rid of it... */
  -    isapi_unload(isa, FALSE);
  -    
       return OK;		/* NOT r->status, even if it has changed. */
   }
   
  @@ -1225,6 +1399,8 @@
   
   static int isapi_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
   {
  +    apr_status_t rv;
  +
       apr_pool_sub_make(&loaded.pool, pconf, NULL);
       if (!loaded.pool) {
   	ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, NULL,
  @@ -1232,40 +1408,26 @@
           return APR_EGENERAL;
       }
       
  -    loaded.modules = apr_array_make(loaded.pool, 20, sizeof(isapi_loaded*));
  -    if (!loaded.modules) {
  -	ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, NULL,
  -                     "ISAPI: could not create the isapi cache");
  +    loaded.hash = apr_hash_make(loaded.pool);
  +    if (!loaded.hash) {
  +        ap_log_error(APLOG_MARK, APLOG_NOERRNO, 0, NULL,
  +                     "ISAPI: Failed to create module cache");
           return APR_EGENERAL;
       }
   
  -    return OK;
  -}
  -
  -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 int isapi_post_config(apr_pool_t *p, apr_pool_t *plog,
  -                             apr_pool_t *ptemp, server_rec *s)
  -{
  -    isapi_loaded **elts = (isapi_loaded **)loaded.modules->elts;
  -    int nelts = loaded.modules->nelts;
  -
  -    /* sort the elements of the main_server, by filename */
  -    qsort(elts, nelts, sizeof(isapi_loaded*), compare_loaded);
  -
  +    rv = apr_thread_mutex_create(&loaded.lock, APR_THREAD_MUTEX_DEFAULT, 
  +                                 loaded.pool);
  +    if (rv != APR_SUCCESS) {
  +        ap_log_error(APLOG_MARK, rv, 0, NULL,
  +                     "ISAPI: Failed to create module cache lock");
  +        return rv;
  +    }
       return OK;
   }
   
   static void isapi_hooks(apr_pool_t *cont)
   {
       ap_hook_pre_config(isapi_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
  -    ap_hook_post_config(isapi_post_config, NULL, NULL, APR_HOOK_MIDDLE);
       ap_hook_handler(isapi_handler, NULL, NULL, APR_HOOK_MIDDLE);
   }
   
  
  
  
  1.2       +4 -0      httpd-2.0/modules/arch/win32/mod_isapi.dsp
  
  Index: mod_isapi.dsp
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/arch/win32/mod_isapi.dsp,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- mod_isapi.dsp	10 Jan 2002 08:47:21 -0000	1.1
  +++ mod_isapi.dsp	14 May 2002 05:23:54 -0000	1.2
  @@ -93,6 +93,10 @@
   # End Source File
   # Begin Source File
   
  +SOURCE=.\mod_isapi.h
  +# End Source File
  +# Begin Source File
  +
   SOURCE=.\mod_isapi.rc
   # End Source File
   # Begin Source File
  
  
  
  1.1                  httpd-2.0/modules/arch/win32/mod_isapi.h
  
  Index: mod_isapi.h
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2000-2002 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * Portions of this software are based upon public domain software
   * originally written at the National Center for Supercomputing Applications,
   * University of Illinois, Urbana-Champaign.
   */
  
  #ifndef MOD_ISAPI_H
  #define MOD_ISAPI_H
  
  #ifdef __cplusplus
  extern "C" {
  #endif
  
  /* The Version Information storage passed to a module on startup
   * via the GetExtensionVersion() entry point.
   */
  typedef struct HSE_VERSION_INFO {
      apr_uint32_t dwExtensionVersion;
      char         lpszExtensionDesc[256];
  } HSE_VERSION_INFO;
  
  /* The startup entry point that must be exported by every ISAPI handler
   */
  int APR_THREAD_FUNC GetExtensionVersion(HSE_VERSION_INFO *ver_info);
  typedef (APR_THREAD_FUNC *PFN_GETEXTENSIONVERSION)(HSE_VERSION_INFO *ver_info);
  
  /* Our internal 'HCONN' representation, always opaque to the user.
   */
  typedef struct isapi_cid isapi_cid, *HCONN;
  
  /* Prototypes of the essential functions exposed by mod_isapi 
   * for the module to communicate with Apache.
   */
  typedef int (APR_THREAD_FUNC 
                  *PFN_GETSERVERVARIABLE)(HCONN         cid,
                                          char         *variable_name,
                                          void         *buf_data,
                                          apr_uint32_t *buf_size);
  typedef int (APR_THREAD_FUNC 
                  *PFN_WRITECLIENT)(HCONN         cid, 
                                    void         *buf_data,
                                    apr_uint32_t *buf_size,
                                    apr_uint32_t  flags);
  typedef int (APR_THREAD_FUNC 
                  *PFN_READCLIENT)(HCONN         cid, 
                                   void         *buf_data,
                                   apr_uint32_t *buf_size);
  typedef int (APR_THREAD_FUNC 
                  *PFN_SERVERSUPPORTFUNCTION)(HCONN         cid,
                                              apr_uint32_t  HSE_code,
                                              void         *buf_data,
                                              apr_uint32_t *buf_size,
                                              apr_uint32_t *flags);
  
  /* The ecb structure is passed on each invocation of the module
   */
  typedef struct EXTENSION_CONTROL_BLOCK {
      apr_uint32_t   cbSize;
      apr_uint32_t   dwVersion;
      HCONN          ConnID;
      apr_uint32_t   dwHttpStatusCode;
      char           lpszLogData[80];
      char          *lpszMethod;
      char          *lpszQueryString;
      char          *lpszPathInfo;
      char          *lpszPathTranslated;
      apr_uint32_t   cbTotalBytes;
      apr_uint32_t   cbAvailable;
      unsigned char *lpbData;
      char          *lpszContentType;
  
      PFN_GETSERVERVARIABLE     GetServerVariable;
      PFN_WRITECLIENT           WriteClient;
      PFN_READCLIENT            ReadClient;
      PFN_SERVERSUPPORTFUNCTION ServerSupportFunction;
  } EXTENSION_CONTROL_BLOCK;
  
  /* Status/Headers structure to pass to HSE_SEND_HEADER_EX, 
   * an MS extension to ServerSupportFunction
   */
  typedef struct HSE_SEND_HEADER_EX_INFO {
      const char * pszStatus; /* HTTP status text, such as "200 OK" */
      const char * pszHeader; /* HTTP header lines text, such as
                               *   "Content-type: text/plain\r\n"
                               *   "Content-Language: en\r\n" 
                               * Note that (in spite of cchFoo lengths below)
                               * NULL characters will interfere in headers.
                               */
      apr_uint32_t cchStatus; /* length of pszStatus text */
      apr_uint32_t cchHeader; /* length of pszHeader text */
      int          fKeepConn; /* Ignored: used to set keep-alive status,
                               * but Apache follows the client's negotiated
                               * HTTP contract to decide.
                               */
  } HSE_SEND_HEADER_EX_INFO;
  
  /* Our only 'supported' MS extended flag bit for TransmitFile,
   * HSE_IO_SEND_HEADERS indicates that Status+Headers are present
   * in the pszStatusCode member of the HSE_TF_INFO structure.
   */
  #define HSE_IO_SEND_HEADERS 8
  
  /* The remaining flags are MS extended flag bits that bear little
   * relation to Apache; the rules that the Apache server obeys follow
   * its own design and HTTP protocol filter rules.
   *
   * We do not support async, however, we fake it.  If HSE_IO_SYNC is 
   * not passed, and a completion context was defined, we will invoke the
   * completion function immediately following the transfer, and then
   * return to the caller.  If HSE_IO_SYNC is passed, there is no call
   * neccessary to the completion context.
   */
  #define HSE_IO_SYNC  1
  #define HSE_IO_ASYNC 2
  #define HSE_IO_DISCONNECT_AFTER_SEND 4
  #define HSE_IO_NODELAY 4096
  
  /* The Completion function prototype.  This callback may be fixed with 
   * the HSE_REQ_IO_COMPLETION ServerSupportFunction call, or overriden
   * for the HSE_REQ_TRANSMIT_FILE call.
   */
  typedef void (APR_THREAD_FUNC *PFN_HSE_IO_COMPLETION)
                                    (EXTENSION_CONTROL_BLOCK *ecb,
                                     void                    *ctxt,
                                     apr_uint32_t             cbIO,
                                     apr_uint32_t             dwError);
  
  /* TransmitFile structure to pass to HSE_REQ_TRANSMIT_FILE, an MS extension
   */
  typedef struct HSE_TF_INFO {
      PFN_HSE_IO_COMPLETION pfnHseIO;      /* Overrides the default setting of
                                            * HSE_REQ_IO_COMPLETION if not NULL
                                            */
      void                 *pContext;
      apr_os_file_t         hFile;         /* HANDLE/fd to transmit */
      const char           *pszStatusCode; /* Ignored if HSE_IO_SEND_HEADERS is 
                                            * not set.  Includes HTTP status text
                                            * plus header text lines, such as
                                            *   "200 OK\r\n"
                                            *   "Content-type: text/plain\r\n"
                                            */
      apr_uint32_t          BytesToWrite;  /* 0 is write-all */
      apr_uint32_t          Offset;        /* File Offset */
      void                 *pHead;         /* Prefix with *pHead body text */
      apr_uint32_t          HeadLength;    /* Length of *pHead body text */
      void                 *pTail;         /* Prefix with *pTail body text */
      apr_uint32_t          TailLength;    /* Length of *pTail body text */
      apr_uint32_t          dwFlags;       /* bit flags described above */
  } HSE_TF_INFO;
  
  typedef struct HSE_URL_MAPEX_INFO {
      char         lpszPath[MAX_PATH];
      apr_uint32_t dwFlags;
      apr_uint32_t cchMatchingPath;
      apr_uint32_t cchMatchingURL;
      apr_uint32_t dwReserved1;
      apr_uint32_t dwReserved2;
  } HSE_URL_MAPEX_INFO;
  
  /* Original ISAPI ServerSupportFunction() HSE_code methods */
  #define HSE_REQ_SEND_URL_REDIRECT_RESP   1
  #define HSE_REQ_SEND_URL                 2
  #define HSE_REQ_SEND_RESPONSE_HEADER     3
  #define HSE_REQ_DONE_WITH_SESSION        4
      
  /* MS Extented methods to ISAPI ServerSupportFunction() HSE_code */
  #define HSE_REQ_MAP_URL_TO_PATH          1001 /* Emulated */
  #define HSE_REQ_GET_SSPI_INFO            1002 /* Not Supported */
  #define HSE_APPEND_LOG_PARAMETER         1003 /* Supported */
  #define HSE_REQ_IO_COMPLETION            1005 /* Emulated */
  #define HSE_REQ_TRANSMIT_FILE            1006 /* Async Emulated */
  #define HSE_REQ_REFRESH_ISAPI_ACL        1007 /* Not Supported */
  #define HSE_REQ_IS_KEEP_CONN             1008 /* Supported */
  #define HSE_REQ_ASYNC_READ_CLIENT        1010 /* Emulated */
  /*   Added with ISAPI 4.0 */
  #define HSE_REQ_GET_IMPERSONATION_TOKEN  1011 /* Not Supported */
  #define HSE_REQ_MAP_URL_TO_PATH_EX       1012 /* Emulated */
  #define HSE_REQ_ABORTIVE_CLOSE           1014 /* Ignored */
  /*   Added after ISAPI 4.0 in IIS 5.0 */
  #define HSE_REQ_GET_CERT_INFO_EX         1015 /* Not Supported */
  #define HSE_REQ_SEND_RESPONSE_HEADER_EX  1016 /* Supported (no nulls!) */
  #define HSE_REQ_CLOSE_CONNECTION         1017 /* Ignored */
  #define HSE_REQ_IS_CONNECTED             1018 /* Supported */
  #define HSE_REQ_EXTENSION_TRIGGER        1020 /* Not Supported */
  
  /* The request entry point that must be exported by every ISAPI handler 
   */
  apr_uint32_t APR_THREAD_FUNC HttpExtensionProc(EXTENSION_CONTROL_BLOCK *ecb);
  typedef apr_uint32_t (APR_THREAD_FUNC 
                          *PFN_HTTPEXTENSIONPROC)(EXTENSION_CONTROL_BLOCK *ecb);
  
  /* Allowable return values from HttpExtensionProc (apparently 0 is also 
   * accepted by MS IIS, and we will respect it as Success.)
   * If the HttpExtensionProc returns HSE_STATUS_PENDING, we will create
   * a wait mutex and lock on it, until HSE_REQ_DONE_WITH_SESSION is called.
   */
  #define HSE_STATUS_SUCCESS                1
  #define HSE_STATUS_SUCCESS_AND_KEEP_CONN  2 /* 1 vs 2 Ignored, we choose */
  #define HSE_STATUS_PENDING                3 /* Emulated (thread lock) */
  #define HSE_STATUS_ERROR                  4
  
  /* Valid flags passed with TerminateExtension()
   */
  #define HSE_TERM_MUST_UNLOAD      1
  #define HSE_TERM_ADVISORY_UNLOAD  2
  
  /* The shutdown entry point �ptionally exported by an ISAPI handler, passed
   * HSE_TERM_MUST_UNLOAD or HSE_TERM_ADVISORY_UNLOAD.  The module may return 
   * if passed HSE_TERM_ADVISORY_UNLOAD, and the module will remain loaded.
   * If the module returns 1 to HSE_TERM_ADVISORY_UNLOAD it is immediately 
   * unloaded.  If the module is passed HSE_TERM_MUST_UNLOAD, its return value 
   * is ignored.
   */
  int APR_THREAD_FUNC TerminateExtension(apr_uint32_t flags);
  typedef int (APR_THREAD_FUNC *PFN_TERMINATEEXTENSION)(apr_uint32_t flags);
  
  /* Module may return 0 if passed HSE_TERM_ADVISORY_UNLOAD, and the module
   * will remain loaded, or 1 if it consents to being unloaded. If the module
   * is passed HSE_TERM_MUST_UNLOAD, it's return value is ignored.
   */
  #define HSE_TERM_MUST_UNLOAD      1
  #define HSE_TERM_ADVISORY_UNLOAD  2
  
  #ifdef __cplusplus
  }
  #endif
  
  #endif	/* !MOD_ISAPI_H */