📄 mod_isapi.c
字号:
* lock so other requests can proceed, then rdlock for completion
* of loading our desired dll or wrlock if we would like to retry
* loading the dll (because last_load_rv failed and retry is up.)
*/
apr_thread_rwlock_t *gainlock = (*isa)->in_progress;
/* gainlock is NULLed after the module loads successfully.
* This free-threaded module can be used without any locking.
*/
if (!gainlock) {
rv = (*isa)->last_load_rv;
apr_thread_mutex_unlock(loaded.lock);
return rv;
}
if ((*isa)->last_load_rv == APR_SUCCESS) {
apr_thread_mutex_unlock(loaded.lock);
if ((rv = apr_thread_rwlock_rdlock(gainlock))
!= APR_SUCCESS) {
return rv;
}
rv = (*isa)->last_load_rv;
apr_thread_rwlock_unlock(gainlock);
return rv;
}
if (apr_time_now() > (*isa)->last_load_time + ISAPI_RETRY) {
/* Remember last_load_time before releasing the global
* hash lock to avoid colliding with another thread
* that hit this exception at the same time as our
* retry attempt, since we unlock the global mutex
* before attempting a write lock for this module.
*/
apr_time_t check_time = (*isa)->last_load_time;
apr_thread_mutex_unlock(loaded.lock);
if ((rv = apr_thread_rwlock_wrlock(gainlock))
!= APR_SUCCESS) {
return rv;
}
/* If last_load_time is unchanged, we still own this
* retry, otherwise presume another thread provided
* our retry (for good or ill). Relock the global
* hash for updating last_load_ vars, so their update
* is always atomic to the global lock.
*/
if (check_time == (*isa)->last_load_time) {
rv = isapi_load(loaded.pool, s, *isa);
apr_thread_mutex_lock(loaded.lock);
(*isa)->last_load_rv = rv;
(*isa)->last_load_time = apr_time_now();
apr_thread_mutex_unlock(loaded.lock);
}
else {
rv = (*isa)->last_load_rv;
}
apr_thread_rwlock_unlock(gainlock);
return rv;
}
/* We haven't hit timeup on retry, let's grab the last_rv
* within the hash mutex before unlocking.
*/
rv = (*isa)->last_load_rv;
apr_thread_mutex_unlock(loaded.lock);
return rv;
}
/* If the module was not found, it's time to create a hash key entry
* before releasing the hash lock to avoid multiple threads from
* loading the same module.
*/
key = apr_pstrdup(loaded.pool, fpath);
*isa = apr_pcalloc(loaded.pool, sizeof(isapi_loaded));
(*isa)->filename = key;
if (r) {
/* A mutex that exists only long enough to attempt to
* load this isapi dll, the release this module to all
* other takers that came along during the one-time
* load process. Short lifetime for this lock would
* be great, however, using r->pool is nasty if those
* blocked on the lock haven't all unlocked before we
* attempt to destroy. A nastier race condition than
* I want to deal with at this moment...
*/
apr_thread_rwlock_create(&(*isa)->in_progress, loaded.pool);
apr_thread_rwlock_wrlock((*isa)->in_progress);
}
apr_hash_set(loaded.hash, key, APR_HASH_KEY_STRING, *isa);
/* Now attempt to load the isapi on our own time,
* allow other isapi processing to resume.
*/
apr_thread_mutex_unlock(loaded.lock);
rv = isapi_load(loaded.pool, s, *isa);
(*isa)->last_load_time = apr_time_now();
(*isa)->last_load_rv = rv;
if (r && (rv == APR_SUCCESS)) {
/* Let others who are blocked on this particular
* module resume their requests, for better or worse.
*/
apr_thread_rwlock_t *unlock = (*isa)->in_progress;
(*isa)->in_progress = NULL;
apr_thread_rwlock_unlock(unlock);
}
else if (!r && (rv != APR_SUCCESS)) {
/* We must leave a rwlock around for requests to retry
* loading this dll after timeup... since we were in
* the setup code we had avoided creating this lock.
*/
apr_thread_rwlock_create(&(*isa)->in_progress, loaded.pool);
}
return (*isa)->last_load_rv;
}
/**********************************************************
*
* ISAPI Module request callbacks section
*
**********************************************************/
/* Our "Connection ID" structure */
struct isapi_cid {
EXTENSION_CONTROL_BLOCK *ecb;
isapi_dir_conf dconf;
isapi_loaded *isa;
request_rec *r;
int headers_set;
int response_sent;
PFN_HSE_IO_COMPLETION completion;
void *completion_arg;
apr_thread_mutex_t *completed;
};
int APR_THREAD_FUNC GetServerVariable (isapi_cid *cid,
char *variable_name,
void *buf_ptr,
apr_uint32_t *buf_size)
{
request_rec *r = cid->r;
const char *result;
char *buf_data = (char*)buf_ptr;
apr_uint32_t len;
if (!strcmp(variable_name, "ALL_HTTP"))
{
/* crlf delimited, colon split, comma separated and
* null terminated list of HTTP_ vars
*/
const apr_array_header_t *arr = apr_table_elts(r->subprocess_env);
const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts;
int i;
for (len = 0, i = 0; i < arr->nelts; i++) {
if (!strncmp(elts[i].key, "HTTP_", 5)) {
len += strlen(elts[i].key) + strlen(elts[i].val) + 3;
}
}
if (*buf_size < len + 1) {
*buf_size = len + 1;
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INSUFFICIENT_BUFFER));
return 0;
}
for (i = 0; i < arr->nelts; i++) {
if (!strncmp(elts[i].key, "HTTP_", 5)) {
strcpy(buf_data, elts[i].key);
buf_data += strlen(elts[i].key);
*(buf_data++) = ':';
strcpy(buf_data, elts[i].val);
buf_data += strlen(elts[i].val);
*(buf_data++) = '\r';
*(buf_data++) = '\n';
}
}
*(buf_data++) = '\0';
*buf_size = len + 1;
return 1;
}
if (!strcmp(variable_name, "ALL_RAW"))
{
/* crlf delimited, colon split, comma separated and
* null terminated list of the raw request header
*/
const apr_array_header_t *arr = apr_table_elts(r->headers_in);
const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts;
int i;
for (len = 0, i = 0; i < arr->nelts; i++) {
len += strlen(elts[i].key) + strlen(elts[i].val) + 4;
}
if (*buf_size < len + 1) {
*buf_size = len + 1;
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INSUFFICIENT_BUFFER));
return 0;
}
for (i = 0; i < arr->nelts; i++) {
strcpy(buf_data, elts[i].key);
buf_data += strlen(elts[i].key);
*(buf_data++) = ':';
*(buf_data++) = ' ';
strcpy(buf_data, elts[i].val);
buf_data += strlen(elts[i].val);
*(buf_data++) = '\r';
*(buf_data++) = '\n';
}
*(buf_data++) = '\0';
*buf_size = len + 1;
return 1;
}
/* Not a special case */
result = apr_table_get(r->subprocess_env, variable_name);
if (result) {
len = strlen(result);
if (*buf_size < len + 1) {
*buf_size = len + 1;
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INSUFFICIENT_BUFFER));
return 0;
}
strcpy(buf_data, result);
*buf_size = len + 1;
return 1;
}
/* Not Found */
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_INVALID_INDEX));
return 0;
}
int APR_THREAD_FUNC ReadClient(isapi_cid *cid,
void *buf_data,
apr_uint32_t *buf_size)
{
request_rec *r = cid->r;
apr_uint32_t read = 0;
int res;
if (r->remaining < *buf_size) {
*buf_size = (apr_size_t)r->remaining;
}
while (read < *buf_size &&
((res = ap_get_client_block(r, (char*)buf_data + read,
*buf_size - read)) > 0)) {
read += res;
}
*buf_size = read;
if (res < 0) {
apr_set_os_error(APR_FROM_OS_ERROR(ERROR_READ_FAULT));
}
return (res >= 0);
}
/* Common code invoked for both HSE_REQ_SEND_RESPONSE_HEADER and
* the newer HSE_REQ_SEND_RESPONSE_HEADER_EX ServerSupportFunction(s)
* as well as other functions that write responses and presume that
* the support functions above are optional.
*
* Other callers trying to split headers and body bytes should pass
* head/headlen alone (leaving stat/statlen NULL/0), so that they
* get a proper count of bytes consumed. The argument passed to stat
* isn't counted as the head bytes are.
*/
static apr_ssize_t send_response_header(isapi_cid *cid,
const char *stat,
const char *head,
apr_size_t statlen,
apr_size_t headlen)
{
int head_present = 1;
int termarg;
int res;
int old_status;
const char *termch;
apr_size_t ate = 0;
if (!head || headlen == 0 || !*head) {
head = stat;
stat = NULL;
headlen = statlen;
statlen = 0;
head_present = 0; /* Don't eat the header */
}
if (!stat || statlen == 0 || !*stat) {
if (head && headlen && *head && ((stat = memchr(head, '\r', headlen))
|| (stat = memchr(head, '\n', headlen))
|| (stat = memchr(head, '\0', headlen))
|| (stat = head + headlen))) {
statlen = stat - head;
if (memchr(head, ':', statlen)) {
stat = "Status: 200 OK";
statlen = strlen(stat);
}
else {
const char *flip = head;
head = stat;
stat = flip;
headlen -= statlen;
ate += statlen;
if (*head == '\r' && headlen)
++head, --headlen, ++ate;
if (*head == '\n' && headlen)
++head, --headlen, ++ate;
}
}
}
if (stat && (statlen > 0) && *stat) {
char *newstat;
if (!apr_isdigit(*stat)) {
const char *stattok = stat;
int toklen = statlen;
while (toklen && *stattok && !apr_isspace(*stattok)) {
++stattok; --toklen;
}
while (toklen && apr_isspace(*stattok)) {
++stattok; --toklen;
}
/* Now decide if we follow the xxx message
* or the http/x.x xxx message format
*/
if (toklen && apr_isdigit(*stattok)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -