]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Part one of a several-fold overhaul to isapi. This part simply
authorWilliam A. Rowe Jr <wrowe@apache.org>
Mon, 13 May 2002 21:00:58 +0000 (21:00 +0000)
committerWilliam A. Rowe Jr <wrowe@apache.org>
Mon, 13 May 2002 21:00:58 +0000 (21:00 +0000)
  normalizes the module to the usual order of config-cmds/helpers/
  handlers/setup.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@95069 13f79535-47bb-0310-9956-ffa450edef68

modules/arch/win32/mod_isapi.c

index 37fdf100ac26855df24f65161a54c3e0aa4a91ac..e8058cc29d0f236378d46d19679ca7fdec3516f3 100644 (file)
 #pragma message("http://msdn.microsoft.com/downloads/sdks/platform/platform.asp")
 #endif
 
-/* TODO: Unknown errors that must be researched for correct codes */
-
-#define TODO_ERROR 1
-
-/* Seems IIS does not enforce the requirement for \r\n termination on HSE_REQ_SEND_RESPONSE_HEADER,
-   define this to conform */
-#define RELAX_HEADER_RULE
+/**********************************************************
+ *
+ *  ISAPI Module Configuration
+ *
+ **********************************************************/
 
 module AP_MODULE_DECLARE_DATA isapi_module;
 
-/* Declare the ISAPI functions */
-
-BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
-                               LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer);
-BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpdwBytes,
-                         DWORD dwReserved);
-BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize);
-BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
-                                   LPVOID lpvBuffer, LPDWORD lpdwSize,
-                                   LPDWORD lpdwDataType);
-
 /* Our isapi server config structure */
-
 typedef struct {
     apr_array_header_t *loaded;
     DWORD ReadAheadBuffer;
@@ -131,9 +117,153 @@ typedef struct {
     int AppendLogToQuery;
 } isapi_server_conf;
 
-/* Our loaded isapi module description structure */
+typedef struct isapi_loaded isapi_loaded;
 
-typedef struct {
+static apr_status_t isapi_load(apr_pool_t *p, isapi_server_conf *sconf, 
+                               request_rec *r, const char *fpath, 
+                               isapi_loaded** isa);
+
+/*
+ * Command handler for the ISAPIReadAheadBuffer directive, which is TAKE1
+ */
+static const char *isapi_cmd_readaheadbuffer(cmd_parms *cmd, void *config, 
+                                             char *arg)
+{
+    isapi_server_conf *sconf = ap_get_module_config(cmd->server->module_config,
+                                                   &isapi_module);
+    char *scan;
+    long val;
+
+    if (((val = strtol(arg, (char **) &scan, 10)) <= 0) || *scan)
+        return "ISAPIReadAheadBuffer must be a legitimate value.";
+    
+    sconf->ReadAheadBuffer = val;
+    return NULL;
+}
+
+/*
+ * Command handler for the ISAPIReadAheadBuffer directive, which is TAKE1
+ */
+static const char *isapi_cmd_lognotsupported(cmd_parms *cmd, void *config, 
+                                             char *arg)
+{
+    isapi_server_conf *sconf = ap_get_module_config(cmd->server->module_config,
+                                               &isapi_module);
+
+    if (strcasecmp(arg, "on") == 0) {
+        sconf->LogNotSupported = -1;
+    }
+    else if (strcasecmp(arg, "off") == 0) {
+        sconf->LogNotSupported = 0;
+    }
+    else {
+        return "ISAPILogNotSupported must be on or off";
+    }
+    return NULL;
+}
+
+static const char *isapi_cmd_appendlogtoerrors(cmd_parms *cmd, void *config, 
+                                               char *arg)
+{
+    isapi_server_conf *sconf = ap_get_module_config(cmd->server->module_config,
+                                                   &isapi_module);
+
+    if (strcasecmp(arg, "on") == 0) {
+        sconf->AppendLogToErrors = -1;
+    }
+    else if (strcasecmp(arg, "off") == 0) {
+        sconf->AppendLogToErrors = 0;
+    }
+    else {
+        return "ISAPIAppendLogToErrors must be on or off";
+    }
+    return NULL;
+}
+
+static const char *isapi_cmd_appendlogtoquery(cmd_parms *cmd, void *config, 
+                                              char *arg)
+{
+    isapi_server_conf *sconf = ap_get_module_config(cmd->server->module_config,
+                                                   &isapi_module);
+
+    if (strcasecmp(arg, "on") == 0) {
+        sconf->AppendLogToQuery = -1;
+    }
+    else if (strcasecmp(arg, "off") == 0) {
+        sconf->AppendLogToQuery = 0;
+    }
+    else {
+        return "ISAPIAppendLogToQuery must be on or off";
+    }
+    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_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);
+       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);
+       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);
+       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", fspec);
+       return NULL;
+    }
+
+    /* Add to cached list of loaded modules */
+    newisa = apr_array_push(sconf->loaded);
+    *newisa = isa;
+    
+    return NULL;
+}
+
+static const command_rec isapi_cmds[] = {
+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 }
+};
+
+/**********************************************************
+ *
+ *  ISAPI Module Cache handling section
+ *
+ **********************************************************/
+
+/* Our loaded isapi module description structure */
+struct isapi_loaded {
     const char *filename;
     apr_dso_handle_t *handle;
     HSE_VERSION_INFO *pVer;
@@ -144,19 +274,7 @@ typedef struct {
     DWORD timeout;
     BOOL  fakeasync;
     DWORD reportversion;
-} isapi_loaded;
-
-/* Our "Connection ID" structure */
-
-typedef struct {
-    LPEXTENSION_CONTROL_BLOCK ecb;
-    isapi_server_conf *sconf;
-    isapi_loaded *isa;
-    request_rec  *r;
-    PFN_HSE_IO_COMPLETION completion;
-    PVOID  completion_arg;
-    HANDLE complete;
-} isapi_cid;
+};
 
 static void *create_isapi_server_config(apr_pool_t *p, server_rec *s)
 {
@@ -171,34 +289,26 @@ static void *create_isapi_server_config(apr_pool_t *p, server_rec *s)
     return sconf;
 }
 
-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)
+static apr_status_t isapi_unload(isapi_loaded* isa, int force)
 {
-    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 all virtualhosts share the same */
-    for (s = s->next; s; s = s->next) {
-       ap_set_module_config(s->module_config, &isapi_module, sconf);
+    /* 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 (((--isa->refcount > 0) && !force) || !isa->handle)
+        return APR_SUCCESS;
+    if (isa->TerminateExtension) {
+        if (force)
+            (*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD);
+        else if (!(*isa->TerminateExtension)(HSE_TERM_ADVISORY_UNLOAD))
+            return APR_EGENERAL;
     }
-    return OK;
+    apr_dso_unload(isa->handle);
+    isa->handle = NULL;
+    return APR_SUCCESS;
 }
 
-static apr_status_t isapi_unload(isapi_loaded* isa, int force);
-
 static apr_status_t cleanup_isapi(void *isa)
 {
     return isapi_unload((isapi_loaded*) isa, TRUE);
@@ -302,248 +412,29 @@ static apr_status_t isapi_load(apr_pool_t *p, isapi_server_conf *sconf,
     return APR_SUCCESS;
 }
 
-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 (((--isa->refcount > 0) && !force) || !isa->handle)
-        return APR_SUCCESS;
-    if (isa->TerminateExtension) {
-        if (force)
-            (*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD);
-        else if (!(*isa->TerminateExtension)(HSE_TERM_ADVISORY_UNLOAD))
-            return APR_EGENERAL;
-    }
-    apr_dso_unload(isa->handle);
-    isa->handle = NULL;
-    return APR_SUCCESS;
-}
+/**********************************************************
+ *
+ *  ISAPI Module request callbacks section
+ *
+ **********************************************************/
 
-apr_status_t isapi_handler (request_rec *r)
-{
-    isapi_server_conf * sconf;
-    apr_table_t *e;
-    apr_status_t rv;
+/* Our "Connection ID" structure */
+typedef struct isapi_cid {
+    LPEXTENSION_CONTROL_BLOCK ecb;
+    isapi_server_conf *sconf;
     isapi_loaded *isa;
-    isapi_cid *cid;
-    const char *val;
-    DWORD read;
-    int res;
-    
-    if(strcmp(r->handler, "isapi-isa"))
-        return DECLINED;    
+    request_rec  *r;
+    PFN_HSE_IO_COMPLETION completion;
+    PVOID  completion_arg;
+    HANDLE complete;
+} isapi_cid;
 
-    sconf = ap_get_module_config(r->server->module_config, &isapi_module);
-    e = r->subprocess_env;
-
-    /* Use similar restrictions as CGIs
-     *
-     * If this fails, it's pointless to load the isapi dll.
-     */
-    if (!(ap_allow_options(r) & OPT_EXECCGI))
-        return HTTP_FORBIDDEN;
-
-    if (r->finfo.filetype == APR_NOFILE)
-        return HTTP_NOT_FOUND;
-
-    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)
-    {
-        /* default to accept */
-        return HTTP_NOT_FOUND;
-    }
-
-    /* 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 */
-    ap_add_common_vars(r);
-    ap_add_cgi_vars(r);
-    apr_table_setn(e, "UNMAPPED_REMOTE_USER", "REMOTE_USER");
-    if ((val = apr_table_get(e, "HTTPS")) && strcmp(val, "on"))
-        apr_table_setn(e, "SERVER_PORT_SECURE", "1");
-    else
-        apr_table_setn(e, "SERVER_PORT_SECURE", "0");
-    apr_table_setn(e, "URL", r->uri);
-
-    /* Set up connection structure and ecb */
-    cid = apr_pcalloc(r->pool, sizeof(isapi_cid));
-    cid->sconf = ap_get_module_config(r->server->module_config, &isapi_module);
-
-    cid->ecb = apr_pcalloc(r->pool, sizeof(struct _EXTENSION_CONTROL_BLOCK));
-    cid->ecb->ConnID = (HCONN)cid;
-    cid->isa = isa;
-    cid->r = r;
-    cid->r->status = 0;
-    cid->complete = NULL;
-    cid->completion = NULL;
-    
-    cid->ecb->cbSize = sizeof(EXTENSION_CONTROL_BLOCK);
-    cid->ecb->dwVersion = isa->reportversion;
-    cid->ecb->dwHttpStatusCode = 0;
-    strcpy(cid->ecb->lpszLogData, "");
-    // TODO: are copies really needed here?
-    cid->ecb->lpszMethod = apr_pstrdup(r->pool, (char*) r->method);
-    cid->ecb->lpszQueryString = apr_pstrdup(r->pool, 
-                                (char*) apr_table_get(e, "QUERY_STRING"));
-    cid->ecb->lpszPathInfo = apr_pstrdup(r->pool, 
-                             (char*) apr_table_get(e, "PATH_INFO"));
-    cid->ecb->lpszPathTranslated = apr_pstrdup(r->pool, 
-                                   (char*) apr_table_get(e, "PATH_TRANSLATED"));
-    cid->ecb->lpszContentType = apr_pstrdup(r->pool, 
-                                (char*) apr_table_get(e, "CONTENT_TYPE"));
-    /* Set up the callbacks */
-    cid->ecb->GetServerVariable = GetServerVariable;
-    cid->ecb->WriteClient = WriteClient;
-    cid->ecb->ReadClient = ReadClient;
-    cid->ecb->ServerSupportFunction = ServerSupportFunction;
-
-    
-    /* Set up client input */
-    res = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR);
-    if (res) {
-        isapi_unload(isa, FALSE);
-        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 ReadAheadBuffer.
-         */
-        if (r->remaining) {
-            cid->ecb->cbTotalBytes = (apr_size_t)r->remaining;
-            if (cid->ecb->cbTotalBytes > cid->sconf->ReadAheadBuffer)
-                cid->ecb->cbAvailable = cid->sconf->ReadAheadBuffer;
-            else
-                cid->ecb->cbAvailable = cid->ecb->cbTotalBytes;
-        }
-        else
-        {
-            cid->ecb->cbTotalBytes = 0xffffffff;
-            cid->ecb->cbAvailable = cid->sconf->ReadAheadBuffer;
-        }
-
-        cid->ecb->lpbData = apr_pcalloc(r->pool, cid->ecb->cbAvailable + 1);
-
-        read = 0;
-        while (read < cid->ecb->cbAvailable &&
-               ((res = ap_get_client_block(r, cid->ecb->lpbData + read,
-                                        cid->ecb->cbAvailable - read)) > 0)) {
-            read += res;
-        }
-
-        if (res < 0) {
-            isapi_unload(isa, FALSE);
-            return HTTP_INTERNAL_SERVER_ERROR;
-        }
-
-        /* Although it's not to spec, IIS seems to null-terminate
-         * its lpdData string. So we will too.
-         */
-        if (res == 0)
-            cid->ecb->cbAvailable = cid->ecb->cbTotalBytes = read;
-        else
-            cid->ecb->cbAvailable = read;
-        cid->ecb->lpbData[read] = '\0';
-    }
-    else {
-        cid->ecb->cbTotalBytes = 0;
-        cid->ecb->cbAvailable = 0;
-        cid->ecb->lpbData = NULL;
-    }
-
-    /* All right... try and run the sucker */
-    rv = (*isa->HttpExtensionProc)(cid->ecb);
-
-    /* Check for a log message - and log it */
-    if (cid->ecb->lpszLogData && *cid->ecb->lpszLogData)
-        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, r,
-                      "ISAPI %s: %s", r->filename, cid->ecb->lpszLogData);
-
-    switch(rv) {
-        case 0:  /* Strange, but MS isapi accepts this as success */
-        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".
-             * 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
-             * maintained, if supported by the client."
-             * ... so we were pat all this time
-             */
-            break;
-
-        case HSE_STATUS_PENDING:    
-            /* emulating async behavior...
-             *
-             * Create a cid->completed event and wait on it for some timeout
-             * so that the app thinks is it running async.
-             *
-             * All async ServerSupportFunction calls will be handled through
-             * the registered IO_COMPLETION hook.
-             */
-            
-            if (!isa->fakeasync) {
-                if (cid->sconf->LogNotSupported)
-                {
-                     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 (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;
-
-        case HSE_STATUS_ERROR:    
-            /* end response if we have yet to do so.
-             */
-            cid->r->status = HTTP_INTERNAL_SERVER_ERROR;
-            break;
-
-        default:
-            /* TODO: log unrecognized retval for debugging 
-             */
-            cid->r->status = HTTP_INTERNAL_SERVER_ERROR;
-            break;
-    }
-
-    /* Set the status (for logging) */
-    if (cid->ecb->dwHttpStatusCode) {
-        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. */
-}
-
-BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
-                               LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer)
-{
-    request_rec *r = ((isapi_cid *)hConn)->r;
-    const char *result;
-    DWORD len;
+BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
+                               LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer)
+{
+    request_rec *r = ((isapi_cid *)hConn)->r;
+    const char *result;
+    DWORD len;
 
     if (!strcmp(lpszVariableName, "ALL_HTTP")) 
     {
@@ -781,7 +672,7 @@ BOOL WINAPI ServerSupportFunction(HCONN hConn, DWORD dwHSERequest,
                                    (char*) lpdwDataType,
                                    statlen, headlen);
         if (ate < 0) {
-            SetLastError(TODO_ERROR);
+            SetLastError(ERROR_INVALID_PARAMETER);
             return FALSE;
         }
         else if ((apr_size_t)ate < headlen) {
@@ -912,7 +803,7 @@ BOOL WINAPI ServerSupportFunction(HCONN hConn, DWORD dwHSERequest,
             if (ate < 0)
             {
                 apr_brigade_destroy(bb);
-                SetLastError(TODO_ERROR);
+                SetLastError(ERROR_INVALID_PARAMETER);
                 return FALSE;
             }
             if ((apr_size_t)ate < tf->HeadLength)
@@ -1086,7 +977,7 @@ BOOL WINAPI ServerSupportFunction(HCONN hConn, DWORD dwHSERequest,
                                                shi->cchStatus, 
                                                shi->cchHeader);
         if (ate < 0) {
-            SetLastError(TODO_ERROR);
+            SetLastError(ERROR_INVALID_PARAMETER);
             return FALSE;
         }
         else if ((apr_size_t)ate < shi->cchHeader) {
@@ -1143,123 +1034,258 @@ BOOL WINAPI ServerSupportFunction(HCONN hConn, DWORD dwHSERequest,
     }
 }
 
-/*
- * Command handler for the ISAPIReadAheadBuffer directive, which is TAKE1
- */
-static const char *isapi_cmd_readaheadbuffer(cmd_parms *cmd, void *config, 
-                                             char *arg)
-{
-    isapi_server_conf *sconf = ap_get_module_config(cmd->server->module_config,
-                                                   &isapi_module);
-    char *scan;
-    long val;
-
-    if (((val = strtol(arg, (char **) &scan, 10)) <= 0) || *scan)
-        return "ISAPIReadAheadBuffer must be a legitimate value.";
-    
-    sconf->ReadAheadBuffer = val;
-    return NULL;
-}
+/**********************************************************
+ *
+ *  ISAPI Module request invocation section
+ *
+ **********************************************************/
 
-/*
- * Command handler for the ISAPIReadAheadBuffer directive, which is TAKE1
- */
-static const char *isapi_cmd_lognotsupported(cmd_parms *cmd, void *config, 
-                                             char *arg)
+apr_status_t isapi_handler (request_rec *r)
 {
-    isapi_server_conf *sconf = ap_get_module_config(cmd->server->module_config,
-                                               &isapi_module);
+    isapi_server_conf * sconf;
+    apr_table_t *e;
+    apr_status_t rv;
+    isapi_loaded *isa;
+    isapi_cid *cid;
+    const char *val;
+    DWORD read;
+    int res;
+    
+    if(strcmp(r->handler, "isapi-isa"))
+        return DECLINED;    
 
-    if (strcasecmp(arg, "on") == 0) {
-        sconf->LogNotSupported = -1;
-    }
-    else if (strcasecmp(arg, "off") == 0) {
-        sconf->LogNotSupported = 0;
-    }
-    else {
-        return "ISAPILogNotSupported must be on or off";
-    }
-    return NULL;
-}
+    sconf = ap_get_module_config(r->server->module_config, &isapi_module);
+    e = r->subprocess_env;
 
-static const char *isapi_cmd_appendlogtoerrors(cmd_parms *cmd, void *config, 
-                                               char *arg)
-{
-    isapi_server_conf *sconf = ap_get_module_config(cmd->server->module_config,
-                                                   &isapi_module);
+    /* Use similar restrictions as CGIs
+     *
+     * If this fails, it's pointless to load the isapi dll.
+     */
+    if (!(ap_allow_options(r) & OPT_EXECCGI))
+        return HTTP_FORBIDDEN;
 
-    if (strcasecmp(arg, "on") == 0) {
-        sconf->AppendLogToErrors = -1;
+    if (r->finfo.filetype == APR_NOFILE)
+        return HTTP_NOT_FOUND;
+
+    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)
+    {
+        /* default to accept */
+        return HTTP_NOT_FOUND;
     }
-    else if (strcasecmp(arg, "off") == 0) {
-        sconf->AppendLogToErrors = 0;
+
+    /* 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 */
+    ap_add_common_vars(r);
+    ap_add_cgi_vars(r);
+    apr_table_setn(e, "UNMAPPED_REMOTE_USER", "REMOTE_USER");
+    if ((val = apr_table_get(e, "HTTPS")) && strcmp(val, "on"))
+        apr_table_setn(e, "SERVER_PORT_SECURE", "1");
+    else
+        apr_table_setn(e, "SERVER_PORT_SECURE", "0");
+    apr_table_setn(e, "URL", r->uri);
+
+    /* Set up connection structure and ecb */
+    cid = apr_pcalloc(r->pool, sizeof(isapi_cid));
+    cid->sconf = ap_get_module_config(r->server->module_config, &isapi_module);
+
+    cid->ecb = apr_pcalloc(r->pool, sizeof(struct _EXTENSION_CONTROL_BLOCK));
+    cid->ecb->ConnID = (HCONN)cid;
+    cid->isa = isa;
+    cid->r = r;
+    cid->r->status = 0;
+    cid->complete = NULL;
+    cid->completion = NULL;
+    
+    cid->ecb->cbSize = sizeof(EXTENSION_CONTROL_BLOCK);
+    cid->ecb->dwVersion = isa->reportversion;
+    cid->ecb->dwHttpStatusCode = 0;
+    strcpy(cid->ecb->lpszLogData, "");
+    // TODO: are copies really needed here?
+    cid->ecb->lpszMethod = apr_pstrdup(r->pool, (char*) r->method);
+    cid->ecb->lpszQueryString = apr_pstrdup(r->pool, 
+                                (char*) apr_table_get(e, "QUERY_STRING"));
+    cid->ecb->lpszPathInfo = apr_pstrdup(r->pool, 
+                             (char*) apr_table_get(e, "PATH_INFO"));
+    cid->ecb->lpszPathTranslated = apr_pstrdup(r->pool, 
+                                   (char*) apr_table_get(e, "PATH_TRANSLATED"));
+    cid->ecb->lpszContentType = apr_pstrdup(r->pool, 
+                                (char*) apr_table_get(e, "CONTENT_TYPE"));
+    /* Set up the callbacks */
+    cid->ecb->GetServerVariable = GetServerVariable;
+    cid->ecb->WriteClient = WriteClient;
+    cid->ecb->ReadClient = ReadClient;
+    cid->ecb->ServerSupportFunction = ServerSupportFunction;
+
+    
+    /* Set up client input */
+    res = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR);
+    if (res) {
+        isapi_unload(isa, FALSE);
+        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 ReadAheadBuffer.
+         */
+        if (r->remaining) {
+            cid->ecb->cbTotalBytes = (apr_size_t)r->remaining;
+            if (cid->ecb->cbTotalBytes > cid->sconf->ReadAheadBuffer)
+                cid->ecb->cbAvailable = cid->sconf->ReadAheadBuffer;
+            else
+                cid->ecb->cbAvailable = cid->ecb->cbTotalBytes;
+        }
+        else
+        {
+            cid->ecb->cbTotalBytes = 0xffffffff;
+            cid->ecb->cbAvailable = cid->sconf->ReadAheadBuffer;
+        }
+
+        cid->ecb->lpbData = apr_pcalloc(r->pool, cid->ecb->cbAvailable + 1);
+
+        read = 0;
+        while (read < cid->ecb->cbAvailable &&
+               ((res = ap_get_client_block(r, cid->ecb->lpbData + read,
+                                        cid->ecb->cbAvailable - read)) > 0)) {
+            read += res;
+        }
+
+        if (res < 0) {
+            isapi_unload(isa, FALSE);
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        /* Although it's not to spec, IIS seems to null-terminate
+         * its lpdData string. So we will too.
+         */
+        if (res == 0)
+            cid->ecb->cbAvailable = cid->ecb->cbTotalBytes = read;
+        else
+            cid->ecb->cbAvailable = read;
+        cid->ecb->lpbData[read] = '\0';
     }
     else {
-        return "ISAPIAppendLogToErrors must be on or off";
+        cid->ecb->cbTotalBytes = 0;
+        cid->ecb->cbAvailable = 0;
+        cid->ecb->lpbData = NULL;
     }
-    return NULL;
-}
 
-static const char *isapi_cmd_appendlogtoquery(cmd_parms *cmd, void *config, 
-                                              char *arg)
-{
-    isapi_server_conf *sconf = ap_get_module_config(cmd->server->module_config,
-                                                   &isapi_module);
+    /* All right... try and run the sucker */
+    rv = (*isa->HttpExtensionProc)(cid->ecb);
 
-    if (strcasecmp(arg, "on") == 0) {
-        sconf->AppendLogToQuery = -1;
-    }
-    else if (strcasecmp(arg, "off") == 0) {
-        sconf->AppendLogToQuery = 0;
+    /* Check for a log message - and log it */
+    if (cid->ecb->lpszLogData && *cid->ecb->lpszLogData)
+        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, r,
+                      "ISAPI %s: %s", r->filename, cid->ecb->lpszLogData);
+
+    switch(rv) {
+        case 0:  /* Strange, but MS isapi accepts this as success */
+        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".
+             * 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
+             * maintained, if supported by the client."
+             * ... so we were pat all this time
+             */
+            break;
+
+        case HSE_STATUS_PENDING:    
+            /* emulating async behavior...
+             *
+             * Create a cid->completed event and wait on it for some timeout
+             * so that the app thinks is it running async.
+             *
+             * All async ServerSupportFunction calls will be handled through
+             * the registered IO_COMPLETION hook.
+             */
+            
+            if (!isa->fakeasync) {
+                if (cid->sconf->LogNotSupported)
+                {
+                     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 (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;
+
+        case HSE_STATUS_ERROR:    
+            /* end response if we have yet to do so.
+             */
+            cid->r->status = HTTP_INTERNAL_SERVER_ERROR;
+            break;
+
+        default:
+            /* TODO: log unrecognized retval for debugging 
+             */
+            cid->r->status = HTTP_INTERNAL_SERVER_ERROR;
+            break;
     }
-    else {
-        return "ISAPIAppendLogToQuery must be on or off";
+
+    /* Set the status (for logging) */
+    if (cid->ecb->dwHttpStatusCode) {
+        cid->r->status = cid->ecb->dwHttpStatusCode;
     }
-    return NULL;
+
+    /* All done with the DLL... get rid of it... */
+    isapi_unload(isa, FALSE);
+    
+    return OK;         /* NOT r->status, even if it has changed. */
 }
 
-static const char *isapi_cmd_cachefile(cmd_parms *cmd, void *dummy, 
-                                       const char *filename)
+/**********************************************************
+ *
+ *  ISAPI Module Setup Hooks
+ *
+ **********************************************************/
 
+static int compare_loaded(const void *av, const void *bv)
 {
-    isapi_server_conf *sconf = ap_get_module_config(cmd->server->module_config, 
+    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_server_conf *sconf = ap_get_module_config(s->module_config, 
                                                     &isapi_module);
-    isapi_loaded *isa, **newisa;
-    apr_finfo_t tmp;
-    apr_status_t rv;
-    char *fspec;
-    
-    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);
-       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);
-       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);
-       return NULL;
-    }
+    isapi_loaded **elts = (isapi_loaded **)sconf->loaded->elts;
+    int nelts = sconf->loaded->nelts;
 
-    /* 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", fspec);
-       return NULL;
-    }
+    /* sort the elements of the main_server, by filename */
+    qsort(elts, nelts, sizeof(isapi_loaded*), compare_loaded);
 
-    /* Add to cached list of loaded modules */
-    newisa = apr_array_push(sconf->loaded);
-    *newisa = isa;
-    
-    return NULL;
+    /* and make all virtualhosts share the same */
+    for (s = s->next; s; s = s->next) {
+       ap_set_module_config(s->module_config, &isapi_module, sconf);
+    }
+    return OK;
 }
 
 static void isapi_hooks(apr_pool_t *cont)
@@ -1268,20 +1294,6 @@ static void isapi_hooks(apr_pool_t *cont)
     ap_hook_handler(isapi_handler, NULL, NULL, APR_HOOK_MIDDLE);
 }
 
-static const command_rec isapi_cmds[] = {
-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 }
-};
-
 module AP_MODULE_DECLARE_DATA isapi_module = {
    STANDARD20_MODULE_STUFF,
    NULL,                        /* create per-dir config */