Return-Path: Delivered-To: apmail-httpd-cvs-archive@httpd.apache.org Received: (qmail 46526 invoked by uid 500); 14 May 2002 05:23:56 -0000 Mailing-List: contact cvs-help@httpd.apache.org; run by ezmlm Precedence: bulk Reply-To: dev@httpd.apache.org list-help: list-unsubscribe: list-post: Delivered-To: mailing list cvs@httpd.apache.org Received: (qmail 46515 invoked by uid 500); 14 May 2002 05:23:55 -0000 Delivered-To: apmail-httpd-2.0-cvs@apache.org Date: 14 May 2002 05:23:54 -0000 Message-ID: <20020514052354.30340.qmail@icarus.apache.org> From: wrowe@apache.org To: httpd-2.0-cvs@apache.org Subject: cvs commit: httpd-2.0/modules/arch/win32 mod_isapi.h mod_isapi.c mod_isapi.dsp X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N 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 + * by Alexei Kosut , significant overhauls and + * redesign by William Rowe , 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 +/* 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 + * + * ISAPICacheFile myapp.dll anotherapp.dll thirdapp.dll + * + * 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 * . * * 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 */