mirror of
https://github.com/apache/httpd.git
synced 2025-09-01 02:02:06 +03:00
Another day's progress... little changes, but just establishing the
baseline for fully implementing extensions. Filters... that's still a little way away. This demonstrates the HSE_STATUS_PENDING result wait event trap, so we let another thread keep working till we get the HSE_REQ_DONE_WITH_SESSION request. Cleaned up callback names and tossed bunches of stuff in the isapi_cid (connection/request) and the isapi_loaded (dll details) for future caching optimizations. PR: Obtained from: Submitted by: Reviewed by: git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@85903 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
@@ -100,14 +100,6 @@
|
|||||||
|
|
||||||
module isapi_module;
|
module isapi_module;
|
||||||
|
|
||||||
/* Our "Connection ID" structure */
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
LPEXTENSION_CONTROL_BLOCK ecb;
|
|
||||||
request_rec *r;
|
|
||||||
int status;
|
|
||||||
} isapi_cid;
|
|
||||||
|
|
||||||
/* Declare the ISAPI functions */
|
/* Declare the ISAPI functions */
|
||||||
|
|
||||||
BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
|
BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
|
||||||
@@ -122,113 +114,172 @@ BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
|
|||||||
/*
|
/*
|
||||||
The optimiser blows it totally here. What happens is that autos are addressed relative to the
|
The optimiser blows it totally here. What happens is that autos are addressed relative to the
|
||||||
stack pointer, which, of course, moves around. The optimiser seems to lose track of it somewhere
|
stack pointer, which, of course, moves around. The optimiser seems to lose track of it somewhere
|
||||||
between setting isapi_entry and calling through it. We work around the problem by forcing it to
|
between setting HttpExtensionProc's address and calling through it. We work around the problem by
|
||||||
use frame pointers.
|
forcing it to use frame pointers.
|
||||||
|
|
||||||
|
The revisions below may eliminate this artifact.
|
||||||
*/
|
*/
|
||||||
#pragma optimize("y",off)
|
#pragma optimize("y",off)
|
||||||
|
|
||||||
int isapi_handler (request_rec *r)
|
/* Our loaded isapi module description structure */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
HINSTANCE handle;
|
||||||
|
HSE_VERSION_INFO *pVer;
|
||||||
|
PFN_GETEXTENSIONVERSION GetExtensionVersion;
|
||||||
|
PFN_HTTPEXTENSIONPROC HttpExtensionProc;
|
||||||
|
PFN_TERMINATEEXTENSION TerminateExtension;
|
||||||
|
int refcount;
|
||||||
|
DWORD timeout;
|
||||||
|
BOOL fakeasync;
|
||||||
|
DWORD reportversion;
|
||||||
|
} isapi_loaded;
|
||||||
|
|
||||||
|
/* Our "Connection ID" structure */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
LPEXTENSION_CONTROL_BLOCK ecb;
|
||||||
|
isapi_loaded *isa;
|
||||||
|
request_rec *r;
|
||||||
|
PFN_HSE_IO_COMPLETION completion;
|
||||||
|
PVOID completion_arg;
|
||||||
|
HANDLE complete;
|
||||||
|
ap_status_t retval;
|
||||||
|
} isapi_cid;
|
||||||
|
|
||||||
|
ap_status_t isapi_handler (request_rec *r)
|
||||||
{
|
{
|
||||||
ap_status_t rv;
|
|
||||||
|
|
||||||
LPEXTENSION_CONTROL_BLOCK ecb =
|
|
||||||
ap_pcalloc(r->pool, sizeof(struct _EXTENSION_CONTROL_BLOCK));
|
|
||||||
HSE_VERSION_INFO *pVer = ap_pcalloc(r->pool, sizeof(HSE_VERSION_INFO));
|
|
||||||
|
|
||||||
HINSTANCE isapi_handle;
|
|
||||||
BOOL (*isapi_version)(HSE_VERSION_INFO *); /* entry point 1 */
|
|
||||||
DWORD (*isapi_entry)(LPEXTENSION_CONTROL_BLOCK); /* entry point 2 */
|
|
||||||
BOOL (*isapi_term)(DWORD); /* optional entry point 3 */
|
|
||||||
|
|
||||||
isapi_cid *cid = ap_pcalloc(r->pool, sizeof(isapi_cid));
|
|
||||||
ap_table_t *e = r->subprocess_env;
|
ap_table_t *e = r->subprocess_env;
|
||||||
int retval;
|
isapi_loaded *isa;
|
||||||
|
isapi_cid *cid;
|
||||||
/* Use similar restrictions as CGIs */
|
|
||||||
|
|
||||||
|
/* Use similar restrictions as CGIs
|
||||||
|
*
|
||||||
|
* 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;
|
return HTTP_FORBIDDEN;
|
||||||
|
|
||||||
if (r->finfo.protection == 0)
|
if (r->finfo.protection == 0)
|
||||||
return HTTP_NOT_FOUND;
|
return HTTP_NOT_FOUND;
|
||||||
|
|
||||||
if (r->finfo.filetype == APR_DIR)
|
if (r->finfo.filetype == APR_DIR)
|
||||||
return HTTP_FORBIDDEN;
|
return HTTP_FORBIDDEN;
|
||||||
|
|
||||||
/* Load the module */
|
/* Load the module
|
||||||
|
*
|
||||||
|
* TODO: Critical section
|
||||||
|
*
|
||||||
|
* Warning: cid should not be allocated from pool if we
|
||||||
|
* cache the isapi process in-memory.
|
||||||
|
*
|
||||||
|
* This code could use cacheing... everything that follows
|
||||||
|
* should only be performed on the first isapi dll invocation,
|
||||||
|
* not with every HttpExtensionProc()
|
||||||
|
*/
|
||||||
|
isa = ap_pcalloc(r->pool, sizeof(isapi_module));
|
||||||
|
isa->pVer = ap_pcalloc(r->pool, sizeof(HSE_VERSION_INFO));
|
||||||
|
isa->refcount = 0;
|
||||||
|
|
||||||
if (!(isapi_handle = LoadLibraryEx(r->filename, NULL,
|
/* TODO: These may need to become overrideable, so that we
|
||||||
LOAD_WITH_ALTERED_SEARCH_PATH))) {
|
* assure a given isapi can be fooled into behaving well.
|
||||||
rv = GetLastError();
|
*/
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_ALERT, rv, r,
|
isa->timeout = INFINITE; /* microsecs */
|
||||||
"Could not load DLL: %s", r->filename);
|
isa->fakeasync = TRUE;
|
||||||
return HTTP_INTERNAL_SERVER_ERROR;
|
isa->reportversion = MAKELONG(0, 5); /* Revision 5.0 */
|
||||||
|
|
||||||
|
if (!(isa->handle = LoadLibraryEx(r->filename, NULL,
|
||||||
|
LOAD_WITH_ALTERED_SEARCH_PATH))) {
|
||||||
|
ap_log_rerror(APLOG_MARK, APLOG_ALERT, GetLastError(), r,
|
||||||
|
"ISAPI %s failed to load", r->filename);
|
||||||
|
return HTTP_INTERNAL_SERVER_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(isapi_version =
|
if (!(isa->GetExtensionVersion =
|
||||||
(void *)(GetProcAddress(isapi_handle, "GetExtensionVersion")))) {
|
(void *)(GetProcAddress(isa->handle, "GetExtensionVersion")))) {
|
||||||
rv = GetLastError();
|
ap_log_rerror(APLOG_MARK, APLOG_ALERT, GetLastError(), r,
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_ALERT, rv, r,
|
"ISAPI %s is missing GetExtensionVersion()",
|
||||||
"Could not load DLL %s symbol GetExtensionVersion()",
|
|
||||||
r->filename);
|
r->filename);
|
||||||
FreeLibrary(isapi_handle);
|
FreeLibrary(isa->handle);
|
||||||
return HTTP_INTERNAL_SERVER_ERROR;
|
return HTTP_INTERNAL_SERVER_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(isapi_entry =
|
if (!(isa->HttpExtensionProc =
|
||||||
(void *)(GetProcAddress(isapi_handle, "HttpExtensionProc")))) {
|
(void *)(GetProcAddress(isa->handle, "HttpExtensionProc")))) {
|
||||||
rv = GetLastError();
|
ap_log_rerror(APLOG_MARK, APLOG_ALERT, GetLastError(), r,
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_ALERT, rv, r,
|
"ISAPI %s is missing HttpExtensionProc()",
|
||||||
"Could not load DLL %s symbol HttpExtensionProc()",
|
|
||||||
r->filename);
|
r->filename);
|
||||||
FreeLibrary(isapi_handle);
|
FreeLibrary(isa->handle);
|
||||||
return HTTP_INTERNAL_SERVER_ERROR;
|
return HTTP_INTERNAL_SERVER_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TerminateExtension() is an optional interface */
|
/* TerminateExtension() is an optional interface */
|
||||||
|
|
||||||
isapi_term = (void *)(GetProcAddress(isapi_handle, "TerminateExtension"));
|
isa->TerminateExtension = (void *)(GetProcAddress(isa->handle, "TerminateExtension"));
|
||||||
|
|
||||||
/* Run GetExtensionVersion() */
|
/* Run GetExtensionVersion() */
|
||||||
|
|
||||||
if (!(*isapi_version)(pVer)) {
|
if (!(*isa->GetExtensionVersion)(isa->pVer)) {
|
||||||
/* ### euh... we're passing the wrong type of error code here */
|
/* ### euh... we're passing the wrong type of error code here */
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_ALERT, HTTP_INTERNAL_SERVER_ERROR, r,
|
ap_log_rerror(APLOG_MARK, APLOG_ALERT, HTTP_INTERNAL_SERVER_ERROR, r,
|
||||||
"ISAPI %s GetExtensionVersion() call failed", r->filename);
|
"ISAPI %s call GetExtensionVersion() failed",
|
||||||
FreeLibrary(isapi_handle);
|
r->filename);
|
||||||
return HTTP_INTERNAL_SERVER_ERROR;
|
FreeLibrary(isa->handle);
|
||||||
|
return HTTP_INTERNAL_SERVER_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Load of this module completed, this is the point at which *isa
|
||||||
|
* could be cached for later invocation.
|
||||||
|
*
|
||||||
|
* on to invoking this request...
|
||||||
|
*/
|
||||||
|
|
||||||
/* Set up variables */
|
/* Set up variables */
|
||||||
ap_add_common_vars(r);
|
ap_add_common_vars(r);
|
||||||
ap_add_cgi_vars(r);
|
ap_add_cgi_vars(r);
|
||||||
|
|
||||||
/* Set up connection ID */
|
/* Set up connection structure and ecb */
|
||||||
ecb->ConnID = (HCONN)cid;
|
cid = ap_pcalloc(r->pool, sizeof(isapi_cid));
|
||||||
cid->ecb = ecb;
|
cid->ecb = ap_pcalloc(r->pool, sizeof(struct _EXTENSION_CONTROL_BLOCK));
|
||||||
|
cid->ecb->ConnID = (HCONN)cid;
|
||||||
|
/* TODO: Critical section */
|
||||||
|
++isa->refcount;
|
||||||
|
cid->isa = isa;
|
||||||
cid->r = r;
|
cid->r = r;
|
||||||
cid->status = 0;
|
cid->r->status = 0;
|
||||||
|
cid->complete = NULL;
|
||||||
|
cid->completion = NULL;
|
||||||
|
cid->retval = APR_SUCCESS;
|
||||||
|
|
||||||
ecb->cbSize = sizeof(struct _EXTENSION_CONTROL_BLOCK);
|
cid->ecb->cbSize = sizeof(EXTENSION_CONTROL_BLOCK);
|
||||||
ecb->dwVersion = MAKELONG(0, 2);
|
cid->ecb->dwVersion = isa->reportversion;
|
||||||
ecb->dwHttpStatusCode = 0;
|
cid->ecb->dwHttpStatusCode = 0;
|
||||||
strcpy(ecb->lpszLogData, "");
|
strcpy(cid->ecb->lpszLogData, "");
|
||||||
// TODO: is a copy needed here?
|
// TODO: are copies really needed here?
|
||||||
ecb->lpszMethod = (char*) r->method;
|
cid->ecb->lpszMethod = ap_pstrdup(r->pool, (char*) r->method);
|
||||||
// TODO: is a copy needed here?
|
cid->ecb->lpszQueryString = ap_pstrdup(r->pool,
|
||||||
ecb->lpszQueryString = (char*) ap_table_get(e, "QUERY_STRING");
|
(char*) ap_table_get(e, "QUERY_STRING"));
|
||||||
// TODO: is a copy needed here?
|
cid->ecb->lpszPathInfo = ap_pstrdup(r->pool,
|
||||||
ecb->lpszPathInfo = (char*) ap_table_get(e, "PATH_INFO");
|
(char*) ap_table_get(e, "PATH_INFO"));
|
||||||
// TODO: is a copy needed here?
|
cid->ecb->lpszPathTranslated = ap_pstrdup(r->pool,
|
||||||
ecb->lpszPathTranslated = (char*) ap_table_get(e, "PATH_TRANSLATED");
|
(char*) ap_table_get(e, "PATH_TRANSLATED"));
|
||||||
// TODO: is a copy needed here?
|
cid->ecb->lpszContentType = ap_pstrdup(r->pool,
|
||||||
ecb->lpszContentType = (char*) ap_table_get(e, "CONTENT_TYPE");
|
(char*) ap_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 */
|
/* Set up client input */
|
||||||
if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) {
|
cid->retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR);
|
||||||
if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
|
if (cid->retval) {
|
||||||
FreeLibrary(isapi_handle);
|
if (isa->TerminateExtension) {
|
||||||
return retval;
|
(*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD);
|
||||||
|
}
|
||||||
|
FreeLibrary(isa->handle);
|
||||||
|
return cid->retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ap_should_client_block(r)) {
|
if (ap_should_client_block(r)) {
|
||||||
@@ -249,16 +300,18 @@ int isapi_handler (request_rec *r)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
if (to_read > 49152) {
|
if (to_read > 49152) {
|
||||||
if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
|
if (isa->TerminateExtension)
|
||||||
FreeLibrary(isapi_handle);
|
(*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD);
|
||||||
|
FreeLibrary(isa->handle);
|
||||||
return HTTP_REQUEST_ENTITY_TOO_LARGE;
|
return HTTP_REQUEST_ENTITY_TOO_LARGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
ecb->lpbData = ap_pcalloc(r->pool, 1 + to_read);
|
cid->ecb->lpbData = ap_pcalloc(r->pool, 1 + to_read);
|
||||||
|
|
||||||
if ((read = ap_get_client_block(r, ecb->lpbData, to_read)) < 0) {
|
if ((read = ap_get_client_block(r, cid->ecb->lpbData, to_read)) < 0) {
|
||||||
if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
|
if (isa->TerminateExtension)
|
||||||
FreeLibrary(isapi_handle);
|
(*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD);
|
||||||
|
FreeLibrary(isa->handle);
|
||||||
return HTTP_INTERNAL_SERVER_ERROR;
|
return HTTP_INTERNAL_SERVER_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,77 +320,96 @@ int isapi_handler (request_rec *r)
|
|||||||
* cbAvailable matches cbTotalBytes, we'll up the latter
|
* cbAvailable matches cbTotalBytes, we'll up the latter
|
||||||
* and equalize them.
|
* and equalize them.
|
||||||
*/
|
*/
|
||||||
ecb->cbAvailable = ecb->cbTotalBytes = read + 1;
|
cid->ecb->cbAvailable = cid->ecb->cbTotalBytes = read + 1;
|
||||||
ecb->lpbData[read] = '\0';
|
cid->ecb->lpbData[read] = '\0';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ecb->cbTotalBytes = 0;
|
cid->ecb->cbTotalBytes = 0;
|
||||||
ecb->cbAvailable = 0;
|
cid->ecb->cbAvailable = 0;
|
||||||
ecb->lpbData = NULL;
|
cid->ecb->lpbData = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set up the callbacks */
|
/* All right... try and run the sucker */
|
||||||
|
cid->retval = (*isa->HttpExtensionProc)(cid->ecb);
|
||||||
ecb->GetServerVariable = &GetServerVariable;
|
|
||||||
ecb->WriteClient = &WriteClient;
|
|
||||||
ecb->ReadClient = &ReadClient;
|
|
||||||
ecb->ServerSupportFunction = &ServerSupportFunction;
|
|
||||||
|
|
||||||
/* All right... try and load the sucker */
|
|
||||||
retval = (*isapi_entry)(ecb);
|
|
||||||
|
|
||||||
/* Set the status (for logging) */
|
/* Set the status (for logging) */
|
||||||
if (ecb->dwHttpStatusCode)
|
if (cid->ecb->dwHttpStatusCode) {
|
||||||
r->status = ecb->dwHttpStatusCode;
|
cid->r->status = cid->ecb->dwHttpStatusCode;
|
||||||
|
}
|
||||||
|
|
||||||
/* Check for a log message - and log it */
|
/* Check for a log message - and log it */
|
||||||
if (ecb->lpszLogData && strcmp(ecb->lpszLogData, ""))
|
if (cid->ecb->lpszLogData && *cid->ecb->lpszLogData)
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
|
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
|
||||||
"ISAPI %s: %s", r->filename, ecb->lpszLogData);
|
"ISAPI %s: %s", r->filename, cid->ecb->lpszLogData);
|
||||||
|
|
||||||
/* All done with the DLL... get rid of it */
|
switch(cid->retval) {
|
||||||
if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
|
|
||||||
FreeLibrary(isapi_handle);
|
|
||||||
|
|
||||||
switch(retval) {
|
|
||||||
case HSE_STATUS_SUCCESS:
|
case HSE_STATUS_SUCCESS:
|
||||||
/* TODO: If content length was missing or incorrect, and the response
|
|
||||||
* was not chunked, we need to close the connection here.
|
|
||||||
* If the response was chunked, and no closing chunk was sent, we aught
|
|
||||||
* to transmit one here
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* fall through... */
|
|
||||||
case HSE_STATUS_SUCCESS_AND_KEEP_CONN:
|
case HSE_STATUS_SUCCESS_AND_KEEP_CONN:
|
||||||
/* Ignore the keepalive stuff; Apache handles it just fine without
|
/* Ignore the keepalive stuff; Apache handles it just fine without
|
||||||
* the ISA's "advice".
|
* 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;
|
||||||
if (cid->status) /* We have a special status to return */
|
|
||||||
return cid->status;
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
|
|
||||||
case HSE_STATUS_PENDING:
|
case HSE_STATUS_PENDING:
|
||||||
/* We don't support this, but we need to... we should simply create a
|
/* emulating async behavior...
|
||||||
* wait event and die on timeout or resume with the callback to our
|
*
|
||||||
* ServerSupportFunction with HSE_REQ_DONE_WITH_SESSION to emulate
|
* Create a cid->completed event and wait on it for some timeout
|
||||||
* async behavior.
|
* so that the app thinks is it running async.
|
||||||
|
*
|
||||||
|
* All async ServerSupportFunction calls will be handled through
|
||||||
|
* the registered IO_COMPLETION hook.
|
||||||
*/
|
*/
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_WARNING, APR_ENOTIMPL, r,
|
|
||||||
"ISAPI asynchronous I/O not supported: %s", r->filename);
|
if (!isa->fakeasync) {
|
||||||
|
ap_log_rerror(APLOG_MARK, APLOG_WARNING, APR_ENOTIMPL, r,
|
||||||
|
"ISAPI %s asynch I/O request refused",
|
||||||
|
r->filename);
|
||||||
|
cid->retval = APR_ENOTIMPL;
|
||||||
|
}
|
||||||
|
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 it's death? For now leave timeout = -1
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case HSE_STATUS_ERROR:
|
case HSE_STATUS_ERROR:
|
||||||
/* end response if we have yet to do so.
|
/* end response if we have yet to do so.
|
||||||
*/
|
*/
|
||||||
return HTTP_INTERNAL_SERVER_ERROR;
|
cid->retval = HTTP_INTERNAL_SERVER_ERROR;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
/* TODO: log unrecognized retval for debugging
|
/* TODO: log unrecognized retval for debugging
|
||||||
*/
|
*/
|
||||||
return HTTP_INTERNAL_SERVER_ERROR;
|
cid->retval = HTTP_INTERNAL_SERVER_ERROR;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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->TerminateExtension) {
|
||||||
|
(*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD);
|
||||||
|
}
|
||||||
|
FreeLibrary(isa->handle);
|
||||||
|
/* TODO: Crit section */
|
||||||
|
cid->isa = NULL;
|
||||||
|
--isa->refcount;
|
||||||
|
isa->handle = NULL;
|
||||||
|
|
||||||
|
return cid->retval;
|
||||||
}
|
}
|
||||||
#pragma optimize("",on)
|
#pragma optimize("",on)
|
||||||
|
|
||||||
@@ -353,33 +425,33 @@ BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
if (!strcasecmp(lpszVariableName, "UNMAPPED_REMOTE_USER")) {
|
if (!strcasecmp(lpszVariableName, "UNMAPPED_REMOTE_USER")) {
|
||||||
/* We don't support NT users, so this is always the same as
|
/* We don't support NT users, so this is always the same as
|
||||||
* REMOTE_USER
|
* REMOTE_USER
|
||||||
*/
|
*/
|
||||||
result = ap_table_get(e, "REMOTE_USER");
|
result = ap_table_get(e, "REMOTE_USER");
|
||||||
}
|
}
|
||||||
else if (!strcasecmp(lpszVariableName, "SERVER_PORT_SECURE")) {
|
else if (!strcasecmp(lpszVariableName, "SERVER_PORT_SECURE")) {
|
||||||
/* Apache doesn't support secure requests inherently, so
|
/* Apache doesn't support secure requests inherently, so
|
||||||
* we have no way of knowing. We'll be conservative, and say
|
* we have no way of knowing. We'll be conservative, and say
|
||||||
* all requests are insecure.
|
* all requests are insecure.
|
||||||
*/
|
*/
|
||||||
result = "0";
|
result = "0";
|
||||||
}
|
}
|
||||||
else if (!strcasecmp(lpszVariableName, "URL")) {
|
else if (!strcasecmp(lpszVariableName, "URL")) {
|
||||||
result = r->uri;
|
result = r->uri;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result = ap_table_get(e, lpszVariableName);
|
result = ap_table_get(e, lpszVariableName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
if (strlen(result) > *lpdwSizeofBuffer) {
|
if (strlen(result) > *lpdwSizeofBuffer) {
|
||||||
*lpdwSizeofBuffer = strlen(result);
|
*lpdwSizeofBuffer = strlen(result);
|
||||||
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
strncpy(lpvBuffer, result, *lpdwSizeofBuffer);
|
strncpy(lpvBuffer, result, *lpdwSizeofBuffer);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Didn't find it */
|
/* Didn't find it */
|
||||||
@@ -388,22 +460,22 @@ BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
|
|||||||
}
|
}
|
||||||
|
|
||||||
BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
|
BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
|
||||||
DWORD dwReserved)
|
DWORD dwReserved)
|
||||||
{
|
{
|
||||||
request_rec *r = ((isapi_cid *)ConnID)->r;
|
request_rec *r = ((isapi_cid *)ConnID)->r;
|
||||||
int writ; /* written, actually, but why shouldn't I make up words? */
|
int writ; /* written, actually, but why shouldn't I make up words? */
|
||||||
|
|
||||||
/* We only support synchronous writing */
|
/* We only support synchronous writing */
|
||||||
if (dwReserved && dwReserved != HSE_IO_SYNC) {
|
if (dwReserved && dwReserved != HSE_IO_SYNC) {
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_WARNING, ERROR_INVALID_PARAMETER, r,
|
ap_log_rerror(APLOG_MARK, APLOG_WARNING, ERROR_INVALID_PARAMETER, r,
|
||||||
"ISAPI asynchronous I/O not supported: %s", r->filename);
|
"ISAPI %s asynch write", r->filename);
|
||||||
SetLastError(ERROR_INVALID_PARAMETER);
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((writ = ap_rwrite(Buffer, *lpwdwBytes, r)) == EOF) {
|
if ((writ = ap_rwrite(Buffer, *lpwdwBytes, r)) == EOF) {
|
||||||
SetLastError(WSAEDISCON); /* TODO: Find the right error code */
|
SetLastError(WSAEDISCON); /* TODO: Find the right error code */
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
*lpwdwBytes = writ;
|
*lpwdwBytes = writ;
|
||||||
@@ -418,13 +490,103 @@ BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize)
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char* ComposeHeaders(request_rec *r, char* data)
|
||||||
|
{
|
||||||
|
/* We *should* break before this while loop ends */
|
||||||
|
while (*data)
|
||||||
|
{
|
||||||
|
char *value, *lf = strchr(data, '\n');
|
||||||
|
int p;
|
||||||
|
|
||||||
|
#ifdef RELAX_HEADER_RULE
|
||||||
|
if (lf)
|
||||||
|
*lf = '\0';
|
||||||
|
#else
|
||||||
|
if (!lf) { /* Huh? Invalid data, I think */
|
||||||
|
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
|
||||||
|
"ISAPI %s sent invalid headers", r->filename);
|
||||||
|
SetLastError(TODO_ERROR);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get rid of \n and \r */
|
||||||
|
*lf = '\0';
|
||||||
|
#endif
|
||||||
|
p = strlen(data);
|
||||||
|
if (p > 0 && data[p-1] == '\r') data[p-1] = '\0';
|
||||||
|
|
||||||
|
/* End of headers */
|
||||||
|
if (*data == '\0') {
|
||||||
|
#ifdef RELAX_HEADER_RULE
|
||||||
|
if (lf)
|
||||||
|
#endif
|
||||||
|
data = lf + 1; /* Reset data */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(value = strchr(data, ':'))) {
|
||||||
|
SetLastError(TODO_ERROR);
|
||||||
|
/* ### euh... we're passing the wrong type of error
|
||||||
|
### code here */
|
||||||
|
ap_log_rerror(APLOG_MARK, APLOG_ERR, HTTP_INTERNAL_SERVER_ERROR, r,
|
||||||
|
"ISAPI %s sent invalid headers", r->filename);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*value++ = '\0';
|
||||||
|
while (*value && ap_isspace(*value)) ++value;
|
||||||
|
|
||||||
|
/* Check all the special-case headers. Similar to what
|
||||||
|
* ap_scan_script_header_err() does (see that function for
|
||||||
|
* more detail)
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!strcasecmp(data, "Content-Type"))
|
||||||
|
{
|
||||||
|
/* Nuke trailing whitespace */
|
||||||
|
char *tmp;
|
||||||
|
char *endp = value + strlen(value) - 1;
|
||||||
|
while (endp > value && ap_isspace(*endp))
|
||||||
|
*endp-- = '\0';
|
||||||
|
|
||||||
|
tmp = ap_pstrdup (r->pool, value);
|
||||||
|
ap_str_tolower(tmp);
|
||||||
|
r->content_type = tmp;
|
||||||
|
}
|
||||||
|
else if (!strcasecmp(data, "Content-Length")) {
|
||||||
|
ap_table_set(r->headers_out, data, value);
|
||||||
|
}
|
||||||
|
else if (!strcasecmp(data, "Transfer-Encoding")) {
|
||||||
|
ap_table_set(r->headers_out, data, value);
|
||||||
|
}
|
||||||
|
else if (!strcasecmp(data, "Set-Cookie")) {
|
||||||
|
ap_table_add(r->err_headers_out, data, value);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ap_table_merge(r->err_headers_out, data, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset data */
|
||||||
|
#ifdef RELAX_HEADER_RULE
|
||||||
|
if (!lf) {
|
||||||
|
data += p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
data = lf + 1;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* XXX: There is an O(n^2) attack possible here. */
|
/* XXX: There is an O(n^2) attack possible here. */
|
||||||
BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
|
BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
|
||||||
LPVOID lpvBuffer, LPDWORD lpdwSize,
|
LPVOID lpvBuffer, LPDWORD lpdwSize,
|
||||||
LPDWORD lpdwDataType)
|
LPDWORD lpdwDataType)
|
||||||
{
|
{
|
||||||
isapi_cid *cid = (isapi_cid *)hConn;
|
isapi_cid *cid = (isapi_cid *)hConn;
|
||||||
request_rec *subreq, *r = cid->r;
|
request_rec *r = cid->r;
|
||||||
|
request_rec *subreq;
|
||||||
char *data;
|
char *data;
|
||||||
|
|
||||||
switch (dwHSERequest) {
|
switch (dwHSERequest) {
|
||||||
@@ -433,8 +595,8 @@ BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
|
|||||||
* is done.
|
* is done.
|
||||||
*/
|
*/
|
||||||
ap_table_set (r->headers_out, "Location", lpvBuffer);
|
ap_table_set (r->headers_out, "Location", lpvBuffer);
|
||||||
cid->status = cid->r->status = cid->ecb->dwHttpStatusCode =
|
cid->r->status = cid->ecb->dwHttpStatusCode
|
||||||
HTTP_MOVED_TEMPORARILY;
|
= HTTP_MOVED_TEMPORARILY;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
case HSE_REQ_SEND_URL:
|
case HSE_REQ_SEND_URL:
|
||||||
@@ -456,27 +618,6 @@ BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
|
|||||||
ap_internal_redirect((char *)lpvBuffer, r);
|
ap_internal_redirect((char *)lpvBuffer, r);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
case HSE_REQ_SEND_RESPONSE_HEADER_EX:
|
|
||||||
SetLastError(ERROR_INVALID_PARAMETER);
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
/* Not yet - but here's the idea...
|
|
||||||
if (((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszStatus
|
|
||||||
&& ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchStatus)
|
|
||||||
r->status_line = ap_pstrndup(r->pool,
|
|
||||||
((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszStatus,
|
|
||||||
((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchStatus);
|
|
||||||
else
|
|
||||||
r->status_line = ap_pstrdup(r->pool, "200 OK");
|
|
||||||
sscanf(r->status_line, "%d", &r->status);
|
|
||||||
cid->ecb->dwHttpStatusCode = r->status;
|
|
||||||
|
|
||||||
* plus we need to handle...
|
|
||||||
* ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszHeader; // HTTP header
|
|
||||||
* ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchHeader; // HTTP header len
|
|
||||||
* ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->fKeepConn; // Keep alive? (bool)
|
|
||||||
*/
|
|
||||||
|
|
||||||
case HSE_REQ_SEND_RESPONSE_HEADER:
|
case HSE_REQ_SEND_RESPONSE_HEADER:
|
||||||
r->status_line = lpvBuffer ? lpvBuffer : ap_pstrdup(r->pool, "200 OK");
|
r->status_line = lpvBuffer ? lpvBuffer : ap_pstrdup(r->pool, "200 OK");
|
||||||
sscanf(r->status_line, "%d", &r->status);
|
sscanf(r->status_line, "%d", &r->status);
|
||||||
@@ -493,102 +634,31 @@ BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
|
|||||||
ap_send_http_header(r);
|
ap_send_http_header(r);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make a copy - don't disturb the original */
|
/* Make a copy - don't disturb the original */
|
||||||
data = ap_pstrdup(r->pool, (char *)lpdwDataType);
|
data = ap_pstrdup(r->pool, (char *)lpdwDataType);
|
||||||
|
|
||||||
/* We *should* break before this while loop ends */
|
|
||||||
while (*data) {
|
|
||||||
char *value, *lf = strchr(data, '\n');
|
|
||||||
int p;
|
|
||||||
|
|
||||||
#ifdef RELAX_HEADER_RULE
|
|
||||||
if (lf)
|
|
||||||
*lf = '\0';
|
|
||||||
#else
|
|
||||||
if (!lf) { /* Huh? Invalid data, I think */
|
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
|
|
||||||
"ISA sent invalid headers: %s", r->filename);
|
|
||||||
SetLastError(TODO_ERROR);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get rid of \n and \r */
|
|
||||||
*lf = '\0';
|
|
||||||
#endif
|
|
||||||
p = strlen(data);
|
|
||||||
if (p > 0 && data[p-1] == '\r') data[p-1] = '\0';
|
|
||||||
|
|
||||||
/* End of headers */
|
|
||||||
if (*data == '\0') {
|
|
||||||
#ifdef RELAX_HEADER_RULE
|
|
||||||
if (lf)
|
|
||||||
#endif
|
|
||||||
data = lf + 1; /* Reset data */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(value = strchr(data, ':'))) {
|
|
||||||
SetLastError(TODO_ERROR);
|
|
||||||
/* ### euh... we're passing the wrong type of error
|
|
||||||
### code here */
|
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_ERR,
|
|
||||||
HTTP_INTERNAL_SERVER_ERROR, r,
|
|
||||||
"ISA sent invalid headers", r->filename);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
*value++ = '\0';
|
|
||||||
while (*value && ap_isspace(*value)) ++value;
|
|
||||||
|
|
||||||
/* Check all the special-case headers. Similar to what
|
|
||||||
* ap_scan_script_header_err() does (see that function for
|
|
||||||
* more detail)
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (!strcasecmp(data, "Content-Type")) {
|
|
||||||
char *tmp;
|
|
||||||
/* Nuke trailing whitespace */
|
|
||||||
|
|
||||||
char *endp = value + strlen(value) - 1;
|
/* Parse them out, or die trying */
|
||||||
while (endp > value && ap_isspace(*endp)) *endp-- = '\0';
|
data = ComposeHeaders(r, data);
|
||||||
|
if (!data)
|
||||||
tmp = ap_pstrdup (r->pool, value);
|
return FALSE;
|
||||||
ap_str_tolower(tmp);
|
|
||||||
r->content_type = tmp;
|
|
||||||
}
|
|
||||||
else if (!strcasecmp(data, "Content-Length")) {
|
|
||||||
ap_table_set(r->headers_out, data, value);
|
|
||||||
}
|
|
||||||
else if (!strcasecmp(data, "Transfer-Encoding")) {
|
|
||||||
ap_table_set(r->headers_out, data, value);
|
|
||||||
}
|
|
||||||
else if (!strcasecmp(data, "Set-Cookie")) {
|
|
||||||
ap_table_add(r->err_headers_out, data, value);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ap_table_merge(r->err_headers_out, data, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reset data */
|
|
||||||
#ifdef RELAX_HEADER_RULE
|
|
||||||
if (!lf) {
|
|
||||||
data += p;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
data = lf + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* All the headers should be set now */
|
/* All the headers should be set now */
|
||||||
|
|
||||||
ap_send_http_header(r);
|
ap_send_http_header(r);
|
||||||
|
|
||||||
/* Any data left should now be sent directly */
|
/* Any data left should now be sent directly */
|
||||||
ap_rputs(data, r);
|
if (*data)
|
||||||
|
ap_rputs(data, r);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
|
case HSE_REQ_DONE_WITH_SESSION:
|
||||||
|
/* Signal to resume the thread completing this request
|
||||||
|
*/
|
||||||
|
if (cid->complete)
|
||||||
|
SetEvent(cid->complete);
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
case HSE_REQ_MAP_URL_TO_PATH:
|
case HSE_REQ_MAP_URL_TO_PATH:
|
||||||
/* Map a URL to a filename */
|
/* Map a URL to a filename */
|
||||||
subreq = ap_sub_req_lookup_uri(ap_pstrndup(r->pool, (char *)lpvBuffer,
|
subreq = ap_sub_req_lookup_uri(ap_pstrndup(r->pool, (char *)lpvBuffer,
|
||||||
@@ -607,23 +677,35 @@ BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
|
|||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
case HSE_REQ_DONE_WITH_SESSION:
|
case HSE_REQ_GET_SSPI_INFO:
|
||||||
/* TODO: Signal the main request with the event to complete the session
|
|
||||||
*/
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
/* We don't support all this async I/O, Microsoft-specific stuff */
|
|
||||||
case HSE_REQ_IO_COMPLETION:
|
|
||||||
SetLastError(ERROR_INVALID_PARAMETER);
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
case HSE_APPEND_LOG_PARAMETER:
|
||||||
|
/* Log lpvBuffer, of lpdwSize bytes, in the URI Query (cs-uri-query) field
|
||||||
|
* This code will do for now...
|
||||||
|
*/
|
||||||
|
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
|
||||||
|
"ISAPI %s: %s", cid->r->filename,
|
||||||
|
(char*) lpvBuffer);
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
case HSE_REQ_IO_COMPLETION:
|
||||||
/* TODO: Emulate a completion port, if we can...
|
/* TODO: Emulate a completion port, if we can...
|
||||||
* Record the callback address and user defined argument...
|
* Record the callback address and user defined argument...
|
||||||
* we will call this after any async request (including transmitfile)
|
* we will call this after any async request (e.g. transmitfile)
|
||||||
* as if the request had been async.
|
* as if the request had completed async execution.
|
||||||
|
* 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)
|
||||||
|
return FALSE;
|
||||||
|
cid->completion = (PFN_HSE_IO_COMPLETION) lpvBuffer;
|
||||||
|
cid->completion_arg = (PVOID) lpdwDataType;
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
case HSE_REQ_TRANSMIT_FILE:
|
case HSE_REQ_TRANSMIT_FILE:
|
||||||
/* Use TransmitFile (in leiu of WriteClient)... nothing wrong with that
|
/* Use TransmitFile... nothing wrong with that :)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* ### euh... we're passing the wrong type of error code here */
|
/* ### euh... we're passing the wrong type of error code here */
|
||||||
@@ -633,23 +715,139 @@ BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
|
|||||||
r->filename);
|
r->filename);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
case HSE_APPEND_LOG_PARAMETER:
|
case HSE_REQ_REFRESH_ISAPI_ACL:
|
||||||
/* Log lpvBuffer, of lpdwSize bytes */
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
case HSE_REQ_IS_KEEP_CONN:
|
||||||
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
case HSE_REQ_ASYNC_READ_CLIENT:
|
||||||
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
case HSE_REQ_GET_IMPERSONATION_TOKEN: /* Added in ISAPI 4.0 */
|
||||||
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
case HSE_REQ_MAP_URL_TO_PATH_EX:
|
||||||
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* TODO: Not quite ready for prime time yet */
|
||||||
|
|
||||||
|
/* Map a URL to a filename */
|
||||||
|
subreq = ap_sub_req_lookup_uri(ap_pstrndup(r->pool, (char *)lpvBuffer,
|
||||||
|
*lpdwSize), r);
|
||||||
|
|
||||||
|
GetFullPathName(subreq->filename, *lpdwSize - 1, (char *)lpvBuffer, NULL);
|
||||||
|
|
||||||
|
/* IIS puts a trailing slash on directories, Apache doesn't */
|
||||||
|
|
||||||
|
if (subreq->finfo.filetype == APR_DIR) {
|
||||||
|
int l = strlen((char *)lpvBuffer);
|
||||||
|
|
||||||
|
((char *)lpvBuffer)[l] = '\\';
|
||||||
|
((char *)lpvBuffer)[l + 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
lpdwDataType = (LPDWORD) ap_palloc(r->pool, sizeof(HSE_URL_MAPEX_INFO));
|
||||||
|
strncpy(((LPHSE_URL_MAPEX_INFO)lpdwDataType)->lpszPath,
|
||||||
|
(char *) lpvBuffer, MAX_PATH);
|
||||||
|
((LPHSE_URL_MAPEX_INFO)lpdwDataType)->dwFlags = 0;
|
||||||
|
/* is a combination of:
|
||||||
|
* HSE_URL_FLAGS_READ Allow for read.
|
||||||
|
* HSE_URL_FLAGS_WRITE Allow for write.
|
||||||
|
* HSE_URL_FLAGS_EXECUTE Allow for execute.
|
||||||
|
* HSE_URL_FLAGS_SSL Require SSL.
|
||||||
|
* HSE_URL_FLAGS_DONT_CACHE Don't cache (virtual root only).
|
||||||
|
* HSE_URL_FLAGS_NEGO_CERT Allow client SSL certifications.
|
||||||
|
* HSE_URL_FLAGS_REQUIRE_CERT Require client SSL certifications.
|
||||||
|
* HSE_URL_FLAGS_MAP_CERT Map SSL certification to a Windows account.
|
||||||
|
* HSE_URL_FLAGS_SSL128 Requires a 128-bit SSL.
|
||||||
|
* HSE_URL_FLAGS_SCRIPT Allows for script execution.
|
||||||
|
*/
|
||||||
|
/* (LPHSE_URL_MAPEX_INFO)lpdwDataType)->cchMatchingPath
|
||||||
|
* (LPHSE_URL_MAPEX_INFO)lpdwDataType)->cchMatchingURL
|
||||||
|
*/
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
case HSE_REQ_ABORTIVE_CLOSE:
|
||||||
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
case HSE_REQ_GET_CERT_INFO_EX: /* Added in ISAPI 4.0 */
|
||||||
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
case HSE_REQ_SEND_RESPONSE_HEADER_EX: /* Added in ISAPI 4.0 */
|
||||||
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* TODO: Not quite ready for prime time */
|
||||||
|
|
||||||
|
if (((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszStatus
|
||||||
|
&& ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchStatus) {
|
||||||
|
r->status_line = ap_pstrndup(r->pool,
|
||||||
|
((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszStatus,
|
||||||
|
((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchStatus);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
r->status_line = ap_pstrdup(r->pool, "200 OK");
|
||||||
|
}
|
||||||
|
sscanf(r->status_line, "%d", &r->status);
|
||||||
|
cid->ecb->dwHttpStatusCode = r->status;
|
||||||
|
|
||||||
|
if (((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszHeader
|
||||||
|
&& ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchHeader)
|
||||||
|
{
|
||||||
|
/* Make a copy - don't disturb the original */
|
||||||
|
data = ap_pstrndup(r->pool,
|
||||||
|
((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszHeader,
|
||||||
|
((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchHeader);
|
||||||
|
|
||||||
|
/* Parse them out, or die trying */
|
||||||
|
data = ComposeHeaders(r, data);
|
||||||
|
if (!data)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
data = "\0";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->fKeepConn;
|
||||||
|
*
|
||||||
|
* Now how are we about to start listening to an ISAPI's
|
||||||
|
* idea of keeping or closing a connection? Seriously :)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* All the headers should be set now */
|
||||||
|
ap_send_http_header(r);
|
||||||
|
|
||||||
|
/* Any data left should now be sent directly */
|
||||||
|
if (*data)
|
||||||
|
ap_rputs(data, r);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
|
case HSE_REQ_CLOSE_CONNECTION: /* Added after ISAPI 4.0 */
|
||||||
/* Need to implement all applicable for ISAPI 5.0 support */
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
case HSE_REQ_ABORTIVE_CLOSE:
|
return FALSE;
|
||||||
case HSE_REQ_ASYNC_READ_CLIENT:
|
|
||||||
case HSE_REQ_CLOSE_CONNECTION:
|
|
||||||
case HSE_REQ_GET_CERT_INFO_EX:
|
|
||||||
case HSE_REQ_GET_IMPERSONATION_TOKEN:
|
|
||||||
case HSE_REQ_GET_SSPI_INFO:
|
|
||||||
case HSE_REQ_IS_KEEP_CONN:
|
|
||||||
case HSE_REQ_MAP_URL_TO_PATH_EX:
|
|
||||||
case HSE_REQ_REFRESH_ISAPI_ACL:
|
|
||||||
|
|
||||||
|
case HSE_REQ_IS_CONNECTED: /* Added after ISAPI 4.0 */
|
||||||
|
/* Returns True if client is connected c.f. Q188346*/
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
/* case HSE_REQ_EXTENSION_TRIGGER:
|
||||||
|
* Added after ISAPI 4.0?
|
||||||
|
* Undocumented - from the Microsoft Jan '00 Platform SDK
|
||||||
|
*/
|
||||||
default:
|
default:
|
||||||
|
/* TODO: log unrecognized ServerSupportCommand for debugging
|
||||||
|
*/
|
||||||
SetLastError(ERROR_INVALID_PARAMETER);
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
@@ -100,14 +100,6 @@
|
|||||||
|
|
||||||
module isapi_module;
|
module isapi_module;
|
||||||
|
|
||||||
/* Our "Connection ID" structure */
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
LPEXTENSION_CONTROL_BLOCK ecb;
|
|
||||||
request_rec *r;
|
|
||||||
int status;
|
|
||||||
} isapi_cid;
|
|
||||||
|
|
||||||
/* Declare the ISAPI functions */
|
/* Declare the ISAPI functions */
|
||||||
|
|
||||||
BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
|
BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
|
||||||
@@ -122,113 +114,172 @@ BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
|
|||||||
/*
|
/*
|
||||||
The optimiser blows it totally here. What happens is that autos are addressed relative to the
|
The optimiser blows it totally here. What happens is that autos are addressed relative to the
|
||||||
stack pointer, which, of course, moves around. The optimiser seems to lose track of it somewhere
|
stack pointer, which, of course, moves around. The optimiser seems to lose track of it somewhere
|
||||||
between setting isapi_entry and calling through it. We work around the problem by forcing it to
|
between setting HttpExtensionProc's address and calling through it. We work around the problem by
|
||||||
use frame pointers.
|
forcing it to use frame pointers.
|
||||||
|
|
||||||
|
The revisions below may eliminate this artifact.
|
||||||
*/
|
*/
|
||||||
#pragma optimize("y",off)
|
#pragma optimize("y",off)
|
||||||
|
|
||||||
int isapi_handler (request_rec *r)
|
/* Our loaded isapi module description structure */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
HINSTANCE handle;
|
||||||
|
HSE_VERSION_INFO *pVer;
|
||||||
|
PFN_GETEXTENSIONVERSION GetExtensionVersion;
|
||||||
|
PFN_HTTPEXTENSIONPROC HttpExtensionProc;
|
||||||
|
PFN_TERMINATEEXTENSION TerminateExtension;
|
||||||
|
int refcount;
|
||||||
|
DWORD timeout;
|
||||||
|
BOOL fakeasync;
|
||||||
|
DWORD reportversion;
|
||||||
|
} isapi_loaded;
|
||||||
|
|
||||||
|
/* Our "Connection ID" structure */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
LPEXTENSION_CONTROL_BLOCK ecb;
|
||||||
|
isapi_loaded *isa;
|
||||||
|
request_rec *r;
|
||||||
|
PFN_HSE_IO_COMPLETION completion;
|
||||||
|
PVOID completion_arg;
|
||||||
|
HANDLE complete;
|
||||||
|
ap_status_t retval;
|
||||||
|
} isapi_cid;
|
||||||
|
|
||||||
|
ap_status_t isapi_handler (request_rec *r)
|
||||||
{
|
{
|
||||||
ap_status_t rv;
|
|
||||||
|
|
||||||
LPEXTENSION_CONTROL_BLOCK ecb =
|
|
||||||
ap_pcalloc(r->pool, sizeof(struct _EXTENSION_CONTROL_BLOCK));
|
|
||||||
HSE_VERSION_INFO *pVer = ap_pcalloc(r->pool, sizeof(HSE_VERSION_INFO));
|
|
||||||
|
|
||||||
HINSTANCE isapi_handle;
|
|
||||||
BOOL (*isapi_version)(HSE_VERSION_INFO *); /* entry point 1 */
|
|
||||||
DWORD (*isapi_entry)(LPEXTENSION_CONTROL_BLOCK); /* entry point 2 */
|
|
||||||
BOOL (*isapi_term)(DWORD); /* optional entry point 3 */
|
|
||||||
|
|
||||||
isapi_cid *cid = ap_pcalloc(r->pool, sizeof(isapi_cid));
|
|
||||||
ap_table_t *e = r->subprocess_env;
|
ap_table_t *e = r->subprocess_env;
|
||||||
int retval;
|
isapi_loaded *isa;
|
||||||
|
isapi_cid *cid;
|
||||||
/* Use similar restrictions as CGIs */
|
|
||||||
|
|
||||||
|
/* Use similar restrictions as CGIs
|
||||||
|
*
|
||||||
|
* 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;
|
return HTTP_FORBIDDEN;
|
||||||
|
|
||||||
if (r->finfo.protection == 0)
|
if (r->finfo.protection == 0)
|
||||||
return HTTP_NOT_FOUND;
|
return HTTP_NOT_FOUND;
|
||||||
|
|
||||||
if (r->finfo.filetype == APR_DIR)
|
if (r->finfo.filetype == APR_DIR)
|
||||||
return HTTP_FORBIDDEN;
|
return HTTP_FORBIDDEN;
|
||||||
|
|
||||||
/* Load the module */
|
/* Load the module
|
||||||
|
*
|
||||||
|
* TODO: Critical section
|
||||||
|
*
|
||||||
|
* Warning: cid should not be allocated from pool if we
|
||||||
|
* cache the isapi process in-memory.
|
||||||
|
*
|
||||||
|
* This code could use cacheing... everything that follows
|
||||||
|
* should only be performed on the first isapi dll invocation,
|
||||||
|
* not with every HttpExtensionProc()
|
||||||
|
*/
|
||||||
|
isa = ap_pcalloc(r->pool, sizeof(isapi_module));
|
||||||
|
isa->pVer = ap_pcalloc(r->pool, sizeof(HSE_VERSION_INFO));
|
||||||
|
isa->refcount = 0;
|
||||||
|
|
||||||
if (!(isapi_handle = LoadLibraryEx(r->filename, NULL,
|
/* TODO: These may need to become overrideable, so that we
|
||||||
LOAD_WITH_ALTERED_SEARCH_PATH))) {
|
* assure a given isapi can be fooled into behaving well.
|
||||||
rv = GetLastError();
|
*/
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_ALERT, rv, r,
|
isa->timeout = INFINITE; /* microsecs */
|
||||||
"Could not load DLL: %s", r->filename);
|
isa->fakeasync = TRUE;
|
||||||
return HTTP_INTERNAL_SERVER_ERROR;
|
isa->reportversion = MAKELONG(0, 5); /* Revision 5.0 */
|
||||||
|
|
||||||
|
if (!(isa->handle = LoadLibraryEx(r->filename, NULL,
|
||||||
|
LOAD_WITH_ALTERED_SEARCH_PATH))) {
|
||||||
|
ap_log_rerror(APLOG_MARK, APLOG_ALERT, GetLastError(), r,
|
||||||
|
"ISAPI %s failed to load", r->filename);
|
||||||
|
return HTTP_INTERNAL_SERVER_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(isapi_version =
|
if (!(isa->GetExtensionVersion =
|
||||||
(void *)(GetProcAddress(isapi_handle, "GetExtensionVersion")))) {
|
(void *)(GetProcAddress(isa->handle, "GetExtensionVersion")))) {
|
||||||
rv = GetLastError();
|
ap_log_rerror(APLOG_MARK, APLOG_ALERT, GetLastError(), r,
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_ALERT, rv, r,
|
"ISAPI %s is missing GetExtensionVersion()",
|
||||||
"Could not load DLL %s symbol GetExtensionVersion()",
|
|
||||||
r->filename);
|
r->filename);
|
||||||
FreeLibrary(isapi_handle);
|
FreeLibrary(isa->handle);
|
||||||
return HTTP_INTERNAL_SERVER_ERROR;
|
return HTTP_INTERNAL_SERVER_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(isapi_entry =
|
if (!(isa->HttpExtensionProc =
|
||||||
(void *)(GetProcAddress(isapi_handle, "HttpExtensionProc")))) {
|
(void *)(GetProcAddress(isa->handle, "HttpExtensionProc")))) {
|
||||||
rv = GetLastError();
|
ap_log_rerror(APLOG_MARK, APLOG_ALERT, GetLastError(), r,
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_ALERT, rv, r,
|
"ISAPI %s is missing HttpExtensionProc()",
|
||||||
"Could not load DLL %s symbol HttpExtensionProc()",
|
|
||||||
r->filename);
|
r->filename);
|
||||||
FreeLibrary(isapi_handle);
|
FreeLibrary(isa->handle);
|
||||||
return HTTP_INTERNAL_SERVER_ERROR;
|
return HTTP_INTERNAL_SERVER_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TerminateExtension() is an optional interface */
|
/* TerminateExtension() is an optional interface */
|
||||||
|
|
||||||
isapi_term = (void *)(GetProcAddress(isapi_handle, "TerminateExtension"));
|
isa->TerminateExtension = (void *)(GetProcAddress(isa->handle, "TerminateExtension"));
|
||||||
|
|
||||||
/* Run GetExtensionVersion() */
|
/* Run GetExtensionVersion() */
|
||||||
|
|
||||||
if (!(*isapi_version)(pVer)) {
|
if (!(*isa->GetExtensionVersion)(isa->pVer)) {
|
||||||
/* ### euh... we're passing the wrong type of error code here */
|
/* ### euh... we're passing the wrong type of error code here */
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_ALERT, HTTP_INTERNAL_SERVER_ERROR, r,
|
ap_log_rerror(APLOG_MARK, APLOG_ALERT, HTTP_INTERNAL_SERVER_ERROR, r,
|
||||||
"ISAPI %s GetExtensionVersion() call failed", r->filename);
|
"ISAPI %s call GetExtensionVersion() failed",
|
||||||
FreeLibrary(isapi_handle);
|
r->filename);
|
||||||
return HTTP_INTERNAL_SERVER_ERROR;
|
FreeLibrary(isa->handle);
|
||||||
|
return HTTP_INTERNAL_SERVER_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Load of this module completed, this is the point at which *isa
|
||||||
|
* could be cached for later invocation.
|
||||||
|
*
|
||||||
|
* on to invoking this request...
|
||||||
|
*/
|
||||||
|
|
||||||
/* Set up variables */
|
/* Set up variables */
|
||||||
ap_add_common_vars(r);
|
ap_add_common_vars(r);
|
||||||
ap_add_cgi_vars(r);
|
ap_add_cgi_vars(r);
|
||||||
|
|
||||||
/* Set up connection ID */
|
/* Set up connection structure and ecb */
|
||||||
ecb->ConnID = (HCONN)cid;
|
cid = ap_pcalloc(r->pool, sizeof(isapi_cid));
|
||||||
cid->ecb = ecb;
|
cid->ecb = ap_pcalloc(r->pool, sizeof(struct _EXTENSION_CONTROL_BLOCK));
|
||||||
|
cid->ecb->ConnID = (HCONN)cid;
|
||||||
|
/* TODO: Critical section */
|
||||||
|
++isa->refcount;
|
||||||
|
cid->isa = isa;
|
||||||
cid->r = r;
|
cid->r = r;
|
||||||
cid->status = 0;
|
cid->r->status = 0;
|
||||||
|
cid->complete = NULL;
|
||||||
|
cid->completion = NULL;
|
||||||
|
cid->retval = APR_SUCCESS;
|
||||||
|
|
||||||
ecb->cbSize = sizeof(struct _EXTENSION_CONTROL_BLOCK);
|
cid->ecb->cbSize = sizeof(EXTENSION_CONTROL_BLOCK);
|
||||||
ecb->dwVersion = MAKELONG(0, 2);
|
cid->ecb->dwVersion = isa->reportversion;
|
||||||
ecb->dwHttpStatusCode = 0;
|
cid->ecb->dwHttpStatusCode = 0;
|
||||||
strcpy(ecb->lpszLogData, "");
|
strcpy(cid->ecb->lpszLogData, "");
|
||||||
// TODO: is a copy needed here?
|
// TODO: are copies really needed here?
|
||||||
ecb->lpszMethod = (char*) r->method;
|
cid->ecb->lpszMethod = ap_pstrdup(r->pool, (char*) r->method);
|
||||||
// TODO: is a copy needed here?
|
cid->ecb->lpszQueryString = ap_pstrdup(r->pool,
|
||||||
ecb->lpszQueryString = (char*) ap_table_get(e, "QUERY_STRING");
|
(char*) ap_table_get(e, "QUERY_STRING"));
|
||||||
// TODO: is a copy needed here?
|
cid->ecb->lpszPathInfo = ap_pstrdup(r->pool,
|
||||||
ecb->lpszPathInfo = (char*) ap_table_get(e, "PATH_INFO");
|
(char*) ap_table_get(e, "PATH_INFO"));
|
||||||
// TODO: is a copy needed here?
|
cid->ecb->lpszPathTranslated = ap_pstrdup(r->pool,
|
||||||
ecb->lpszPathTranslated = (char*) ap_table_get(e, "PATH_TRANSLATED");
|
(char*) ap_table_get(e, "PATH_TRANSLATED"));
|
||||||
// TODO: is a copy needed here?
|
cid->ecb->lpszContentType = ap_pstrdup(r->pool,
|
||||||
ecb->lpszContentType = (char*) ap_table_get(e, "CONTENT_TYPE");
|
(char*) ap_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 */
|
/* Set up client input */
|
||||||
if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) {
|
cid->retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR);
|
||||||
if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
|
if (cid->retval) {
|
||||||
FreeLibrary(isapi_handle);
|
if (isa->TerminateExtension) {
|
||||||
return retval;
|
(*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD);
|
||||||
|
}
|
||||||
|
FreeLibrary(isa->handle);
|
||||||
|
return cid->retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ap_should_client_block(r)) {
|
if (ap_should_client_block(r)) {
|
||||||
@@ -249,16 +300,18 @@ int isapi_handler (request_rec *r)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
if (to_read > 49152) {
|
if (to_read > 49152) {
|
||||||
if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
|
if (isa->TerminateExtension)
|
||||||
FreeLibrary(isapi_handle);
|
(*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD);
|
||||||
|
FreeLibrary(isa->handle);
|
||||||
return HTTP_REQUEST_ENTITY_TOO_LARGE;
|
return HTTP_REQUEST_ENTITY_TOO_LARGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
ecb->lpbData = ap_pcalloc(r->pool, 1 + to_read);
|
cid->ecb->lpbData = ap_pcalloc(r->pool, 1 + to_read);
|
||||||
|
|
||||||
if ((read = ap_get_client_block(r, ecb->lpbData, to_read)) < 0) {
|
if ((read = ap_get_client_block(r, cid->ecb->lpbData, to_read)) < 0) {
|
||||||
if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
|
if (isa->TerminateExtension)
|
||||||
FreeLibrary(isapi_handle);
|
(*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD);
|
||||||
|
FreeLibrary(isa->handle);
|
||||||
return HTTP_INTERNAL_SERVER_ERROR;
|
return HTTP_INTERNAL_SERVER_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,77 +320,96 @@ int isapi_handler (request_rec *r)
|
|||||||
* cbAvailable matches cbTotalBytes, we'll up the latter
|
* cbAvailable matches cbTotalBytes, we'll up the latter
|
||||||
* and equalize them.
|
* and equalize them.
|
||||||
*/
|
*/
|
||||||
ecb->cbAvailable = ecb->cbTotalBytes = read + 1;
|
cid->ecb->cbAvailable = cid->ecb->cbTotalBytes = read + 1;
|
||||||
ecb->lpbData[read] = '\0';
|
cid->ecb->lpbData[read] = '\0';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ecb->cbTotalBytes = 0;
|
cid->ecb->cbTotalBytes = 0;
|
||||||
ecb->cbAvailable = 0;
|
cid->ecb->cbAvailable = 0;
|
||||||
ecb->lpbData = NULL;
|
cid->ecb->lpbData = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set up the callbacks */
|
/* All right... try and run the sucker */
|
||||||
|
cid->retval = (*isa->HttpExtensionProc)(cid->ecb);
|
||||||
ecb->GetServerVariable = &GetServerVariable;
|
|
||||||
ecb->WriteClient = &WriteClient;
|
|
||||||
ecb->ReadClient = &ReadClient;
|
|
||||||
ecb->ServerSupportFunction = &ServerSupportFunction;
|
|
||||||
|
|
||||||
/* All right... try and load the sucker */
|
|
||||||
retval = (*isapi_entry)(ecb);
|
|
||||||
|
|
||||||
/* Set the status (for logging) */
|
/* Set the status (for logging) */
|
||||||
if (ecb->dwHttpStatusCode)
|
if (cid->ecb->dwHttpStatusCode) {
|
||||||
r->status = ecb->dwHttpStatusCode;
|
cid->r->status = cid->ecb->dwHttpStatusCode;
|
||||||
|
}
|
||||||
|
|
||||||
/* Check for a log message - and log it */
|
/* Check for a log message - and log it */
|
||||||
if (ecb->lpszLogData && strcmp(ecb->lpszLogData, ""))
|
if (cid->ecb->lpszLogData && *cid->ecb->lpszLogData)
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
|
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
|
||||||
"ISAPI %s: %s", r->filename, ecb->lpszLogData);
|
"ISAPI %s: %s", r->filename, cid->ecb->lpszLogData);
|
||||||
|
|
||||||
/* All done with the DLL... get rid of it */
|
switch(cid->retval) {
|
||||||
if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
|
|
||||||
FreeLibrary(isapi_handle);
|
|
||||||
|
|
||||||
switch(retval) {
|
|
||||||
case HSE_STATUS_SUCCESS:
|
case HSE_STATUS_SUCCESS:
|
||||||
/* TODO: If content length was missing or incorrect, and the response
|
|
||||||
* was not chunked, we need to close the connection here.
|
|
||||||
* If the response was chunked, and no closing chunk was sent, we aught
|
|
||||||
* to transmit one here
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* fall through... */
|
|
||||||
case HSE_STATUS_SUCCESS_AND_KEEP_CONN:
|
case HSE_STATUS_SUCCESS_AND_KEEP_CONN:
|
||||||
/* Ignore the keepalive stuff; Apache handles it just fine without
|
/* Ignore the keepalive stuff; Apache handles it just fine without
|
||||||
* the ISA's "advice".
|
* 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;
|
||||||
if (cid->status) /* We have a special status to return */
|
|
||||||
return cid->status;
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
|
|
||||||
case HSE_STATUS_PENDING:
|
case HSE_STATUS_PENDING:
|
||||||
/* We don't support this, but we need to... we should simply create a
|
/* emulating async behavior...
|
||||||
* wait event and die on timeout or resume with the callback to our
|
*
|
||||||
* ServerSupportFunction with HSE_REQ_DONE_WITH_SESSION to emulate
|
* Create a cid->completed event and wait on it for some timeout
|
||||||
* async behavior.
|
* so that the app thinks is it running async.
|
||||||
|
*
|
||||||
|
* All async ServerSupportFunction calls will be handled through
|
||||||
|
* the registered IO_COMPLETION hook.
|
||||||
*/
|
*/
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_WARNING, APR_ENOTIMPL, r,
|
|
||||||
"ISAPI asynchronous I/O not supported: %s", r->filename);
|
if (!isa->fakeasync) {
|
||||||
|
ap_log_rerror(APLOG_MARK, APLOG_WARNING, APR_ENOTIMPL, r,
|
||||||
|
"ISAPI %s asynch I/O request refused",
|
||||||
|
r->filename);
|
||||||
|
cid->retval = APR_ENOTIMPL;
|
||||||
|
}
|
||||||
|
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 it's death? For now leave timeout = -1
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case HSE_STATUS_ERROR:
|
case HSE_STATUS_ERROR:
|
||||||
/* end response if we have yet to do so.
|
/* end response if we have yet to do so.
|
||||||
*/
|
*/
|
||||||
return HTTP_INTERNAL_SERVER_ERROR;
|
cid->retval = HTTP_INTERNAL_SERVER_ERROR;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
/* TODO: log unrecognized retval for debugging
|
/* TODO: log unrecognized retval for debugging
|
||||||
*/
|
*/
|
||||||
return HTTP_INTERNAL_SERVER_ERROR;
|
cid->retval = HTTP_INTERNAL_SERVER_ERROR;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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->TerminateExtension) {
|
||||||
|
(*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD);
|
||||||
|
}
|
||||||
|
FreeLibrary(isa->handle);
|
||||||
|
/* TODO: Crit section */
|
||||||
|
cid->isa = NULL;
|
||||||
|
--isa->refcount;
|
||||||
|
isa->handle = NULL;
|
||||||
|
|
||||||
|
return cid->retval;
|
||||||
}
|
}
|
||||||
#pragma optimize("",on)
|
#pragma optimize("",on)
|
||||||
|
|
||||||
@@ -353,33 +425,33 @@ BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
if (!strcasecmp(lpszVariableName, "UNMAPPED_REMOTE_USER")) {
|
if (!strcasecmp(lpszVariableName, "UNMAPPED_REMOTE_USER")) {
|
||||||
/* We don't support NT users, so this is always the same as
|
/* We don't support NT users, so this is always the same as
|
||||||
* REMOTE_USER
|
* REMOTE_USER
|
||||||
*/
|
*/
|
||||||
result = ap_table_get(e, "REMOTE_USER");
|
result = ap_table_get(e, "REMOTE_USER");
|
||||||
}
|
}
|
||||||
else if (!strcasecmp(lpszVariableName, "SERVER_PORT_SECURE")) {
|
else if (!strcasecmp(lpszVariableName, "SERVER_PORT_SECURE")) {
|
||||||
/* Apache doesn't support secure requests inherently, so
|
/* Apache doesn't support secure requests inherently, so
|
||||||
* we have no way of knowing. We'll be conservative, and say
|
* we have no way of knowing. We'll be conservative, and say
|
||||||
* all requests are insecure.
|
* all requests are insecure.
|
||||||
*/
|
*/
|
||||||
result = "0";
|
result = "0";
|
||||||
}
|
}
|
||||||
else if (!strcasecmp(lpszVariableName, "URL")) {
|
else if (!strcasecmp(lpszVariableName, "URL")) {
|
||||||
result = r->uri;
|
result = r->uri;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result = ap_table_get(e, lpszVariableName);
|
result = ap_table_get(e, lpszVariableName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
if (strlen(result) > *lpdwSizeofBuffer) {
|
if (strlen(result) > *lpdwSizeofBuffer) {
|
||||||
*lpdwSizeofBuffer = strlen(result);
|
*lpdwSizeofBuffer = strlen(result);
|
||||||
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
strncpy(lpvBuffer, result, *lpdwSizeofBuffer);
|
strncpy(lpvBuffer, result, *lpdwSizeofBuffer);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Didn't find it */
|
/* Didn't find it */
|
||||||
@@ -388,22 +460,22 @@ BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
|
|||||||
}
|
}
|
||||||
|
|
||||||
BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
|
BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
|
||||||
DWORD dwReserved)
|
DWORD dwReserved)
|
||||||
{
|
{
|
||||||
request_rec *r = ((isapi_cid *)ConnID)->r;
|
request_rec *r = ((isapi_cid *)ConnID)->r;
|
||||||
int writ; /* written, actually, but why shouldn't I make up words? */
|
int writ; /* written, actually, but why shouldn't I make up words? */
|
||||||
|
|
||||||
/* We only support synchronous writing */
|
/* We only support synchronous writing */
|
||||||
if (dwReserved && dwReserved != HSE_IO_SYNC) {
|
if (dwReserved && dwReserved != HSE_IO_SYNC) {
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_WARNING, ERROR_INVALID_PARAMETER, r,
|
ap_log_rerror(APLOG_MARK, APLOG_WARNING, ERROR_INVALID_PARAMETER, r,
|
||||||
"ISAPI asynchronous I/O not supported: %s", r->filename);
|
"ISAPI %s asynch write", r->filename);
|
||||||
SetLastError(ERROR_INVALID_PARAMETER);
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((writ = ap_rwrite(Buffer, *lpwdwBytes, r)) == EOF) {
|
if ((writ = ap_rwrite(Buffer, *lpwdwBytes, r)) == EOF) {
|
||||||
SetLastError(WSAEDISCON); /* TODO: Find the right error code */
|
SetLastError(WSAEDISCON); /* TODO: Find the right error code */
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
*lpwdwBytes = writ;
|
*lpwdwBytes = writ;
|
||||||
@@ -418,13 +490,103 @@ BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize)
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char* ComposeHeaders(request_rec *r, char* data)
|
||||||
|
{
|
||||||
|
/* We *should* break before this while loop ends */
|
||||||
|
while (*data)
|
||||||
|
{
|
||||||
|
char *value, *lf = strchr(data, '\n');
|
||||||
|
int p;
|
||||||
|
|
||||||
|
#ifdef RELAX_HEADER_RULE
|
||||||
|
if (lf)
|
||||||
|
*lf = '\0';
|
||||||
|
#else
|
||||||
|
if (!lf) { /* Huh? Invalid data, I think */
|
||||||
|
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
|
||||||
|
"ISAPI %s sent invalid headers", r->filename);
|
||||||
|
SetLastError(TODO_ERROR);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get rid of \n and \r */
|
||||||
|
*lf = '\0';
|
||||||
|
#endif
|
||||||
|
p = strlen(data);
|
||||||
|
if (p > 0 && data[p-1] == '\r') data[p-1] = '\0';
|
||||||
|
|
||||||
|
/* End of headers */
|
||||||
|
if (*data == '\0') {
|
||||||
|
#ifdef RELAX_HEADER_RULE
|
||||||
|
if (lf)
|
||||||
|
#endif
|
||||||
|
data = lf + 1; /* Reset data */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(value = strchr(data, ':'))) {
|
||||||
|
SetLastError(TODO_ERROR);
|
||||||
|
/* ### euh... we're passing the wrong type of error
|
||||||
|
### code here */
|
||||||
|
ap_log_rerror(APLOG_MARK, APLOG_ERR, HTTP_INTERNAL_SERVER_ERROR, r,
|
||||||
|
"ISAPI %s sent invalid headers", r->filename);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*value++ = '\0';
|
||||||
|
while (*value && ap_isspace(*value)) ++value;
|
||||||
|
|
||||||
|
/* Check all the special-case headers. Similar to what
|
||||||
|
* ap_scan_script_header_err() does (see that function for
|
||||||
|
* more detail)
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!strcasecmp(data, "Content-Type"))
|
||||||
|
{
|
||||||
|
/* Nuke trailing whitespace */
|
||||||
|
char *tmp;
|
||||||
|
char *endp = value + strlen(value) - 1;
|
||||||
|
while (endp > value && ap_isspace(*endp))
|
||||||
|
*endp-- = '\0';
|
||||||
|
|
||||||
|
tmp = ap_pstrdup (r->pool, value);
|
||||||
|
ap_str_tolower(tmp);
|
||||||
|
r->content_type = tmp;
|
||||||
|
}
|
||||||
|
else if (!strcasecmp(data, "Content-Length")) {
|
||||||
|
ap_table_set(r->headers_out, data, value);
|
||||||
|
}
|
||||||
|
else if (!strcasecmp(data, "Transfer-Encoding")) {
|
||||||
|
ap_table_set(r->headers_out, data, value);
|
||||||
|
}
|
||||||
|
else if (!strcasecmp(data, "Set-Cookie")) {
|
||||||
|
ap_table_add(r->err_headers_out, data, value);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ap_table_merge(r->err_headers_out, data, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset data */
|
||||||
|
#ifdef RELAX_HEADER_RULE
|
||||||
|
if (!lf) {
|
||||||
|
data += p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
data = lf + 1;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* XXX: There is an O(n^2) attack possible here. */
|
/* XXX: There is an O(n^2) attack possible here. */
|
||||||
BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
|
BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
|
||||||
LPVOID lpvBuffer, LPDWORD lpdwSize,
|
LPVOID lpvBuffer, LPDWORD lpdwSize,
|
||||||
LPDWORD lpdwDataType)
|
LPDWORD lpdwDataType)
|
||||||
{
|
{
|
||||||
isapi_cid *cid = (isapi_cid *)hConn;
|
isapi_cid *cid = (isapi_cid *)hConn;
|
||||||
request_rec *subreq, *r = cid->r;
|
request_rec *r = cid->r;
|
||||||
|
request_rec *subreq;
|
||||||
char *data;
|
char *data;
|
||||||
|
|
||||||
switch (dwHSERequest) {
|
switch (dwHSERequest) {
|
||||||
@@ -433,8 +595,8 @@ BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
|
|||||||
* is done.
|
* is done.
|
||||||
*/
|
*/
|
||||||
ap_table_set (r->headers_out, "Location", lpvBuffer);
|
ap_table_set (r->headers_out, "Location", lpvBuffer);
|
||||||
cid->status = cid->r->status = cid->ecb->dwHttpStatusCode =
|
cid->r->status = cid->ecb->dwHttpStatusCode
|
||||||
HTTP_MOVED_TEMPORARILY;
|
= HTTP_MOVED_TEMPORARILY;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
case HSE_REQ_SEND_URL:
|
case HSE_REQ_SEND_URL:
|
||||||
@@ -456,27 +618,6 @@ BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
|
|||||||
ap_internal_redirect((char *)lpvBuffer, r);
|
ap_internal_redirect((char *)lpvBuffer, r);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
case HSE_REQ_SEND_RESPONSE_HEADER_EX:
|
|
||||||
SetLastError(ERROR_INVALID_PARAMETER);
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
/* Not yet - but here's the idea...
|
|
||||||
if (((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszStatus
|
|
||||||
&& ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchStatus)
|
|
||||||
r->status_line = ap_pstrndup(r->pool,
|
|
||||||
((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszStatus,
|
|
||||||
((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchStatus);
|
|
||||||
else
|
|
||||||
r->status_line = ap_pstrdup(r->pool, "200 OK");
|
|
||||||
sscanf(r->status_line, "%d", &r->status);
|
|
||||||
cid->ecb->dwHttpStatusCode = r->status;
|
|
||||||
|
|
||||||
* plus we need to handle...
|
|
||||||
* ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszHeader; // HTTP header
|
|
||||||
* ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchHeader; // HTTP header len
|
|
||||||
* ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->fKeepConn; // Keep alive? (bool)
|
|
||||||
*/
|
|
||||||
|
|
||||||
case HSE_REQ_SEND_RESPONSE_HEADER:
|
case HSE_REQ_SEND_RESPONSE_HEADER:
|
||||||
r->status_line = lpvBuffer ? lpvBuffer : ap_pstrdup(r->pool, "200 OK");
|
r->status_line = lpvBuffer ? lpvBuffer : ap_pstrdup(r->pool, "200 OK");
|
||||||
sscanf(r->status_line, "%d", &r->status);
|
sscanf(r->status_line, "%d", &r->status);
|
||||||
@@ -493,102 +634,31 @@ BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
|
|||||||
ap_send_http_header(r);
|
ap_send_http_header(r);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make a copy - don't disturb the original */
|
/* Make a copy - don't disturb the original */
|
||||||
data = ap_pstrdup(r->pool, (char *)lpdwDataType);
|
data = ap_pstrdup(r->pool, (char *)lpdwDataType);
|
||||||
|
|
||||||
/* We *should* break before this while loop ends */
|
|
||||||
while (*data) {
|
|
||||||
char *value, *lf = strchr(data, '\n');
|
|
||||||
int p;
|
|
||||||
|
|
||||||
#ifdef RELAX_HEADER_RULE
|
|
||||||
if (lf)
|
|
||||||
*lf = '\0';
|
|
||||||
#else
|
|
||||||
if (!lf) { /* Huh? Invalid data, I think */
|
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
|
|
||||||
"ISA sent invalid headers: %s", r->filename);
|
|
||||||
SetLastError(TODO_ERROR);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get rid of \n and \r */
|
|
||||||
*lf = '\0';
|
|
||||||
#endif
|
|
||||||
p = strlen(data);
|
|
||||||
if (p > 0 && data[p-1] == '\r') data[p-1] = '\0';
|
|
||||||
|
|
||||||
/* End of headers */
|
|
||||||
if (*data == '\0') {
|
|
||||||
#ifdef RELAX_HEADER_RULE
|
|
||||||
if (lf)
|
|
||||||
#endif
|
|
||||||
data = lf + 1; /* Reset data */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(value = strchr(data, ':'))) {
|
|
||||||
SetLastError(TODO_ERROR);
|
|
||||||
/* ### euh... we're passing the wrong type of error
|
|
||||||
### code here */
|
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_ERR,
|
|
||||||
HTTP_INTERNAL_SERVER_ERROR, r,
|
|
||||||
"ISA sent invalid headers", r->filename);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
*value++ = '\0';
|
|
||||||
while (*value && ap_isspace(*value)) ++value;
|
|
||||||
|
|
||||||
/* Check all the special-case headers. Similar to what
|
|
||||||
* ap_scan_script_header_err() does (see that function for
|
|
||||||
* more detail)
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (!strcasecmp(data, "Content-Type")) {
|
|
||||||
char *tmp;
|
|
||||||
/* Nuke trailing whitespace */
|
|
||||||
|
|
||||||
char *endp = value + strlen(value) - 1;
|
/* Parse them out, or die trying */
|
||||||
while (endp > value && ap_isspace(*endp)) *endp-- = '\0';
|
data = ComposeHeaders(r, data);
|
||||||
|
if (!data)
|
||||||
tmp = ap_pstrdup (r->pool, value);
|
return FALSE;
|
||||||
ap_str_tolower(tmp);
|
|
||||||
r->content_type = tmp;
|
|
||||||
}
|
|
||||||
else if (!strcasecmp(data, "Content-Length")) {
|
|
||||||
ap_table_set(r->headers_out, data, value);
|
|
||||||
}
|
|
||||||
else if (!strcasecmp(data, "Transfer-Encoding")) {
|
|
||||||
ap_table_set(r->headers_out, data, value);
|
|
||||||
}
|
|
||||||
else if (!strcasecmp(data, "Set-Cookie")) {
|
|
||||||
ap_table_add(r->err_headers_out, data, value);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ap_table_merge(r->err_headers_out, data, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reset data */
|
|
||||||
#ifdef RELAX_HEADER_RULE
|
|
||||||
if (!lf) {
|
|
||||||
data += p;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
data = lf + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* All the headers should be set now */
|
/* All the headers should be set now */
|
||||||
|
|
||||||
ap_send_http_header(r);
|
ap_send_http_header(r);
|
||||||
|
|
||||||
/* Any data left should now be sent directly */
|
/* Any data left should now be sent directly */
|
||||||
ap_rputs(data, r);
|
if (*data)
|
||||||
|
ap_rputs(data, r);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
|
case HSE_REQ_DONE_WITH_SESSION:
|
||||||
|
/* Signal to resume the thread completing this request
|
||||||
|
*/
|
||||||
|
if (cid->complete)
|
||||||
|
SetEvent(cid->complete);
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
case HSE_REQ_MAP_URL_TO_PATH:
|
case HSE_REQ_MAP_URL_TO_PATH:
|
||||||
/* Map a URL to a filename */
|
/* Map a URL to a filename */
|
||||||
subreq = ap_sub_req_lookup_uri(ap_pstrndup(r->pool, (char *)lpvBuffer,
|
subreq = ap_sub_req_lookup_uri(ap_pstrndup(r->pool, (char *)lpvBuffer,
|
||||||
@@ -607,23 +677,35 @@ BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
|
|||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
case HSE_REQ_DONE_WITH_SESSION:
|
case HSE_REQ_GET_SSPI_INFO:
|
||||||
/* TODO: Signal the main request with the event to complete the session
|
|
||||||
*/
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
/* We don't support all this async I/O, Microsoft-specific stuff */
|
|
||||||
case HSE_REQ_IO_COMPLETION:
|
|
||||||
SetLastError(ERROR_INVALID_PARAMETER);
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
case HSE_APPEND_LOG_PARAMETER:
|
||||||
|
/* Log lpvBuffer, of lpdwSize bytes, in the URI Query (cs-uri-query) field
|
||||||
|
* This code will do for now...
|
||||||
|
*/
|
||||||
|
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
|
||||||
|
"ISAPI %s: %s", cid->r->filename,
|
||||||
|
(char*) lpvBuffer);
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
case HSE_REQ_IO_COMPLETION:
|
||||||
/* TODO: Emulate a completion port, if we can...
|
/* TODO: Emulate a completion port, if we can...
|
||||||
* Record the callback address and user defined argument...
|
* Record the callback address and user defined argument...
|
||||||
* we will call this after any async request (including transmitfile)
|
* we will call this after any async request (e.g. transmitfile)
|
||||||
* as if the request had been async.
|
* as if the request had completed async execution.
|
||||||
|
* 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)
|
||||||
|
return FALSE;
|
||||||
|
cid->completion = (PFN_HSE_IO_COMPLETION) lpvBuffer;
|
||||||
|
cid->completion_arg = (PVOID) lpdwDataType;
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
case HSE_REQ_TRANSMIT_FILE:
|
case HSE_REQ_TRANSMIT_FILE:
|
||||||
/* Use TransmitFile (in leiu of WriteClient)... nothing wrong with that
|
/* Use TransmitFile... nothing wrong with that :)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* ### euh... we're passing the wrong type of error code here */
|
/* ### euh... we're passing the wrong type of error code here */
|
||||||
@@ -633,23 +715,139 @@ BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
|
|||||||
r->filename);
|
r->filename);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
case HSE_APPEND_LOG_PARAMETER:
|
case HSE_REQ_REFRESH_ISAPI_ACL:
|
||||||
/* Log lpvBuffer, of lpdwSize bytes */
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
case HSE_REQ_IS_KEEP_CONN:
|
||||||
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
case HSE_REQ_ASYNC_READ_CLIENT:
|
||||||
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
case HSE_REQ_GET_IMPERSONATION_TOKEN: /* Added in ISAPI 4.0 */
|
||||||
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
case HSE_REQ_MAP_URL_TO_PATH_EX:
|
||||||
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* TODO: Not quite ready for prime time yet */
|
||||||
|
|
||||||
|
/* Map a URL to a filename */
|
||||||
|
subreq = ap_sub_req_lookup_uri(ap_pstrndup(r->pool, (char *)lpvBuffer,
|
||||||
|
*lpdwSize), r);
|
||||||
|
|
||||||
|
GetFullPathName(subreq->filename, *lpdwSize - 1, (char *)lpvBuffer, NULL);
|
||||||
|
|
||||||
|
/* IIS puts a trailing slash on directories, Apache doesn't */
|
||||||
|
|
||||||
|
if (subreq->finfo.filetype == APR_DIR) {
|
||||||
|
int l = strlen((char *)lpvBuffer);
|
||||||
|
|
||||||
|
((char *)lpvBuffer)[l] = '\\';
|
||||||
|
((char *)lpvBuffer)[l + 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
lpdwDataType = (LPDWORD) ap_palloc(r->pool, sizeof(HSE_URL_MAPEX_INFO));
|
||||||
|
strncpy(((LPHSE_URL_MAPEX_INFO)lpdwDataType)->lpszPath,
|
||||||
|
(char *) lpvBuffer, MAX_PATH);
|
||||||
|
((LPHSE_URL_MAPEX_INFO)lpdwDataType)->dwFlags = 0;
|
||||||
|
/* is a combination of:
|
||||||
|
* HSE_URL_FLAGS_READ Allow for read.
|
||||||
|
* HSE_URL_FLAGS_WRITE Allow for write.
|
||||||
|
* HSE_URL_FLAGS_EXECUTE Allow for execute.
|
||||||
|
* HSE_URL_FLAGS_SSL Require SSL.
|
||||||
|
* HSE_URL_FLAGS_DONT_CACHE Don't cache (virtual root only).
|
||||||
|
* HSE_URL_FLAGS_NEGO_CERT Allow client SSL certifications.
|
||||||
|
* HSE_URL_FLAGS_REQUIRE_CERT Require client SSL certifications.
|
||||||
|
* HSE_URL_FLAGS_MAP_CERT Map SSL certification to a Windows account.
|
||||||
|
* HSE_URL_FLAGS_SSL128 Requires a 128-bit SSL.
|
||||||
|
* HSE_URL_FLAGS_SCRIPT Allows for script execution.
|
||||||
|
*/
|
||||||
|
/* (LPHSE_URL_MAPEX_INFO)lpdwDataType)->cchMatchingPath
|
||||||
|
* (LPHSE_URL_MAPEX_INFO)lpdwDataType)->cchMatchingURL
|
||||||
|
*/
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
case HSE_REQ_ABORTIVE_CLOSE:
|
||||||
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
case HSE_REQ_GET_CERT_INFO_EX: /* Added in ISAPI 4.0 */
|
||||||
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
case HSE_REQ_SEND_RESPONSE_HEADER_EX: /* Added in ISAPI 4.0 */
|
||||||
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* TODO: Not quite ready for prime time */
|
||||||
|
|
||||||
|
if (((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszStatus
|
||||||
|
&& ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchStatus) {
|
||||||
|
r->status_line = ap_pstrndup(r->pool,
|
||||||
|
((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszStatus,
|
||||||
|
((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchStatus);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
r->status_line = ap_pstrdup(r->pool, "200 OK");
|
||||||
|
}
|
||||||
|
sscanf(r->status_line, "%d", &r->status);
|
||||||
|
cid->ecb->dwHttpStatusCode = r->status;
|
||||||
|
|
||||||
|
if (((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszHeader
|
||||||
|
&& ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchHeader)
|
||||||
|
{
|
||||||
|
/* Make a copy - don't disturb the original */
|
||||||
|
data = ap_pstrndup(r->pool,
|
||||||
|
((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->pszHeader,
|
||||||
|
((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->cchHeader);
|
||||||
|
|
||||||
|
/* Parse them out, or die trying */
|
||||||
|
data = ComposeHeaders(r, data);
|
||||||
|
if (!data)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
data = "\0";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ((LPHSE_SEND_HEADER_EX_INFO)lpvBuffer)->fKeepConn;
|
||||||
|
*
|
||||||
|
* Now how are we about to start listening to an ISAPI's
|
||||||
|
* idea of keeping or closing a connection? Seriously :)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* All the headers should be set now */
|
||||||
|
ap_send_http_header(r);
|
||||||
|
|
||||||
|
/* Any data left should now be sent directly */
|
||||||
|
if (*data)
|
||||||
|
ap_rputs(data, r);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
|
case HSE_REQ_CLOSE_CONNECTION: /* Added after ISAPI 4.0 */
|
||||||
/* Need to implement all applicable for ISAPI 5.0 support */
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
case HSE_REQ_ABORTIVE_CLOSE:
|
return FALSE;
|
||||||
case HSE_REQ_ASYNC_READ_CLIENT:
|
|
||||||
case HSE_REQ_CLOSE_CONNECTION:
|
|
||||||
case HSE_REQ_GET_CERT_INFO_EX:
|
|
||||||
case HSE_REQ_GET_IMPERSONATION_TOKEN:
|
|
||||||
case HSE_REQ_GET_SSPI_INFO:
|
|
||||||
case HSE_REQ_IS_KEEP_CONN:
|
|
||||||
case HSE_REQ_MAP_URL_TO_PATH_EX:
|
|
||||||
case HSE_REQ_REFRESH_ISAPI_ACL:
|
|
||||||
|
|
||||||
|
case HSE_REQ_IS_CONNECTED: /* Added after ISAPI 4.0 */
|
||||||
|
/* Returns True if client is connected c.f. Q188346*/
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
/* case HSE_REQ_EXTENSION_TRIGGER:
|
||||||
|
* Added after ISAPI 4.0?
|
||||||
|
* Undocumented - from the Microsoft Jan '00 Platform SDK
|
||||||
|
*/
|
||||||
default:
|
default:
|
||||||
|
/* TODO: log unrecognized ServerSupportCommand for debugging
|
||||||
|
*/
|
||||||
SetLastError(ERROR_INVALID_PARAMETER);
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user