1
/* Licensed to the Apache Software Foundation (ASF) under one or more
2
* contributor license agreements. See the NOTICE file distributed with
3
* this work for additional information regarding copyright ownership.
4
* The ASF licenses this file to You under the Apache License, Version 2.0
5
* (the "License"); you may not use this file except in compliance with
6
* the License. You may obtain a copy of the License at
8
* http://www.apache.org/licenses/LICENSE-2.0
10
* Unless required by applicable law or agreed to in writing, software
11
* distributed under the License is distributed on an "AS IS" BASIS,
12
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
* See the License for the specific language governing permissions and
14
* limitations under the License.
18
* mod_isapi.c - Internet Server Application (ISA) module for Apache
19
* by Alexei Kosut <akosut@apache.org>, significant overhauls and
20
* redesign by William Rowe <wrowe@covalent.net>, and hints from many
21
* other developer/users who have hit on specific flaws.
23
* This module implements the ISAPI Handler architecture, allowing
24
* Apache to load Internet Server Applications (ISAPI extensions),
25
* similar to the support in IIS, Zope, O'Reilly's WebSite and others.
27
* It is a complete implementation of the ISAPI 2.0 specification,
28
* except for "Microsoft extensions" to the API which provide
29
* asynchronous I/O. It is further extended to include additional
30
* "Microsoft extentions" through IIS 5.0, with some deficiencies
31
* where one-to-one mappings don't exist.
33
* Refer to /manual/mod/mod_isapi.html for additional details on
34
* configuration and use, but check this source for specific support
38
#include "ap_config.h"
40
#include "http_config.h"
41
#include "http_core.h"
42
#include "http_protocol.h"
43
#include "http_request.h"
45
#include "util_script.h"
48
#include "apr_strings.h"
49
#include "apr_portable.h"
50
#include "apr_buckets.h"
51
#include "apr_thread_mutex.h"
52
#include "apr_thread_rwlock.h"
54
#include "mod_isapi.h"
56
/* Retry frequency for a failed-to-load isapi .dll */
57
#define ISAPI_RETRY apr_time_from_sec(30)
59
/**********************************************************
61
* ISAPI Module Configuration
63
**********************************************************/
65
module AP_MODULE_DECLARE_DATA isapi_module;
67
#define ISAPI_UNDEF -1
69
/* Our isapi per-dir config structure */
70
typedef struct isapi_dir_conf {
71
int read_ahead_buflen;
78
typedef struct isapi_loaded isapi_loaded;
80
apr_status_t isapi_lookup(apr_pool_t *p, server_rec *s, request_rec *r,
81
const char *fpath, isapi_loaded** isa);
83
static void *create_isapi_dir_config(apr_pool_t *p, char *dummy)
85
isapi_dir_conf *dir = apr_palloc(p, sizeof(isapi_dir_conf));
87
dir->read_ahead_buflen = ISAPI_UNDEF;
88
dir->log_unsupported = ISAPI_UNDEF;
89
dir->log_to_errlog = ISAPI_UNDEF;
90
dir->log_to_query = ISAPI_UNDEF;
91
dir->fake_async = ISAPI_UNDEF;
96
static void *merge_isapi_dir_configs(apr_pool_t *p, void *base_, void *add_)
98
isapi_dir_conf *base = (isapi_dir_conf *) base_;
99
isapi_dir_conf *add = (isapi_dir_conf *) add_;
100
isapi_dir_conf *dir = apr_palloc(p, sizeof(isapi_dir_conf));
102
dir->read_ahead_buflen = (add->read_ahead_buflen == ISAPI_UNDEF)
103
? base->read_ahead_buflen
104
: add->read_ahead_buflen;
105
dir->log_unsupported = (add->log_unsupported == ISAPI_UNDEF)
106
? base->log_unsupported
107
: add->log_unsupported;
108
dir->log_to_errlog = (add->log_to_errlog == ISAPI_UNDEF)
109
? base->log_to_errlog
110
: add->log_to_errlog;
111
dir->log_to_query = (add->log_to_query == ISAPI_UNDEF)
114
dir->fake_async = (add->fake_async == ISAPI_UNDEF)
121
static const char *isapi_cmd_cachefile(cmd_parms *cmd, void *dummy,
122
const char *filename)
129
/* ### Just an observation ... it would be terribly cool to be
130
* able to use this per-dir, relative to the directory block being
131
* defined. The hash result remains global, but shorthand of
132
* <Directory "c:/webapps/isapi">
133
* ISAPICacheFile myapp.dll anotherapp.dll thirdapp.dll
135
* would be very convienent.
137
fspec = ap_server_root_relative(cmd->pool, filename);
139
ap_log_error(APLOG_MARK, APLOG_WARNING, APR_EBADPATH, cmd->server,
140
"ISAPI: invalid module path, skipping %s", filename);
143
if ((rv = apr_stat(&tmp, fspec, APR_FINFO_TYPE,
144
cmd->temp_pool)) != APR_SUCCESS) {
145
ap_log_error(APLOG_MARK, APLOG_WARNING, rv, cmd->server,
146
"ISAPI: unable to stat, skipping %s", fspec);
149
if (tmp.filetype != APR_REG) {
150
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
151
"ISAPI: not a regular file, skipping %s", fspec);
155
/* Load the extention as cached (with null request_rec) */
156
rv = isapi_lookup(cmd->pool, cmd->server, NULL, fspec, &isa);
157
if (rv != APR_SUCCESS) {
158
ap_log_error(APLOG_MARK, APLOG_WARNING, rv, cmd->server,
159
"ISAPI: unable to cache, skipping %s", fspec);
166
static const command_rec isapi_cmds[] = {
167
AP_INIT_TAKE1("ISAPIReadAheadBuffer", ap_set_int_slot,
168
(void *)APR_OFFSETOF(isapi_dir_conf, read_ahead_buflen),
169
OR_FILEINFO, "Maximum client request body to initially pass to the"
170
" ISAPI handler (default: 49152)"),
171
AP_INIT_FLAG("ISAPILogNotSupported", ap_set_flag_slot,
172
(void *)APR_OFFSETOF(isapi_dir_conf, log_unsupported),
173
OR_FILEINFO, "Log requests not supported by the ISAPI server"
174
" on or off (default: off)"),
175
AP_INIT_FLAG("ISAPIAppendLogToErrors", ap_set_flag_slot,
176
(void *)APR_OFFSETOF(isapi_dir_conf, log_to_errlog),
177
OR_FILEINFO, "Send all Append Log requests to the error log"
178
" on or off (default: off)"),
179
AP_INIT_FLAG("ISAPIAppendLogToQuery", ap_set_flag_slot,
180
(void *)APR_OFFSETOF(isapi_dir_conf, log_to_query),
181
OR_FILEINFO, "Append Log requests are concatinated to the query args"
182
" on or off (default: on)"),
183
AP_INIT_FLAG("ISAPIFakeAsync", ap_set_flag_slot,
184
(void *)APR_OFFSETOF(isapi_dir_conf, fake_async),
185
OR_FILEINFO, "Fake Asynchronous support for isapi callbacks"
186
" on or off [Experimental] (default: off)"),
187
AP_INIT_ITERATE("ISAPICacheFile", isapi_cmd_cachefile, NULL,
188
RSRC_CONF, "Cache the specified ISAPI extension in-process"),
192
/**********************************************************
194
* ISAPI Module Cache handling section
196
**********************************************************/
198
/* Our isapi global config values */
199
static struct isapi_global_conf {
201
apr_thread_mutex_t *lock;
205
/* Our loaded isapi module description structure */
206
struct isapi_loaded {
207
const char *filename;
208
apr_thread_rwlock_t *in_progress;
209
apr_status_t last_load_rv;
210
apr_time_t last_load_time;
211
apr_dso_handle_t *handle;
212
HSE_VERSION_INFO *isapi_version;
213
apr_uint32_t report_version;
214
apr_uint32_t timeout;
215
PFN_GETEXTENSIONVERSION GetExtensionVersion;
216
PFN_HTTPEXTENSIONPROC HttpExtensionProc;
217
PFN_TERMINATEEXTENSION TerminateExtension;
220
static apr_status_t isapi_unload(isapi_loaded *isa, int force)
222
/* All done with the DLL... get rid of it...
224
* If optionally cached, and we weren't asked to force the unload,
225
* pass HSE_TERM_ADVISORY_UNLOAD, and if it returns 1, unload,
226
* otherwise, leave it alone (it didn't choose to cooperate.)
231
if (isa->TerminateExtension) {
233
(*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD);
235
else if (!(*isa->TerminateExtension)(HSE_TERM_ADVISORY_UNLOAD)) {
239
apr_dso_unload(isa->handle);
244
static apr_status_t cleanup_isapi(void *isa_)
246
isapi_loaded* isa = (isapi_loaded*) isa_;
248
/* We must force the module to unload, we are about
249
* to lose the isapi structure's allocation entirely.
251
return isapi_unload(isa, 1);
254
static apr_status_t isapi_load(apr_pool_t *p, server_rec *s, isapi_loaded *isa)
258
isa->isapi_version = apr_pcalloc(p, sizeof(HSE_VERSION_INFO));
260
/* TODO: These aught to become overrideable, so that we
261
* assure a given isapi can be fooled into behaving well.
263
* The tricky bit, they aren't really a per-dir sort of
264
* config, they will always be constant across every
265
* reference to the .dll no matter what context (vhost,
266
* location, etc) they apply to.
268
isa->report_version = 0x500; /* Revision 5.0 */
269
isa->timeout = 300 * 1000000; /* microsecs, not used */
271
rv = apr_dso_load(&isa->handle, isa->filename, p);
274
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
275
"ISAPI: failed to load %s", isa->filename);
280
rv = apr_dso_sym((void**)&isa->GetExtensionVersion, isa->handle,
281
"GetExtensionVersion");
284
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
285
"ISAPI: missing GetExtensionVersion() in %s",
287
apr_dso_unload(isa->handle);
292
rv = apr_dso_sym((void**)&isa->HttpExtensionProc, isa->handle,
293
"HttpExtensionProc");
296
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
297
"ISAPI: missing HttpExtensionProc() in %s",
299
apr_dso_unload(isa->handle);
304
/* TerminateExtension() is an optional interface */
305
rv = apr_dso_sym((void**)&isa->TerminateExtension, isa->handle,
306
"TerminateExtension");
309
/* Run GetExtensionVersion() */
310
if (!(isa->GetExtensionVersion)(isa->isapi_version)) {
311
apr_status_t rv = apr_get_os_error();
312
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
313
"ISAPI: failed call to GetExtensionVersion() in %s",
315
apr_dso_unload(isa->handle);
320
apr_pool_cleanup_register(p, isa, cleanup_isapi,
321
apr_pool_cleanup_null);
326
apr_status_t isapi_lookup(apr_pool_t *p, server_rec *s, request_rec *r,
327
const char *fpath, isapi_loaded** isa)
332
if ((rv = apr_thread_mutex_lock(loaded.lock)) != APR_SUCCESS) {
336
*isa = apr_hash_get(loaded.hash, fpath, APR_HASH_KEY_STRING);
340
/* If we find this lock exists, use a set-aside copy of gainlock
341
* to avoid race conditions on NULLing the in_progress variable
342
* when the load has completed. Release the global isapi hash
343
* lock so other requests can proceed, then rdlock for completion
344
* of loading our desired dll or wrlock if we would like to retry
345
* loading the dll (because last_load_rv failed and retry is up.)
347
apr_thread_rwlock_t *gainlock = (*isa)->in_progress;
349
/* gainlock is NULLed after the module loads successfully.
350
* This free-threaded module can be used without any locking.
353
rv = (*isa)->last_load_rv;
354
apr_thread_mutex_unlock(loaded.lock);
359
if ((*isa)->last_load_rv == APR_SUCCESS) {
360
apr_thread_mutex_unlock(loaded.lock);
361
if ((rv = apr_thread_rwlock_rdlock(gainlock))
365
rv = (*isa)->last_load_rv;
366
apr_thread_rwlock_unlock(gainlock);
370
if (apr_time_now() > (*isa)->last_load_time + ISAPI_RETRY) {
372
/* Remember last_load_time before releasing the global
373
* hash lock to avoid colliding with another thread
374
* that hit this exception at the same time as our
375
* retry attempt, since we unlock the global mutex
376
* before attempting a write lock for this module.
378
apr_time_t check_time = (*isa)->last_load_time;
379
apr_thread_mutex_unlock(loaded.lock);
381
if ((rv = apr_thread_rwlock_wrlock(gainlock))
386
/* If last_load_time is unchanged, we still own this
387
* retry, otherwise presume another thread provided
388
* our retry (for good or ill). Relock the global
389
* hash for updating last_load_ vars, so their update
390
* is always atomic to the global lock.
392
if (check_time == (*isa)->last_load_time) {
394
rv = isapi_load(loaded.pool, s, *isa);
396
apr_thread_mutex_lock(loaded.lock);
397
(*isa)->last_load_rv = rv;
398
(*isa)->last_load_time = apr_time_now();
399
apr_thread_mutex_unlock(loaded.lock);
402
rv = (*isa)->last_load_rv;
404
apr_thread_rwlock_unlock(gainlock);
409
/* We haven't hit timeup on retry, let's grab the last_rv
410
* within the hash mutex before unlocking.
412
rv = (*isa)->last_load_rv;
413
apr_thread_mutex_unlock(loaded.lock);
418
/* If the module was not found, it's time to create a hash key entry
419
* before releasing the hash lock to avoid multiple threads from
420
* loading the same module.
422
key = apr_pstrdup(loaded.pool, fpath);
423
*isa = apr_pcalloc(loaded.pool, sizeof(isapi_loaded));
424
(*isa)->filename = key;
426
/* A mutex that exists only long enough to attempt to
427
* load this isapi dll, the release this module to all
428
* other takers that came along during the one-time
429
* load process. Short lifetime for this lock would
430
* be great, however, using r->pool is nasty if those
431
* blocked on the lock haven't all unlocked before we
432
* attempt to destroy. A nastier race condition than
433
* I want to deal with at this moment...
435
apr_thread_rwlock_create(&(*isa)->in_progress, loaded.pool);
436
apr_thread_rwlock_wrlock((*isa)->in_progress);
439
apr_hash_set(loaded.hash, key, APR_HASH_KEY_STRING, *isa);
441
/* Now attempt to load the isapi on our own time,
442
* allow other isapi processing to resume.
444
apr_thread_mutex_unlock(loaded.lock);
446
rv = isapi_load(loaded.pool, s, *isa);
447
(*isa)->last_load_time = apr_time_now();
448
(*isa)->last_load_rv = rv;
450
if (r && (rv == APR_SUCCESS)) {
451
/* Let others who are blocked on this particular
452
* module resume their requests, for better or worse.
454
apr_thread_rwlock_t *unlock = (*isa)->in_progress;
455
(*isa)->in_progress = NULL;
456
apr_thread_rwlock_unlock(unlock);
458
else if (!r && (rv != APR_SUCCESS)) {
459
/* We must leave a rwlock around for requests to retry
460
* loading this dll after timeup... since we were in
461
* the setup code we had avoided creating this lock.
463
apr_thread_rwlock_create(&(*isa)->in_progress, loaded.pool);
466
return (*isa)->last_load_rv;
469
/**********************************************************
471
* ISAPI Module request callbacks section
473
**********************************************************/
475
/* Our "Connection ID" structure */
477
EXTENSION_CONTROL_BLOCK *ecb;
478
isapi_dir_conf dconf;
483
PFN_HSE_IO_COMPLETION completion;
484
void *completion_arg;
485
apr_thread_mutex_t *completed;
488
int APR_THREAD_FUNC GetServerVariable (isapi_cid *cid,
491
apr_uint32_t *buf_size)
493
request_rec *r = cid->r;
495
char *buf_data = (char*)buf_ptr;
498
if (!strcmp(variable_name, "ALL_HTTP"))
500
/* crlf delimited, colon split, comma separated and
501
* null terminated list of HTTP_ vars
503
const apr_array_header_t *arr = apr_table_elts(r->subprocess_env);
504
const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts;
507
for (len = 0, i = 0; i < arr->nelts; i++) {
508
if (!strncmp(elts[i].key, "HTTP_", 5)) {
509
len += strlen(elts[i].key) + strlen(elts[i].val) + 3;
513
if (*buf_size < len + 1) {
515
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INSUFFICIENT_BUFFER));
519
for (i = 0; i < arr->nelts; i++) {
520
if (!strncmp(elts[i].key, "HTTP_", 5)) {
521
strcpy(buf_data, elts[i].key);
522
buf_data += strlen(elts[i].key);
524
strcpy(buf_data, elts[i].val);
525
buf_data += strlen(elts[i].val);
526
*(buf_data++) = '\r';
527
*(buf_data++) = '\n';
531
*(buf_data++) = '\0';
536
if (!strcmp(variable_name, "ALL_RAW"))
538
/* crlf delimited, colon split, comma separated and
539
* null terminated list of the raw request header
541
const apr_array_header_t *arr = apr_table_elts(r->headers_in);
542
const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts;
545
for (len = 0, i = 0; i < arr->nelts; i++) {
546
len += strlen(elts[i].key) + strlen(elts[i].val) + 4;
549
if (*buf_size < len + 1) {
551
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INSUFFICIENT_BUFFER));
555
for (i = 0; i < arr->nelts; i++) {
556
strcpy(buf_data, elts[i].key);
557
buf_data += strlen(elts[i].key);
560
strcpy(buf_data, elts[i].val);
561
buf_data += strlen(elts[i].val);
562
*(buf_data++) = '\r';
563
*(buf_data++) = '\n';
565
*(buf_data++) = '\0';
570
/* Not a special case */
571
result = apr_table_get(r->subprocess_env, variable_name);
574
len = strlen(result);
575
if (*buf_size < len + 1) {
577
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INSUFFICIENT_BUFFER));
580
strcpy(buf_data, result);
586
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_INDEX));
590
int APR_THREAD_FUNC ReadClient(isapi_cid *cid,
592
apr_uint32_t *buf_size)
594
request_rec *r = cid->r;
595
apr_uint32_t read = 0;
598
if (r->remaining < *buf_size) {
599
*buf_size = (apr_size_t)r->remaining;
602
while (read < *buf_size &&
603
((res = ap_get_client_block(r, (char*)buf_data + read,
604
*buf_size - read)) > 0)) {
610
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_READ_FAULT));
615
/* Common code invoked for both HSE_REQ_SEND_RESPONSE_HEADER and
616
* the newer HSE_REQ_SEND_RESPONSE_HEADER_EX ServerSupportFunction(s)
617
* as well as other functions that write responses and presume that
618
* the support functions above are optional.
620
* Other callers trying to split headers and body bytes should pass
621
* head/headlen alone (leaving stat/statlen NULL/0), so that they
622
* get a proper count of bytes consumed. The argument passed to stat
623
* isn't counted as the head bytes are.
625
static apr_ssize_t send_response_header(isapi_cid *cid,
631
int head_present = 1;
636
if (!head || headlen == 0 || !*head) {
641
head_present = 0; /* Don't eat the header */
644
if (!stat || statlen == 0 || !*stat) {
645
if (head && headlen && *head && ((stat = memchr(head, '\r', headlen))
646
|| (stat = memchr(head, '\n', headlen))
647
|| (stat = memchr(head, '\0', headlen))
648
|| (stat = head + headlen))) {
649
statlen = stat - head;
650
if (memchr(head, ':', statlen)) {
651
stat = "Status: 200 OK";
652
statlen = strlen(stat);
655
const char *flip = head;
660
if (*head == '\r' && headlen)
661
++head, --headlen, ++ate;
662
if (*head == '\n' && headlen)
663
++head, --headlen, ++ate;
668
if (stat && (statlen > 0) && *stat) {
670
if (!apr_isdigit(*stat)) {
671
const char *stattok = stat;
672
int toklen = statlen;
673
while (toklen && *stattok && !apr_isspace(*stattok)) {
676
while (toklen && apr_isspace(*stattok)) {
679
/* Now decide if we follow the xxx message
680
* or the http/x.x xxx message format
682
if (toklen && apr_isdigit(*stattok)) {
687
newstat = apr_palloc(cid->r->pool, statlen + 9);
688
strcpy(newstat, "Status: ");
689
apr_cpystrn(newstat + 8, stat, statlen + 1);
694
if (!head || headlen == 0 || !*head) {
700
if (head[headlen - 1] && head[headlen]) {
701
/* Whoops... not NULL terminated */
702
head = apr_pstrndup(cid->r->pool, head, headlen);
706
/* Seems IIS does not enforce the requirement for \r\n termination
707
* on HSE_REQ_SEND_RESPONSE_HEADER, but we won't panic...
708
* ap_scan_script_header_err_strs handles this aspect for us.
710
* Parse them out, or die trying
713
cid->r->status = ap_scan_script_header_err_strs(cid->r, NULL,
714
&termch, &termarg, stat, head, NULL);
715
cid->ecb->dwHttpStatusCode = cid->r->status;
718
cid->r->status = ap_scan_script_header_err_strs(cid->r, NULL,
719
&termch, &termarg, head, NULL);
720
if (cid->ecb->dwHttpStatusCode && cid->r->status == HTTP_OK
721
&& cid->ecb->dwHttpStatusCode != HTTP_OK) {
722
/* We tried every way to Sunday to get the status...
723
* so now we fall back on dwHttpStatusCode if it appears
724
* ap_scan_script_header fell back on the default code.
725
* Any other results set dwHttpStatusCode to the decoded
728
cid->r->status = cid->ecb->dwHttpStatusCode;
729
cid->r->status_line = ap_get_status_line(cid->r->status);
732
cid->ecb->dwHttpStatusCode = cid->r->status;
735
if (cid->r->status == HTTP_INTERNAL_SERVER_ERROR) {
739
/* If only Status was passed, we consumed nothing
744
cid->headers_set = 1;
746
/* If all went well, tell the caller we consumed the headers complete
749
return(ate + headlen);
751
/* Any data left must be sent directly by the caller, all we
752
* give back is the size of the headers we consumed (which only
753
* happens if the parser got to the head arg, which varies based
754
* on whether we passed stat+head to scan, or only head.
756
if (termch && (termarg == (stat ? 1 : 0))
757
&& head_present && head + headlen > termch) {
758
return ate + termch - head;
763
int APR_THREAD_FUNC WriteClient(isapi_cid *cid,
765
apr_uint32_t *size_arg,
768
request_rec *r = cid->r;
769
conn_rec *c = r->connection;
770
apr_uint32_t buf_size = *size_arg;
771
char *buf_data = (char*)buf_ptr;
772
apr_bucket_brigade *bb;
776
if (!cid->headers_set) {
777
/* It appears that the foxisapi module and other clients
778
* presume that WriteClient("headers\n\nbody") will work.
779
* Parse them out, or die trying.
782
ate = send_response_header(cid, NULL, buf_data, 0, buf_size);
784
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
793
bb = apr_brigade_create(r->pool, c->bucket_alloc);
794
b = apr_bucket_transient_create(buf_data, buf_size, c->bucket_alloc);
795
APR_BRIGADE_INSERT_TAIL(bb, b);
796
b = apr_bucket_flush_create(c->bucket_alloc);
797
APR_BRIGADE_INSERT_TAIL(bb, b);
798
rv = ap_pass_brigade(r->output_filters, bb);
799
cid->response_sent = 1;
802
if ((flags & HSE_IO_ASYNC) && cid->completion) {
804
cid->completion(cid->ecb, cid->completion_arg,
805
*size_arg, ERROR_SUCCESS);
808
cid->completion(cid->ecb, cid->completion_arg,
809
*size_arg, ERROR_WRITE_FAULT);
815
int APR_THREAD_FUNC ServerSupportFunction(isapi_cid *cid,
816
apr_uint32_t HSE_code,
818
apr_uint32_t *buf_size,
819
apr_uint32_t *data_type)
821
request_rec *r = cid->r;
822
conn_rec *c = r->connection;
823
char *buf_data = (char*)buf_ptr;
827
case HSE_REQ_SEND_URL_REDIRECT_RESP:
828
/* Set the status to be returned when the HttpExtensionProc()
830
* WARNING: Microsoft now advertises HSE_REQ_SEND_URL_REDIRECT_RESP
831
* and HSE_REQ_SEND_URL as equivalant per the Jan 2000 SDK.
832
* They most definately are not, even in their own samples.
834
apr_table_set (r->headers_out, "Location", buf_data);
835
cid->r->status = cid->ecb->dwHttpStatusCode = HTTP_MOVED_TEMPORARILY;
836
cid->r->status_line = ap_get_status_line(cid->r->status);
837
cid->headers_set = 1;
840
case HSE_REQ_SEND_URL:
841
/* Soak up remaining input */
842
if (r->remaining > 0) {
843
char argsbuffer[HUGE_STRING_LEN];
844
while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN));
847
/* Reset the method to GET */
848
r->method = apr_pstrdup(r->pool, "GET");
849
r->method_number = M_GET;
851
/* Don't let anyone think there's still data */
852
apr_table_unset(r->headers_in, "Content-Length");
854
/* AV fault per PR3598 - redirected path is lost! */
855
buf_data = apr_pstrdup(r->pool, (char*)buf_data);
856
ap_internal_redirect(buf_data, r);
859
case HSE_REQ_SEND_RESPONSE_HEADER:
861
/* Parse them out, or die trying */
862
apr_size_t statlen = 0, headlen = 0;
865
statlen = strlen((char*) buf_data);
867
headlen = strlen((char*) data_type);
868
ate = send_response_header(cid, (char*) buf_data,
872
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
875
else if ((apr_size_t)ate < headlen) {
876
apr_bucket_brigade *bb;
878
bb = apr_brigade_create(cid->r->pool, c->bucket_alloc);
879
b = apr_bucket_transient_create((char*) data_type + ate,
880
headlen - ate, c->bucket_alloc);
881
APR_BRIGADE_INSERT_TAIL(bb, b);
882
b = apr_bucket_flush_create(c->bucket_alloc);
883
APR_BRIGADE_INSERT_TAIL(bb, b);
884
ap_pass_brigade(cid->r->output_filters, bb);
885
cid->response_sent = 1;
890
case HSE_REQ_DONE_WITH_SESSION:
891
/* Signal to resume the thread completing this request,
892
* leave it to the pool cleanup to dispose of our mutex.
894
if (cid->completed) {
895
(void)apr_thread_mutex_unlock(cid->completed);
898
else if (cid->dconf.log_unsupported) {
899
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
900
"ISAPI: ServerSupportFunction "
901
"HSE_REQ_DONE_WITH_SESSION is not supported: %s",
904
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
907
case HSE_REQ_MAP_URL_TO_PATH:
909
/* Map a URL to a filename */
910
char *file = (char *)buf_data;
912
subreq = ap_sub_req_lookup_uri(apr_pstrndup(r->pool, file, *buf_size),
915
len = apr_cpystrn(file, subreq->filename, *buf_size) - file;
918
/* IIS puts a trailing slash on directories, Apache doesn't */
919
if (subreq->finfo.filetype == APR_DIR) {
920
if (len < *buf_size - 1) {
929
case HSE_REQ_GET_SSPI_INFO:
930
if (cid->dconf.log_unsupported)
931
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
932
"ISAPI: ServerSupportFunction HSE_REQ_GET_SSPI_INFO "
933
"is not supported: %s", r->filename);
934
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
937
case HSE_APPEND_LOG_PARAMETER:
938
/* Log buf_data, of buf_size bytes, in the URI Query (cs-uri-query) field
940
apr_table_set(r->notes, "isapi-parameter", (char*) buf_data);
941
if (cid->dconf.log_to_query) {
943
r->args = apr_pstrcat(r->pool, r->args, (char*) buf_data, NULL);
945
r->args = apr_pstrdup(r->pool, (char*) buf_data);
947
if (cid->dconf.log_to_errlog)
948
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
949
"ISAPI: %s: %s", cid->r->filename,
953
case HSE_REQ_IO_COMPLETION:
954
/* Emulates a completion port... Record callback address and
955
* user defined arg, we will call this after any async request
956
* (e.g. transmitfile) as if the request executed async.
957
* Per MS docs... HSE_REQ_IO_COMPLETION replaces any prior call
958
* to HSE_REQ_IO_COMPLETION, and buf_data may be set to NULL.
960
if (cid->dconf.fake_async) {
961
cid->completion = (PFN_HSE_IO_COMPLETION) buf_data;
962
cid->completion_arg = (void *) data_type;
965
if (cid->dconf.log_unsupported)
966
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
967
"ISAPI: ServerSupportFunction HSE_REQ_IO_COMPLETION "
968
"is not supported: %s", r->filename);
969
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
972
case HSE_REQ_TRANSMIT_FILE:
974
/* we do nothing with (tf->dwFlags & HSE_DISCONNECT_AFTER_SEND)
976
HSE_TF_INFO *tf = (HSE_TF_INFO*)buf_data;
977
apr_uint32_t sent = 0;
980
apr_bucket_brigade *bb;
985
if (!cid->dconf.fake_async && (tf->dwFlags & HSE_IO_ASYNC)) {
986
if (cid->dconf.log_unsupported)
987
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
988
"ISAPI: ServerSupportFunction HSE_REQ_TRANSMIT_FILE "
989
"as HSE_IO_ASYNC is not supported: %s", r->filename);
990
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
994
/* Presume the handle was opened with the CORRECT semantics
997
if ((rv = apr_os_file_put(&fd, &tf->hFile,
998
APR_READ | APR_XTHREAD, r->pool))
1002
if (tf->BytesToWrite) {
1003
fsize = tf->BytesToWrite;
1007
if (apr_file_info_get(&fi, APR_FINFO_SIZE, fd) != APR_SUCCESS) {
1008
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
1011
fsize = fi.size - tf->Offset;
1014
/* apr_dupfile_oshandle (&fd, tf->hFile, r->pool); */
1015
bb = apr_brigade_create(r->pool, c->bucket_alloc);
1017
/* According to MS: if calling HSE_REQ_TRANSMIT_FILE with the
1018
* HSE_IO_SEND_HEADERS flag, then you can't otherwise call any
1019
* HSE_SEND_RESPONSE_HEADERS* fn, but if you don't use the flag,
1020
* you must have done so. They document that the pHead headers
1021
* option is valid only for HSE_IO_SEND_HEADERS - we are a bit
1022
* more flexible and assume with the flag, pHead are the
1023
* response headers, and without, pHead simply contains text
1024
* (handled after this case).
1026
if ((tf->dwFlags & HSE_IO_SEND_HEADERS) && tf->pszStatusCode) {
1027
ate = send_response_header(cid, tf->pszStatusCode,
1029
strlen(tf->pszStatusCode),
1032
else if (!cid->headers_set && tf->pHead && tf->HeadLength
1033
&& *(char*)tf->pHead) {
1034
ate = send_response_header(cid, NULL, (char*)tf->pHead,
1038
apr_brigade_destroy(bb);
1039
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
1044
if (tf->pHead && (apr_size_t)ate < tf->HeadLength) {
1045
b = apr_bucket_transient_create((char*)tf->pHead + ate,
1046
tf->HeadLength - ate,
1048
APR_BRIGADE_INSERT_TAIL(bb, b);
1049
sent = tf->HeadLength;
1052
sent += (apr_uint32_t)fsize;
1053
#if APR_HAS_LARGE_FILES
1054
if (r->finfo.size > AP_MAX_SENDFILE) {
1055
/* APR_HAS_LARGE_FILES issue; must split into mutiple buckets,
1056
* no greater than MAX(apr_size_t), and more granular than that
1057
* in case the brigade code/filters attempt to read it directly.
1059
b = apr_bucket_file_create(fd, tf->Offset, AP_MAX_SENDFILE,
1060
r->pool, c->bucket_alloc);
1061
while (fsize > AP_MAX_SENDFILE) {
1063
apr_bucket_copy(b, &bc);
1064
APR_BRIGADE_INSERT_TAIL(bb, bc);
1065
b->start += AP_MAX_SENDFILE;
1066
fsize -= AP_MAX_SENDFILE;
1068
b->length = (apr_size_t)fsize; /* Resize just the last bucket */
1072
b = apr_bucket_file_create(fd, tf->Offset, (apr_size_t)fsize,
1073
r->pool, c->bucket_alloc);
1074
APR_BRIGADE_INSERT_TAIL(bb, b);
1076
if (tf->pTail && tf->TailLength) {
1077
sent += tf->TailLength;
1078
b = apr_bucket_transient_create((char*)tf->pTail,
1079
tf->TailLength, c->bucket_alloc);
1080
APR_BRIGADE_INSERT_TAIL(bb, b);
1083
b = apr_bucket_flush_create(c->bucket_alloc);
1084
APR_BRIGADE_INSERT_TAIL(bb, b);
1085
ap_pass_brigade(r->output_filters, bb);
1086
cid->response_sent = 1;
1088
/* Use tf->pfnHseIO + tf->pContext, or if NULL, then use cid->fnIOComplete
1089
* pass pContect to the HseIO callback.
1091
if (tf->dwFlags & HSE_IO_ASYNC) {
1094
tf->pfnHseIO(cid->ecb, tf->pContext,
1095
ERROR_SUCCESS, sent);
1098
tf->pfnHseIO(cid->ecb, tf->pContext,
1099
ERROR_WRITE_FAULT, sent);
1102
else if (cid->completion) {
1104
cid->completion(cid->ecb, cid->completion_arg,
1105
sent, ERROR_SUCCESS);
1108
cid->completion(cid->ecb, cid->completion_arg,
1109
sent, ERROR_WRITE_FAULT);
1116
case HSE_REQ_REFRESH_ISAPI_ACL:
1117
if (cid->dconf.log_unsupported)
1118
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1119
"ISAPI: ServerSupportFunction "
1120
"HSE_REQ_REFRESH_ISAPI_ACL "
1121
"is not supported: %s", r->filename);
1122
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
1125
case HSE_REQ_IS_KEEP_CONN:
1126
*((int *)buf_data) = (r->connection->keepalive == AP_CONN_KEEPALIVE);
1129
case HSE_REQ_ASYNC_READ_CLIENT:
1131
apr_uint32_t read = 0;
1133
if (!cid->dconf.fake_async) {
1134
if (cid->dconf.log_unsupported)
1135
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1136
"ISAPI: asynchronous I/O not supported: %s",
1138
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
1142
if (r->remaining < *buf_size) {
1143
*buf_size = (apr_size_t)r->remaining;
1146
while (read < *buf_size &&
1147
((res = ap_get_client_block(r, (char*)buf_data + read,
1148
*buf_size - read)) > 0)) {
1152
if ((*data_type & HSE_IO_ASYNC) && cid->completion) {
1154
cid->completion(cid->ecb, cid->completion_arg,
1155
read, ERROR_SUCCESS);
1158
cid->completion(cid->ecb, cid->completion_arg,
1159
read, ERROR_READ_FAULT);
1165
case HSE_REQ_GET_IMPERSONATION_TOKEN: /* Added in ISAPI 4.0 */
1166
if (cid->dconf.log_unsupported)
1167
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1168
"ISAPI: ServerSupportFunction "
1169
"HSE_REQ_GET_IMPERSONATION_TOKEN "
1170
"is not supported: %s", r->filename);
1171
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
1174
case HSE_REQ_MAP_URL_TO_PATH_EX:
1176
/* Map a URL to a filename */
1177
HSE_URL_MAPEX_INFO *info = (HSE_URL_MAPEX_INFO*)data_type;
1178
char* test_uri = apr_pstrndup(r->pool, (char *)buf_data, *buf_size);
1180
subreq = ap_sub_req_lookup_uri(test_uri, r, NULL);
1181
info->cchMatchingURL = strlen(test_uri);
1182
info->cchMatchingPath = apr_cpystrn(info->lpszPath, subreq->filename,
1183
sizeof(info->lpszPath)) - info->lpszPath;
1185
/* Mapping started with assuming both strings matched.
1186
* Now roll on the path_info as a mismatch and handle
1187
* terminating slashes for directory matches.
1189
if (subreq->path_info && *subreq->path_info) {
1190
apr_cpystrn(info->lpszPath + info->cchMatchingPath,
1192
sizeof(info->lpszPath) - info->cchMatchingPath);
1193
info->cchMatchingURL -= strlen(subreq->path_info);
1194
if (subreq->finfo.filetype == APR_DIR
1195
&& info->cchMatchingPath < sizeof(info->lpszPath) - 1) {
1196
/* roll forward over path_info's first slash */
1197
++info->cchMatchingPath;
1198
++info->cchMatchingURL;
1201
else if (subreq->finfo.filetype == APR_DIR
1202
&& info->cchMatchingPath < sizeof(info->lpszPath) - 1) {
1203
/* Add a trailing slash for directory */
1204
info->lpszPath[info->cchMatchingPath++] = '/';
1205
info->lpszPath[info->cchMatchingPath] = '\0';
1208
/* If the matched isn't a file, roll match back to the prior slash */
1209
if (subreq->finfo.filetype == APR_NOFILE) {
1210
while (info->cchMatchingPath && info->cchMatchingURL) {
1211
if (info->lpszPath[info->cchMatchingPath - 1] == '/')
1213
--info->cchMatchingPath;
1214
--info->cchMatchingURL;
1218
/* Paths returned with back slashes */
1219
for (test_uri = info->lpszPath; *test_uri; ++test_uri)
1220
if (*test_uri == '/')
1223
/* is a combination of:
1224
* HSE_URL_FLAGS_READ 0x001 Allow read
1225
* HSE_URL_FLAGS_WRITE 0x002 Allow write
1226
* HSE_URL_FLAGS_EXECUTE 0x004 Allow execute
1227
* HSE_URL_FLAGS_SSL 0x008 Require SSL
1228
* HSE_URL_FLAGS_DONT_CACHE 0x010 Don't cache (VRoot only)
1229
* HSE_URL_FLAGS_NEGO_CERT 0x020 Allow client SSL cert
1230
* HSE_URL_FLAGS_REQUIRE_CERT 0x040 Require client SSL cert
1231
* HSE_URL_FLAGS_MAP_CERT 0x080 Map client SSL cert to account
1232
* HSE_URL_FLAGS_SSL128 0x100 Require 128-bit SSL cert
1233
* HSE_URL_FLAGS_SCRIPT 0x200 Allow script execution
1235
* XxX: As everywhere, EXEC flags could use some work...
1236
* and this could go further with more flags, as desired.
1238
info->dwFlags = (subreq->finfo.protection & APR_UREAD ? 0x001 : 0)
1239
| (subreq->finfo.protection & APR_UWRITE ? 0x002 : 0)
1240
| (subreq->finfo.protection & APR_UEXECUTE ? 0x204 : 0);
1244
case HSE_REQ_ABORTIVE_CLOSE:
1245
if (cid->dconf.log_unsupported)
1246
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1247
"ISAPI: ServerSupportFunction HSE_REQ_ABORTIVE_CLOSE"
1248
" is not supported: %s", r->filename);
1249
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
1252
case HSE_REQ_GET_CERT_INFO_EX: /* Added in ISAPI 4.0 */
1253
if (cid->dconf.log_unsupported)
1254
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1255
"ISAPI: ServerSupportFunction "
1256
"HSE_REQ_GET_CERT_INFO_EX "
1257
"is not supported: %s", r->filename);
1258
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
1261
case HSE_REQ_SEND_RESPONSE_HEADER_EX: /* Added in ISAPI 4.0 */
1263
HSE_SEND_HEADER_EX_INFO *shi = (HSE_SEND_HEADER_EX_INFO*)buf_data;
1265
/* Ignore shi->fKeepConn - we don't want the advise
1267
apr_ssize_t ate = send_response_header(cid, shi->pszStatus,
1272
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
1275
else if ((apr_size_t)ate < shi->cchHeader) {
1276
apr_bucket_brigade *bb;
1278
bb = apr_brigade_create(cid->r->pool, c->bucket_alloc);
1279
b = apr_bucket_transient_create(shi->pszHeader + ate,
1280
shi->cchHeader - ate,
1282
APR_BRIGADE_INSERT_TAIL(bb, b);
1283
b = apr_bucket_flush_create(c->bucket_alloc);
1284
APR_BRIGADE_INSERT_TAIL(bb, b);
1285
ap_pass_brigade(cid->r->output_filters, bb);
1286
cid->response_sent = 1;
1291
case HSE_REQ_CLOSE_CONNECTION: /* Added after ISAPI 4.0 */
1292
if (cid->dconf.log_unsupported)
1293
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1294
"ISAPI: ServerSupportFunction "
1295
"HSE_REQ_CLOSE_CONNECTION "
1296
"is not supported: %s", r->filename);
1297
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
1300
case HSE_REQ_IS_CONNECTED: /* Added after ISAPI 4.0 */
1301
/* Returns True if client is connected c.f. MSKB Q188346
1302
* assuming the identical return mechanism as HSE_REQ_IS_KEEP_CONN
1304
*((int *)buf_data) = (r->connection->aborted == 0);
1307
case HSE_REQ_EXTENSION_TRIGGER: /* Added after ISAPI 4.0 */
1308
/* Undocumented - defined by the Microsoft Jan '00 Platform SDK
1310
if (cid->dconf.log_unsupported)
1311
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1312
"ISAPI: ServerSupportFunction "
1313
"HSE_REQ_EXTENSION_TRIGGER "
1314
"is not supported: %s", r->filename);
1315
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
1319
if (cid->dconf.log_unsupported)
1320
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1321
"ISAPI: ServerSupportFunction (%d) not supported: "
1322
"%s", HSE_code, r->filename);
1323
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER));
1328
/**********************************************************
1330
* ISAPI Module request invocation section
1332
**********************************************************/
1334
apr_status_t isapi_handler (request_rec *r)
1336
isapi_dir_conf *dconf;
1345
if(strcmp(r->handler, "isapi-isa")
1346
&& strcmp(r->handler, "isapi-handler")) {
1347
/* Hang on to the isapi-isa for compatibility with older docs
1348
* (wtf did '-isa' mean in the first place?) but introduce
1349
* a newer and clearer "isapi-handler" name.
1353
dconf = ap_get_module_config(r->per_dir_config, &isapi_module);
1354
e = r->subprocess_env;
1356
/* Use similar restrictions as CGIs
1358
* If this fails, it's pointless to load the isapi dll.
1360
if (!(ap_allow_options(r) & OPT_EXECCGI)) {
1361
return HTTP_FORBIDDEN;
1363
if (r->finfo.filetype == APR_NOFILE) {
1364
return HTTP_NOT_FOUND;
1366
if (r->finfo.filetype != APR_REG) {
1367
return HTTP_FORBIDDEN;
1369
if ((r->used_path_info == AP_REQ_REJECT_PATH_INFO) &&
1370
r->path_info && *r->path_info) {
1371
/* default to accept */
1372
return HTTP_NOT_FOUND;
1375
if (isapi_lookup(r->pool, r->server, r, r->filename, &isa)
1377
return HTTP_INTERNAL_SERVER_ERROR;
1379
/* Set up variables */
1380
ap_add_common_vars(r);
1382
apr_table_setn(e, "UNMAPPED_REMOTE_USER", "REMOTE_USER");
1383
if ((val = apr_table_get(e, "HTTPS")) && strcmp(val, "on"))
1384
apr_table_setn(e, "SERVER_PORT_SECURE", "1");
1386
apr_table_setn(e, "SERVER_PORT_SECURE", "0");
1387
apr_table_setn(e, "URL", r->uri);
1389
/* Set up connection structure and ecb,
1390
* NULL or zero out most fields.
1392
cid = apr_pcalloc(r->pool, sizeof(isapi_cid));
1394
/* Fixup defaults for dconf */
1395
cid->dconf.read_ahead_buflen = (dconf->read_ahead_buflen == ISAPI_UNDEF)
1396
? 49152 : dconf->read_ahead_buflen;
1397
cid->dconf.log_unsupported = (dconf->log_unsupported == ISAPI_UNDEF)
1398
? 0 : dconf->log_unsupported;
1399
cid->dconf.log_to_errlog = (dconf->log_to_errlog == ISAPI_UNDEF)
1400
? 0 : dconf->log_to_errlog;
1401
cid->dconf.log_to_query = (dconf->log_to_query == ISAPI_UNDEF)
1402
? 1 : dconf->log_to_query;
1403
cid->dconf.fake_async = (dconf->fake_async == ISAPI_UNDEF)
1404
? 0 : dconf->fake_async;
1406
cid->ecb = apr_pcalloc(r->pool, sizeof(EXTENSION_CONTROL_BLOCK));
1407
cid->ecb->ConnID = cid;
1412
cid->ecb->cbSize = sizeof(EXTENSION_CONTROL_BLOCK);
1413
cid->ecb->dwVersion = isa->report_version;
1414
cid->ecb->dwHttpStatusCode = 0;
1415
strcpy(cid->ecb->lpszLogData, "");
1416
/* TODO: are copies really needed here?
1418
cid->ecb->lpszMethod = (char*) r->method;
1419
cid->ecb->lpszQueryString = (char*) apr_table_get(e, "QUERY_STRING");
1420
cid->ecb->lpszPathInfo = (char*) apr_table_get(e, "PATH_INFO");
1421
cid->ecb->lpszPathTranslated = (char*) apr_table_get(e, "PATH_TRANSLATED");
1422
cid->ecb->lpszContentType = (char*) apr_table_get(e, "CONTENT_TYPE");
1424
/* Set up the callbacks */
1425
cid->ecb->GetServerVariable = GetServerVariable;
1426
cid->ecb->WriteClient = WriteClient;
1427
cid->ecb->ReadClient = ReadClient;
1428
cid->ecb->ServerSupportFunction = ServerSupportFunction;
1430
/* Set up client input */
1431
res = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR);
1433
isapi_unload(isa, 0);
1437
if (ap_should_client_block(r)) {
1438
/* Time to start reading the appropriate amount of data,
1439
* and allow the administrator to tweak the number
1442
cid->ecb->cbTotalBytes = (apr_size_t)r->remaining;
1443
if (cid->ecb->cbTotalBytes > (apr_uint32_t)cid->dconf.read_ahead_buflen)
1444
cid->ecb->cbAvailable = cid->dconf.read_ahead_buflen;
1446
cid->ecb->cbAvailable = cid->ecb->cbTotalBytes;
1450
cid->ecb->cbTotalBytes = 0xffffffff;
1451
cid->ecb->cbAvailable = cid->dconf.read_ahead_buflen;
1454
cid->ecb->lpbData = apr_pcalloc(r->pool, cid->ecb->cbAvailable + 1);
1457
while (read < cid->ecb->cbAvailable &&
1458
((res = ap_get_client_block(r, (char*)cid->ecb->lpbData + read,
1459
cid->ecb->cbAvailable - read)) > 0)) {
1464
isapi_unload(isa, 0);
1465
return HTTP_INTERNAL_SERVER_ERROR;
1468
/* Although it's not to spec, IIS seems to null-terminate
1469
* its lpdData string. So we will too.
1472
cid->ecb->cbAvailable = cid->ecb->cbTotalBytes = read;
1474
cid->ecb->cbAvailable = read;
1475
cid->ecb->lpbData[read] = '\0';
1478
cid->ecb->cbTotalBytes = 0;
1479
cid->ecb->cbAvailable = 0;
1480
cid->ecb->lpbData = NULL;
1483
/* To emulate async behavior...
1485
* We create a cid->completed mutex and lock on it so that the
1486
* app can believe is it running async.
1488
* This request completes upon a notification through
1489
* ServerSupportFunction(HSE_REQ_DONE_WITH_SESSION), which
1490
* unlocks this mutex. If the HttpExtensionProc() returns
1491
* HSE_STATUS_PENDING, we will attempt to gain this lock again
1492
* which may *only* happen once HSE_REQ_DONE_WITH_SESSION has
1493
* unlocked the mutex.
1495
if (cid->dconf.fake_async) {
1496
rv = apr_thread_mutex_create(&cid->completed,
1497
APR_THREAD_MUTEX_UNNESTED,
1499
if (cid->completed && (rv == APR_SUCCESS)) {
1500
rv = apr_thread_mutex_lock(cid->completed);
1503
if (!cid->completed || (rv != APR_SUCCESS)) {
1504
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1505
"ISAPI: Failed to create completion mutex");
1506
return HTTP_INTERNAL_SERVER_ERROR;
1510
/* All right... try and run the sucker */
1511
rv = (*isa->HttpExtensionProc)(cid->ecb);
1513
/* Check for a log message - and log it */
1514
if (cid->ecb->lpszLogData && *cid->ecb->lpszLogData)
1515
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1516
"ISAPI: %s: %s", r->filename, cid->ecb->lpszLogData);
1519
case 0: /* Strange, but MS isapi accepts this as success */
1520
case HSE_STATUS_SUCCESS:
1521
case HSE_STATUS_SUCCESS_AND_KEEP_CONN:
1522
/* Ignore the keepalive stuff; Apache handles it just fine without
1523
* the ISAPI Handler's "advice".
1524
* Per Microsoft: "In IIS versions 4.0 and later, the return
1525
* values HSE_STATUS_SUCCESS and HSE_STATUS_SUCCESS_AND_KEEP_CONN
1526
* are functionally identical: Keep-Alive connections are
1527
* maintained, if supported by the client."
1528
* ... so we were pat all this time
1532
case HSE_STATUS_PENDING:
1533
/* emulating async behavior...
1535
if (cid->completed) {
1536
/* The completion port was locked prior to invoking
1537
* HttpExtensionProc(). Once we can regain the lock,
1538
* when ServerSupportFunction(HSE_REQ_DONE_WITH_SESSION)
1539
* is called by the extension to release the lock,
1540
* we may finally destroy the request.
1542
(void)apr_thread_mutex_lock(cid->completed);
1545
else if (cid->dconf.log_unsupported) {
1546
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1547
"ISAPI: asynch I/O result HSE_STATUS_PENDING "
1548
"from HttpExtensionProc() is not supported: %s",
1550
r->status = HTTP_INTERNAL_SERVER_ERROR;
1554
case HSE_STATUS_ERROR:
1555
/* end response if we have yet to do so.
1557
r->status = HTTP_INTERNAL_SERVER_ERROR;
1561
/* TODO: log unrecognized retval for debugging
1563
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1564
"ISAPI: return code %d from HttpExtensionProc() "
1565
"was not not recognized", rv);
1566
r->status = HTTP_INTERNAL_SERVER_ERROR;
1570
/* Flush the response now, including headers-only responses */
1571
if (cid->headers_set) {
1572
conn_rec *c = r->connection;
1573
apr_bucket_brigade *bb;
1577
bb = apr_brigade_create(r->pool, c->bucket_alloc);
1578
b = apr_bucket_eos_create(c->bucket_alloc);
1579
APR_BRIGADE_INSERT_TAIL(bb, b);
1580
b = apr_bucket_flush_create(c->bucket_alloc);
1581
APR_BRIGADE_INSERT_TAIL(bb, b);
1582
rv = ap_pass_brigade(r->output_filters, bb);
1583
cid->response_sent = 1;
1585
return OK; /* NOT r->status or cid->r->status, even if it has changed. */
1588
/* As the client returned no error, and if we did not error out
1589
* ourselves, trust dwHttpStatusCode to say something relevant.
1591
if (!ap_is_HTTP_SERVER_ERROR(r->status) && cid->ecb->dwHttpStatusCode) {
1592
r->status = cid->ecb->dwHttpStatusCode;
1595
/* For all missing-response situations simply return the status.
1596
* and let the core deal respond to the client.
1601
/**********************************************************
1603
* ISAPI Module Setup Hooks
1605
**********************************************************/
1607
static int isapi_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
1611
apr_pool_create_ex(&loaded.pool, pconf, NULL, NULL);
1613
ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, NULL,
1614
"ISAPI: could not create the isapi cache pool");
1615
return APR_EGENERAL;
1618
loaded.hash = apr_hash_make(loaded.pool);
1620
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
1621
"ISAPI: Failed to create module cache");
1622
return APR_EGENERAL;
1625
rv = apr_thread_mutex_create(&loaded.lock, APR_THREAD_MUTEX_DEFAULT,
1627
if (rv != APR_SUCCESS) {
1628
ap_log_error(APLOG_MARK, rv, 0, NULL,
1629
"ISAPI: Failed to create module cache lock");
1635
static void isapi_hooks(apr_pool_t *cont)
1637
ap_hook_pre_config(isapi_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
1638
ap_hook_handler(isapi_handler, NULL, NULL, APR_HOOK_MIDDLE);
1641
module AP_MODULE_DECLARE_DATA isapi_module = {
1642
STANDARD20_MODULE_STUFF,
1643
create_isapi_dir_config, /* create per-dir config */
1644
merge_isapi_dir_configs, /* merge per-dir config */
1645
NULL, /* server config */
1646
NULL, /* merge server config */
1647
isapi_cmds, /* command apr_table_t */
1648
isapi_hooks /* register hooks */