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/13 23:00:59 UTC

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

wrowe       02/05/13 14:00:59

  Modified:    modules/arch/win32 mod_isapi.c
  Log:
    Part one of a several-fold overhaul to isapi.  This part simply
    normalizes the module to the usual order of config-cmds/helpers/
    handlers/setup.
  
  Revision  Changes    Path
  1.65      +431 -419  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.64
  retrieving revision 1.65
  diff -u -r1.64 -r1.65
  --- mod_isapi.c	26 Apr 2002 22:23:06 -0000	1.64
  +++ mod_isapi.c	13 May 2002 21:00:58 -0000	1.65
  @@ -100,29 +100,15 @@
   #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 @@
       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 @@
       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 @@
       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 @@
       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;    
  -
  -    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;
  +    request_rec  *r;
  +    PFN_HSE_IO_COMPLETION completion;
  +    PVOID  completion_arg;
  +    HANDLE complete;
  +} isapi_cid;
   
  -    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 @@
                                      (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 @@
               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 @@
                                                  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 @@
       }
   }
   
  -/*
  - * 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;
  +/**********************************************************
  + *
  + *  ISAPI Module request invocation section
  + *
  + **********************************************************/
   
  -    if (((val = strtol(arg, (char **) &scan, 10)) <= 0) || *scan)
  -        return "ISAPIReadAheadBuffer must be a legitimate value.";
  +apr_status_t isapi_handler (request_rec *r)
  +{
  +    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;
       
  -    sconf->ReadAheadBuffer = val;
  -    return NULL;
  -}
  +    if(strcmp(r->handler, "isapi-isa"))
  +        return DECLINED;    
   
  -/*
  - * 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);
  +    sconf = ap_get_module_config(r->server->module_config, &isapi_module);
  +    e = r->subprocess_env;
   
  -    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;
  -}
  +    /* 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;
   
  -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 (r->finfo.filetype == APR_NOFILE)
  +        return HTTP_NOT_FOUND;
   
  -    if (strcasecmp(arg, "on") == 0) {
  -        sconf->AppendLogToErrors = -1;
  +    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)
  +{
  +    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(cmd->server->module_config, 
  +    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)
  @@ -1267,20 +1293,6 @@
       ap_hook_post_config(isapi_post_config, NULL, NULL, APR_HOOK_MIDDLE);
       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,