📄 mod_isapi.c
字号:
/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *//* * mod_isapi.c - Internet Server Application (ISA) module for Apache * by Alexei Kosut <akosut@apache.org>, significant overhauls and * redesign by William Rowe <wrowe@covalent.net>, and hints from many * other developer/users who have hit on specific flaws. * * This module implements the ISAPI Handler architecture, allowing * Apache to load Internet Server Applications (ISAPI extensions), * similar to the support in IIS, Zope, O'Reilly's WebSite and others. * * It is a complete implementation of the ISAPI 2.0 specification, * except for "Microsoft extensions" to the API which provide * asynchronous I/O. It is further extended to include additional * "Microsoft extentions" through IIS 5.0, with some deficiencies * where one-to-one mappings don't exist. * * Refer to /manual/mod/mod_isapi.html for additional details on * configuration and use, but check this source for specific support * of the API, */#include "ap_config.h"#include "httpd.h"#include "http_config.h"#include "http_core.h"#include "http_protocol.h"#include "http_request.h"#include "http_log.h"#include "util_script.h"#include "mod_core.h"#include "apr_lib.h"#include "apr_strings.h"#include "apr_portable.h"#include "apr_buckets.h"#include "apr_thread_mutex.h"#include "apr_thread_rwlock.h"#include "apr_hash.h"#include "mod_isapi.h"/* Retry frequency for a failed-to-load isapi .dll */#define ISAPI_RETRY apr_time_from_sec(30)/********************************************************** * * ISAPI Module Configuration * **********************************************************/module AP_MODULE_DECLARE_DATA isapi_module;#define ISAPI_UNDEF -1/* Our isapi per-dir config structure */typedef struct isapi_dir_conf { int read_ahead_buflen; int log_unsupported; int log_to_errlog; int log_to_query; int fake_async;} isapi_dir_conf;typedef struct isapi_loaded isapi_loaded;apr_status_t isapi_lookup(apr_pool_t *p, server_rec *s, request_rec *r, const char *fpath, isapi_loaded** isa);static void *create_isapi_dir_config(apr_pool_t *p, char *dummy){ isapi_dir_conf *dir = apr_palloc(p, sizeof(isapi_dir_conf)); dir->read_ahead_buflen = ISAPI_UNDEF; dir->log_unsupported = ISAPI_UNDEF; dir->log_to_errlog = ISAPI_UNDEF; dir->log_to_query = ISAPI_UNDEF; dir->fake_async = ISAPI_UNDEF; return dir;}static void *merge_isapi_dir_configs(apr_pool_t *p, void *base_, void *add_){ isapi_dir_conf *base = (isapi_dir_conf *) base_; isapi_dir_conf *add = (isapi_dir_conf *) add_; isapi_dir_conf *dir = apr_palloc(p, sizeof(isapi_dir_conf)); dir->read_ahead_buflen = (add->read_ahead_buflen == ISAPI_UNDEF) ? base->read_ahead_buflen : add->read_ahead_buflen; dir->log_unsupported = (add->log_unsupported == ISAPI_UNDEF) ? base->log_unsupported : add->log_unsupported; dir->log_to_errlog = (add->log_to_errlog == ISAPI_UNDEF) ? base->log_to_errlog : add->log_to_errlog; dir->log_to_query = (add->log_to_query == ISAPI_UNDEF) ? base->log_to_query : add->log_to_query; dir->fake_async = (add->fake_async == ISAPI_UNDEF) ? base->fake_async : add->fake_async; return dir;}static const char *isapi_cmd_cachefile(cmd_parms *cmd, void *dummy, const char *filename){ isapi_loaded *isa; apr_finfo_t tmp; apr_status_t rv; char *fspec; /* ### Just an observation ... it would be terribly cool to be * able to use this per-dir, relative to the directory block being * defined. The hash result remains global, but shorthand of * <Directory "c:/webapps/isapi"> * ISAPICacheFile myapp.dll anotherapp.dll thirdapp.dll * </Directory> * would be very convienent. */ fspec = ap_server_root_relative(cmd->pool, filename); if (!fspec) { ap_log_error(APLOG_MARK, APLOG_WARNING, APR_EBADPATH, cmd->server, "ISAPI: invalid module path, skipping %s", filename); return NULL; } if ((rv = apr_stat(&tmp, fspec, APR_FINFO_TYPE, cmd->temp_pool)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_WARNING, rv, cmd->server, "ISAPI: unable to stat, skipping %s", fspec); return NULL; } if (tmp.filetype != APR_REG) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, "ISAPI: not a regular file, skipping %s", fspec); return NULL; } /* Load the extention as cached (with null request_rec) */ rv = isapi_lookup(cmd->pool, cmd->server, NULL, fspec, &isa); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_WARNING, rv, cmd->server, "ISAPI: unable to cache, skipping %s", fspec); return NULL; } return NULL;}static const command_rec isapi_cmds[] = { AP_INIT_TAKE1("ISAPIReadAheadBuffer", ap_set_int_slot, (void *)APR_OFFSETOF(isapi_dir_conf, read_ahead_buflen), OR_FILEINFO, "Maximum client request body to initially pass to the" " ISAPI handler (default: 49152)"), AP_INIT_FLAG("ISAPILogNotSupported", ap_set_flag_slot, (void *)APR_OFFSETOF(isapi_dir_conf, log_unsupported), OR_FILEINFO, "Log requests not supported by the ISAPI server" " on or off (default: off)"), AP_INIT_FLAG("ISAPIAppendLogToErrors", ap_set_flag_slot, (void *)APR_OFFSETOF(isapi_dir_conf, log_to_errlog), OR_FILEINFO, "Send all Append Log requests to the error log" " on or off (default: off)"), AP_INIT_FLAG("ISAPIAppendLogToQuery", ap_set_flag_slot, (void *)APR_OFFSETOF(isapi_dir_conf, log_to_query), OR_FILEINFO, "Append Log requests are concatinated to the query args" " on or off (default: on)"), AP_INIT_FLAG("ISAPIFakeAsync", ap_set_flag_slot, (void *)APR_OFFSETOF(isapi_dir_conf, fake_async), OR_FILEINFO, "Fake Asynchronous support for isapi callbacks" " on or off [Experimental] (default: off)"), AP_INIT_ITERATE("ISAPICacheFile", isapi_cmd_cachefile, NULL, RSRC_CONF, "Cache the specified ISAPI extension in-process"), {NULL}};/********************************************************** * * ISAPI Module Cache handling section * **********************************************************//* Our isapi global config values */static struct isapi_global_conf { apr_pool_t *pool; apr_thread_mutex_t *lock; apr_hash_t *hash;} loaded;/* Our loaded isapi module description structure */struct isapi_loaded { const char *filename; apr_thread_rwlock_t *in_progress; apr_status_t last_load_rv; apr_time_t last_load_time; apr_dso_handle_t *handle; HSE_VERSION_INFO *isapi_version; apr_uint32_t report_version; apr_uint32_t timeout; PFN_GETEXTENSIONVERSION GetExtensionVersion; PFN_HTTPEXTENSIONPROC HttpExtensionProc; PFN_TERMINATEEXTENSION TerminateExtension;};static apr_status_t isapi_unload(isapi_loaded *isa, int force){ /* All done with the DLL... get rid of it... * * If optionally cached, and we weren't asked to force the unload, * pass HSE_TERM_ADVISORY_UNLOAD, and if it returns 1, unload, * otherwise, leave it alone (it didn't choose to cooperate.) */ if (!isa->handle) { return APR_SUCCESS; } if (isa->TerminateExtension) { if (force) { (*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD); } else if (!(*isa->TerminateExtension)(HSE_TERM_ADVISORY_UNLOAD)) { return APR_EGENERAL; } } apr_dso_unload(isa->handle); isa->handle = NULL; return APR_SUCCESS;}static apr_status_t cleanup_isapi(void *isa_){ isapi_loaded* isa = (isapi_loaded*) isa_; /* We must force the module to unload, we are about * to lose the isapi structure's allocation entirely. */ return isapi_unload(isa, 1);}static apr_status_t isapi_load(apr_pool_t *p, server_rec *s, isapi_loaded *isa){ apr_status_t rv; isa->isapi_version = apr_pcalloc(p, sizeof(HSE_VERSION_INFO)); /* TODO: These aught to become overrideable, so that we * assure a given isapi can be fooled into behaving well. * * The tricky bit, they aren't really a per-dir sort of * config, they will always be constant across every * reference to the .dll no matter what context (vhost, * location, etc) they apply to. */ isa->report_version = 0x500; /* Revision 5.0 */ isa->timeout = 300 * 1000000; /* microsecs, not used */ rv = apr_dso_load(&isa->handle, isa->filename, p); if (rv) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "ISAPI: failed to load %s", isa->filename); isa->handle = NULL; return rv; } rv = apr_dso_sym((void**)&isa->GetExtensionVersion, isa->handle, "GetExtensionVersion"); if (rv) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "ISAPI: missing GetExtensionVersion() in %s", isa->filename); apr_dso_unload(isa->handle); isa->handle = NULL; return rv; } rv = apr_dso_sym((void**)&isa->HttpExtensionProc, isa->handle, "HttpExtensionProc"); if (rv) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "ISAPI: missing HttpExtensionProc() in %s", isa->filename); apr_dso_unload(isa->handle); isa->handle = NULL; return rv; } /* TerminateExtension() is an optional interface */ rv = apr_dso_sym((void**)&isa->TerminateExtension, isa->handle, "TerminateExtension"); apr_set_os_error(0); /* Run GetExtensionVersion() */ if (!(isa->GetExtensionVersion)(isa->isapi_version)) { apr_status_t rv = apr_get_os_error(); ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "ISAPI: failed call to GetExtensionVersion() in %s", isa->filename); apr_dso_unload(isa->handle); isa->handle = NULL; return rv; } apr_pool_cleanup_register(p, isa, cleanup_isapi, apr_pool_cleanup_null); return APR_SUCCESS;}apr_status_t isapi_lookup(apr_pool_t *p, server_rec *s, request_rec *r, const char *fpath, isapi_loaded** isa){ apr_status_t rv; const char *key; if ((rv = apr_thread_mutex_lock(loaded.lock)) != APR_SUCCESS) { return rv; } *isa = apr_hash_get(loaded.hash, fpath, APR_HASH_KEY_STRING); if (*isa) { /* If we find this lock exists, use a set-aside copy of gainlock * to avoid race conditions on NULLing the in_progress variable * when the load has completed. Release the global isapi hash * lock so other requests can proceed, then rdlock for completion * of loading our desired dll or wrlock if we would like to retry * loading the dll (because last_load_rv failed and retry is up.) */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -