ISAPI 概要
ISAPI(Internet?Server?Application?Programming?Interface)作為一種可用來替代CGI的方法,是由微軟和Process軟件公司聯(lián)合提出的Web服務(wù)器上的API標(biāo)準(zhǔn)。ISAPI與Web服務(wù)器結(jié)合緊密,功能強(qiáng)大,能夠獲得大量的信息,因此利用ISAPI可以開發(fā)出靈活高效的Web服務(wù)器增強(qiáng)程序。由于ISAPI程序與Web服務(wù)器的關(guān)系,使得ISAPI接口在安全方面有一定的研究價(jià)值。本文主要討論ISAPI在IIS和VC++?6.0中的實(shí)現(xiàn)。?
一、ISAPI接口和CGI接口的不同。?
ISAPI程序和CGI程序完成類似的功能,但是實(shí)現(xiàn)方法不同。?
1、ISAPI程序以DLL形式被Web服務(wù)器加載到自己的進(jìn)程空間中,因此和服務(wù)器共用同一個地址空間,且在沒有客戶請求時可以將其從內(nèi)存中卸載;而對客戶端發(fā)來的每個對CGI程序的請求則需要服務(wù)器為它單獨(dú)啟動一個進(jìn)程,這需要耗費(fèi)大量的時間和內(nèi)存。當(dāng)并發(fā)的請求數(shù)目很大時,使用CGI在效率上不如ISAPI。?
2、CGI程序通過環(huán)境塊和標(biāo)準(zhǔn)輸入輸出與Web服務(wù)器進(jìn)行通信,而ISAPI程序與服務(wù)器結(jié)合得更為緊密,與服務(wù)器共享同一個進(jìn)程上下文,主要通過一個參數(shù)塊與服務(wù)器進(jìn)行交互,可以從服務(wù)器那里獲得關(guān)于當(dāng)前HTTP連接的大量信息。?
ISAPI主要分為ISA和ISAPI?Filter兩部分。ISA方法相對而言要傳統(tǒng)一些,利用一些特殊的鏈接,指向服務(wù)器的作業(yè),供程序開發(fā)人員設(shè)計(jì)一些擴(kuò)展功能;而ISAPI過濾器則傾向于構(gòu)造服務(wù)器直接調(diào)用的模塊,提供一種無縫鏈接部件用于監(jiān)測直接來自于服務(wù)器的HTTP請求。?
二、ISA?
ISA(Internet?Server?Application)也可稱為ISAPI?DLL,其功能和CGI程序的功能直接相對應(yīng),使用方法和CGI也類似,由客戶端在URL中指定其名稱而激活。例如下面的請求將調(diào)用服務(wù)器的虛擬可執(zhí)行目錄Scripts下的function.dll(ISAPI?DLL必須放在服務(wù)器的虛擬可執(zhí)行目錄下):?
http://www.abc.com/Scripts/function.dll??
ISA和服務(wù)器之間的接口主要有兩個:GetExtentionVersion(?)和HttpExtentionProc(?)。任何ISA都必須在其PE文件頭的引出表中定義這兩個引出函數(shù),以供Web服務(wù)器在適當(dāng)?shù)臅r候調(diào)用。?
1、當(dāng)服務(wù)器剛加載ISA時,它會調(diào)用ISA提供的GetExtentionVersion(?)來獲得該ISA所需要的服務(wù)器版本,并與自己的版本相比較,以保證版本兼容。函數(shù)原型如下:?
BOOL?WINAPI?GetExtentionVersion(HSE_VERSION_INFO?*version);?
typedef?struct?_HSE_VERSION_INFO?
{?
DWORD?dwExtensionVersion;?//版本號?
CHAR?lpszExtensionDesc[HSE_MAX_EXT_DLL_NAME_LEN];?//關(guān)于ISA的描述字符串?
}?HSE_VERSION_INFO,?*LPHSE_VERSION_INFO;?
2、ISA的真正入口是HttpExtentionProc(?),它相當(dāng)于普通C程序的main(?)函數(shù),在這個函數(shù)中根據(jù)不同的客戶請求作不同的處理。服務(wù)器和HttpExtentionProc(?)之間是通過擴(kuò)展控制塊(Extention?Control?Block)來進(jìn)行通信的,即ECB中存放入口參數(shù)和出口參數(shù),包括服務(wù)器提供的幾個回調(diào)函數(shù)的入口地址。函數(shù)原型如下:?
DWORD?HttpExtensionProc(?EXTENSION_CONTROL_BLOCK?*pECB?);?
ECB的結(jié)構(gòu)定義如下(IN表示入口參數(shù),OUT表示出口參數(shù)):?
typedef?struct?_EXTENSION_CONTROL_BLOCK?
{?
DWORD?cbSize;?//IN,本結(jié)構(gòu)的大小,只讀?
DWORD?dwVersion?//IN,版本號,高16位為主版本號,低16位為次版本號?
HCONN?ConnID;?//IN,連接句柄,由服務(wù)器分配,ISA只能讀取該值?
DWORD?dwHttpStatusCode;?//OUT,當(dāng)前完成的事務(wù)狀態(tài)?
CHAR?lpszLogData[HSE_LOG_BUFFER_LEN];?//OUT,需要寫入到日志文件中的內(nèi)容?
LPSTR?lpszMethod;?//IN,等價(jià)于CGI的環(huán)境變量REQUEST_METHOD?
LPSTR?lpszQueryString;?//IN,等價(jià)于環(huán)境變量QUERY_STRING?
LPSTR?lpszPathInfo;?//IN,等價(jià)于環(huán)境變量PATH_INFO?
LPSTR?lpszPathTranslated;?//IN,等價(jià)于環(huán)境變量PATH_TRANSLATED?
DWORD?cbTotalBytes;?//IN,等價(jià)于環(huán)境變量CONTENT_LENGTH?
DWORD?cbAvailable;?//IN,緩沖區(qū)中的可用字節(jié)數(shù)?
LPBYTE?lpbData;?//IN,緩沖區(qū)指針,指向客戶端發(fā)來的數(shù)據(jù)?
LPSTR?lpszContentType;?//IN,等價(jià)于環(huán)境變量CONTENT_TYPE?
//回調(diào)函數(shù),用于返回服務(wù)器的連接信息或特定的服務(wù)器詳細(xì)情況?
BOOL?(?WINAPI?*?GetServerVariable?)?
(?HCONN?hConn,?
LPSTR?lpszVariableName,?
LPVOID?lpvBuffer,?
LPDWORD?lpdwSize?);?
BOOL?(?WINAPI?*?WriteClient?)?//回調(diào)函數(shù),從客戶端的HTTP請求中讀取數(shù)據(jù)?
(?HCONN?ConnID,?
LPVOID?Buffer,?
LPDWORD?lpdwBytes,?
DWORD?dwReserved?);?
BOOL?(?WINAPI?*?ReadClient?)?//回調(diào)函數(shù),向客戶端發(fā)送數(shù)據(jù)?
(?HCONN?ConnID,?
LPVOID?lpvBuffer,?
LPDWORD?lpdwSize?);?
BOOL?(?WINAPI?*?ServerSupportFunction?)?//回調(diào)函數(shù),訪問服務(wù)器的一般和特定功能?
(?HCONN?hConn,?
DWORD?dwHSERRequest,?
LPVOID?lpvBuffer,?
LPDWORD?lpdwSize,?
LPDWORD?lpdwDataType?);?
}?EXTENSION_CONTROL_BLOCK,?*LPEXTENSION_CONTROL_BLOCK;?
在上述ECB中,服務(wù)器不但提供了當(dāng)前HTTP連接的句柄和一些變量,而且提供了4個回調(diào)函數(shù)給ISA調(diào)用,從而使ISA可以獲得更詳盡的信息。?
三、ISAPI?Filter?
ISAPI?Filter位于服務(wù)器和客戶端之間,能夠?qū)Ψ?wù)器和客戶端之間的通信進(jìn)行預(yù)處理和后處理,比如對通信進(jìn)行加密/解密、提供對客戶進(jìn)行身份驗(yàn)證的新方法、提供自定義的日志記錄等,在CGI中沒有與ISAPI?Filter直接相對應(yīng)的部分。?
ISAPI?Filter與服務(wù)器之間的接口有兩個:GetFilterVersion(?)和HttpFilterProc(?)。任何?
ISAPI?Filter都必須引出這兩個函數(shù)以供服務(wù)器調(diào)用。?
1、在注冊表的如下鍵值中存放著所有ISAPI?Filter的文件名,IIS服務(wù)器啟動時從該鍵值中獲得?
Filter的文件名并加載它們。?
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/W3SVC/Parameters/FilterDLL?
2、然后服務(wù)器調(diào)用每個Filter提供的GetFilterVersion(?)函數(shù),獲得版本號以及該Filter希望處理的事件,即ISAPI?Filter通過引出GetFilterVersion(?)函數(shù)來告知服務(wù)器自己希望處理什么類型的事件,因?yàn)镮SAPI?Filter是通過事件來激活的,當(dāng)滿足條件的事件到達(dá)時,服務(wù)器就會調(diào)用Filter引出的主函數(shù)HttpFilterProc(?)對該事件進(jìn)行處理。GetFilterVersion(?)的原型如下:?
BOOL?WINAPI?GetFilterVersion(?
DWORD?dwServerFilterVersion;?//IN,服務(wù)器使用的版本規(guī)范?
DWORD?dwFilterVersion;?//OUT,過濾器使用的版本規(guī)范?
CHAR?lpszFilterDesc[SF_MAX_FILTER_DESC_LEN+1];?//OUT,對該過濾器的描述字符串?
DWORD?dwFlags?//OUT,事件和優(yōu)先級標(biāo)志?
);?
事件和優(yōu)先級標(biāo)志dwFlasg的取值在MSDN中有詳細(xì)解釋,其中包括該Filter被調(diào)用的優(yōu)先級,一般應(yīng)使用默認(rèn)的低優(yōu)先級,否則可能會對系統(tǒng)的性能造成很大影響。?
3、HttpFilterProc(?)是ISAPI?Filter主要的入口函數(shù),它根據(jù)當(dāng)前的事件的不同作出不同的處理。服務(wù)器通過如下的參數(shù)塊和Filter進(jìn)行交互,這個參數(shù)塊的作用和ISA中的ECB類似。?
typedef?struct?_HTTP_FILTER_CONTEXT?
{?
DWORD?cbSize;?//IN,本參數(shù)塊的大小?
DWORD?Revision;?//IN?
PVOID?ServerContext;?//IN,由server使用本參數(shù)?
DWORD?ulReserved;?//IN,由server使用本參數(shù)?
BOOL?fIsSecurePort;?//IN,事件是否發(fā)生在安全端口上?
PVOID?pFilterContext;?//IN/OUT,與本次請求相關(guān)的上下文?
//回調(diào)函數(shù),取得關(guān)于服務(wù)器和本次連接的信息?
BOOL?(WINAPI?*?GetServerVariable)?(?
struct?_HTTP_FILTER_CONTEXT?*?pfc,?
LPSTR?lpszVariableName,?
LPVOID?lpvBuffer,?
LPDWORD?lpdwSize?
);?
BOOL?(WINAPI?*?AddResponseHeaders)?(?//回調(diào)函數(shù),給HTTP響應(yīng)添加一個標(biāo)頭?
struct?_HTTP_FILTER_CONTEXT?*?pfc,?
LPSTR?lpszHeaders,?
DWORD?dwReserved?
);?
BOOL?(WINAPI?*?WriteClient)?(?//回調(diào)函數(shù),將原始數(shù)據(jù)發(fā)送給客戶端?
struct?_HTTP_FILTER_CONTEXT?*?pfc,?
LPVOID?Buffer,?
LPDWORD?lpdwBytes,?
DWORD?dwReserved?
);?
VOID?*?(WINAPI?*?AllocMem)?(?//回調(diào)函數(shù),分配內(nèi)存。?
struct?_HTTP_FILTER_CONTEXT?*?pfc,?
DWORD?cbSize,?
DWORD?dwReserved?
);?
BOOL?(WINAPI?*?ServerSupportFunction)?(?//回調(diào)函數(shù),訪問服務(wù)器的一般和特定功能?
struct?_HTTP_FILTER_CONTEXT?*?pfc,?
enum?SF_REQ_TYPE?sfReq,?
PVOID?pData,?
DWORD?ul1,?
DWORD?ul2?
);?
}?HTTP_FILTER_CONTEXT,?*PHTTP_FILTER_CONTEXT;?
四、VC++?6.0中對ISAPI的支持?
VC++?6.0中定義了5個相關(guān)的類以簡化ISAPI的編程工作:CHttpServer、CHttpServerContext、CHttpFilter、CHttpFilterContext、CHtmlStream,這5個類都沒有父類。其中CHttpServer和CHttpServerContext主要用來編寫ISA,CHttpFilter和CHttpFilterContext則用來編寫ISAPI?Filter,而CHtmlStream則用來操作內(nèi)存中的HTML文件,為其它的4個類提供服務(wù)。CHttpServer在每個ISA中只能有一個實(shí)例,一個CHttpServer可以對應(yīng)多個CHttpServerContext實(shí)例,每個?
CHttpServerContext處理一個客戶請求,這樣可以處理并發(fā)的HTTP請求;CttpFilter和CHttpFilterContext之間的關(guān)系與此類似,在每個ISAPI?Filter中只能有一個CHttpFilter實(shí)例,但是可以有多個CHttpFilterContext來處理并發(fā)的事件。CHttpServer和CHttpFilter是獨(dú)立的類,它們可以共存于一個DLL中,也可以分別在不同的DLL中。?
一個ISA可以提供多個命令,每個命令對應(yīng)于CHttpServer(或其子類)的一個成員函數(shù),客戶端可以在URL中指定命令名及其參數(shù)。在VC++?6.0中是通過parse?map來實(shí)現(xiàn)這種對應(yīng)的。?
Parse?map類似MFC中的Windows消息分發(fā)機(jī)制,通過使用VC提供的DECLARE_PARSE_MAP、BEGIN_PARSE_MAP、ON_PARSE_COMMAND、ON_PARSE_COMMAND_PARAMS、DEFAULT_PARSE_COMMAND、END_PARSE_MAP等宏,可以實(shí)現(xiàn)對不同的命令的處理。每個CHttpServer中只能建立一個parse?map,當(dāng)客戶端給ISA發(fā)來命令的時候,parse?map可以分析HTTP請求中的命令名及其參數(shù),將該命令與相應(yīng)的成員函數(shù)關(guān)聯(lián)起來,即由該成員函數(shù)處理該命令。以MSDN中的例子程序pinball為例,該例中有下面這樣一個表單:?
<form?method=get?action="pinball.dll?">?
<input?type="hidden"?name="MfcISAPICommand"?VALUE="GetImage">?
<input?type="radio"?name="Favorite"?value="1"?checked>?Attack?from?Mars<br>?
<input?type="radio"?name="Favorite"?value="2">?Twilight?Zone<br>?
<input?type="radio"?name="Favorite"?value="3">?The?Addams?Family<br>?
<input?type="radio"?name="Favorite"?value="4">?Cirqus?Voltaire<br>?
<input?type="radio"?name="Favorite"?value="0">?I?don't?see?it?here<br>?
<br>?
<input?type="submit"?value="Show?Me!">?
</form>?
當(dāng)客戶端選中了上面的表單中的“Attack?from?Mars”這一項(xiàng)并點(diǎn)擊了submit按鈕后,服務(wù)器端?
最終將得到如下的URL串:?
http://www.abc.com/pinball.dll?MfcISAPICommand=GetImage&Favorite=1?
在該URL串中,命令名是GetImage,參數(shù)Favorite的值是1,因此pinball.dll中的如下成員函數(shù)?
將被調(diào)用以處理該請求,其中參數(shù)dwChoice對應(yīng)URL中的參數(shù)Favorite:?
void?CPinballExtension::GetImage(CHttpServerContext*?pCtxt,?long?dwChoice);?
而parse?map需要按照下面的形式定義:?
//CPinballExtension從CHttpServer派生而來?
BEGIN_PARSE_MAP(CPinballExtension,?CHttpServer)?
//GetImage是CPinballExtension的成員函數(shù),且有一個long型的參數(shù)即dwChoice?
ON_PARSE_COMMAND(GetImage,?CPinballExtension,?ITS_I4)?
//該參數(shù)在URL中的名字為Favorite?
ON_PARSE_COMMAND_PARAMS("Favorite")?
END_PARSE_MAP(CPinballExtension)?
而對于ISAPI?Filter,在VC中可以通過重載CHttpFilter(或其子類)的不同的成員函數(shù)來實(shí)現(xiàn)對不同事件的處理。可重載的函數(shù)如下,每一個成員函數(shù)均對應(yīng)一個或多個事件:?
OnPreprocHeaders?
OnAuthentication?
OnUrlMap?
OnSendRawData?
OnReadRawData?
OnLog?
OnEndOfNetSession?
MSDN提供了4個關(guān)于ISAPI的編程實(shí)例:counter、MFCUCASE、pinball、wwwquote,有興趣的可看看,本文主要不是介紹編程,所以不再贅述。?
參考資料:?
1、MSDN?
2、《精通CGI編程》,丁一強(qiáng)等,清華大學(xué)出版社?
一、ISAPI接口和CGI接口的不同。?
ISAPI程序和CGI程序完成類似的功能,但是實(shí)現(xiàn)方法不同。?
1、ISAPI程序以DLL形式被Web服務(wù)器加載到自己的進(jìn)程空間中,因此和服務(wù)器共用同一個地址空間,且在沒有客戶請求時可以將其從內(nèi)存中卸載;而對客戶端發(fā)來的每個對CGI程序的請求則需要服務(wù)器為它單獨(dú)啟動一個進(jìn)程,這需要耗費(fèi)大量的時間和內(nèi)存。當(dāng)并發(fā)的請求數(shù)目很大時,使用CGI在效率上不如ISAPI。?
2、CGI程序通過環(huán)境塊和標(biāo)準(zhǔn)輸入輸出與Web服務(wù)器進(jìn)行通信,而ISAPI程序與服務(wù)器結(jié)合得更為緊密,與服務(wù)器共享同一個進(jìn)程上下文,主要通過一個參數(shù)塊與服務(wù)器進(jìn)行交互,可以從服務(wù)器那里獲得關(guān)于當(dāng)前HTTP連接的大量信息。?
ISAPI主要分為ISA和ISAPI?Filter兩部分。ISA方法相對而言要傳統(tǒng)一些,利用一些特殊的鏈接,指向服務(wù)器的作業(yè),供程序開發(fā)人員設(shè)計(jì)一些擴(kuò)展功能;而ISAPI過濾器則傾向于構(gòu)造服務(wù)器直接調(diào)用的模塊,提供一種無縫鏈接部件用于監(jiān)測直接來自于服務(wù)器的HTTP請求。?
二、ISA?
ISA(Internet?Server?Application)也可稱為ISAPI?DLL,其功能和CGI程序的功能直接相對應(yīng),使用方法和CGI也類似,由客戶端在URL中指定其名稱而激活。例如下面的請求將調(diào)用服務(wù)器的虛擬可執(zhí)行目錄Scripts下的function.dll(ISAPI?DLL必須放在服務(wù)器的虛擬可執(zhí)行目錄下):?
http://www.abc.com/Scripts/function.dll??
ISA和服務(wù)器之間的接口主要有兩個:GetExtentionVersion(?)和HttpExtentionProc(?)。任何ISA都必須在其PE文件頭的引出表中定義這兩個引出函數(shù),以供Web服務(wù)器在適當(dāng)?shù)臅r候調(diào)用。?
1、當(dāng)服務(wù)器剛加載ISA時,它會調(diào)用ISA提供的GetExtentionVersion(?)來獲得該ISA所需要的服務(wù)器版本,并與自己的版本相比較,以保證版本兼容。函數(shù)原型如下:?
BOOL?WINAPI?GetExtentionVersion(HSE_VERSION_INFO?*version);?
typedef?struct?_HSE_VERSION_INFO?
{?
DWORD?dwExtensionVersion;?//版本號?
CHAR?lpszExtensionDesc[HSE_MAX_EXT_DLL_NAME_LEN];?//關(guān)于ISA的描述字符串?
}?HSE_VERSION_INFO,?*LPHSE_VERSION_INFO;?
2、ISA的真正入口是HttpExtentionProc(?),它相當(dāng)于普通C程序的main(?)函數(shù),在這個函數(shù)中根據(jù)不同的客戶請求作不同的處理。服務(wù)器和HttpExtentionProc(?)之間是通過擴(kuò)展控制塊(Extention?Control?Block)來進(jìn)行通信的,即ECB中存放入口參數(shù)和出口參數(shù),包括服務(wù)器提供的幾個回調(diào)函數(shù)的入口地址。函數(shù)原型如下:?
DWORD?HttpExtensionProc(?EXTENSION_CONTROL_BLOCK?*pECB?);?
ECB的結(jié)構(gòu)定義如下(IN表示入口參數(shù),OUT表示出口參數(shù)):?
typedef?struct?_EXTENSION_CONTROL_BLOCK?
{?
DWORD?cbSize;?//IN,本結(jié)構(gòu)的大小,只讀?
DWORD?dwVersion?//IN,版本號,高16位為主版本號,低16位為次版本號?
HCONN?ConnID;?//IN,連接句柄,由服務(wù)器分配,ISA只能讀取該值?
DWORD?dwHttpStatusCode;?//OUT,當(dāng)前完成的事務(wù)狀態(tài)?
CHAR?lpszLogData[HSE_LOG_BUFFER_LEN];?//OUT,需要寫入到日志文件中的內(nèi)容?
LPSTR?lpszMethod;?//IN,等價(jià)于CGI的環(huán)境變量REQUEST_METHOD?
LPSTR?lpszQueryString;?//IN,等價(jià)于環(huán)境變量QUERY_STRING?
LPSTR?lpszPathInfo;?//IN,等價(jià)于環(huán)境變量PATH_INFO?
LPSTR?lpszPathTranslated;?//IN,等價(jià)于環(huán)境變量PATH_TRANSLATED?
DWORD?cbTotalBytes;?//IN,等價(jià)于環(huán)境變量CONTENT_LENGTH?
DWORD?cbAvailable;?//IN,緩沖區(qū)中的可用字節(jié)數(shù)?
LPBYTE?lpbData;?//IN,緩沖區(qū)指針,指向客戶端發(fā)來的數(shù)據(jù)?
LPSTR?lpszContentType;?//IN,等價(jià)于環(huán)境變量CONTENT_TYPE?
//回調(diào)函數(shù),用于返回服務(wù)器的連接信息或特定的服務(wù)器詳細(xì)情況?
BOOL?(?WINAPI?*?GetServerVariable?)?
(?HCONN?hConn,?
LPSTR?lpszVariableName,?
LPVOID?lpvBuffer,?
LPDWORD?lpdwSize?);?
BOOL?(?WINAPI?*?WriteClient?)?//回調(diào)函數(shù),從客戶端的HTTP請求中讀取數(shù)據(jù)?
(?HCONN?ConnID,?
LPVOID?Buffer,?
LPDWORD?lpdwBytes,?
DWORD?dwReserved?);?
BOOL?(?WINAPI?*?ReadClient?)?//回調(diào)函數(shù),向客戶端發(fā)送數(shù)據(jù)?
(?HCONN?ConnID,?
LPVOID?lpvBuffer,?
LPDWORD?lpdwSize?);?
BOOL?(?WINAPI?*?ServerSupportFunction?)?//回調(diào)函數(shù),訪問服務(wù)器的一般和特定功能?
(?HCONN?hConn,?
DWORD?dwHSERRequest,?
LPVOID?lpvBuffer,?
LPDWORD?lpdwSize,?
LPDWORD?lpdwDataType?);?
}?EXTENSION_CONTROL_BLOCK,?*LPEXTENSION_CONTROL_BLOCK;?
在上述ECB中,服務(wù)器不但提供了當(dāng)前HTTP連接的句柄和一些變量,而且提供了4個回調(diào)函數(shù)給ISA調(diào)用,從而使ISA可以獲得更詳盡的信息。?
三、ISAPI?Filter?
ISAPI?Filter位于服務(wù)器和客戶端之間,能夠?qū)Ψ?wù)器和客戶端之間的通信進(jìn)行預(yù)處理和后處理,比如對通信進(jìn)行加密/解密、提供對客戶進(jìn)行身份驗(yàn)證的新方法、提供自定義的日志記錄等,在CGI中沒有與ISAPI?Filter直接相對應(yīng)的部分。?
ISAPI?Filter與服務(wù)器之間的接口有兩個:GetFilterVersion(?)和HttpFilterProc(?)。任何?
ISAPI?Filter都必須引出這兩個函數(shù)以供服務(wù)器調(diào)用。?
1、在注冊表的如下鍵值中存放著所有ISAPI?Filter的文件名,IIS服務(wù)器啟動時從該鍵值中獲得?
Filter的文件名并加載它們。?
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/W3SVC/Parameters/FilterDLL?
2、然后服務(wù)器調(diào)用每個Filter提供的GetFilterVersion(?)函數(shù),獲得版本號以及該Filter希望處理的事件,即ISAPI?Filter通過引出GetFilterVersion(?)函數(shù)來告知服務(wù)器自己希望處理什么類型的事件,因?yàn)镮SAPI?Filter是通過事件來激活的,當(dāng)滿足條件的事件到達(dá)時,服務(wù)器就會調(diào)用Filter引出的主函數(shù)HttpFilterProc(?)對該事件進(jìn)行處理。GetFilterVersion(?)的原型如下:?
BOOL?WINAPI?GetFilterVersion(?
DWORD?dwServerFilterVersion;?//IN,服務(wù)器使用的版本規(guī)范?
DWORD?dwFilterVersion;?//OUT,過濾器使用的版本規(guī)范?
CHAR?lpszFilterDesc[SF_MAX_FILTER_DESC_LEN+1];?//OUT,對該過濾器的描述字符串?
DWORD?dwFlags?//OUT,事件和優(yōu)先級標(biāo)志?
);?
事件和優(yōu)先級標(biāo)志dwFlasg的取值在MSDN中有詳細(xì)解釋,其中包括該Filter被調(diào)用的優(yōu)先級,一般應(yīng)使用默認(rèn)的低優(yōu)先級,否則可能會對系統(tǒng)的性能造成很大影響。?
3、HttpFilterProc(?)是ISAPI?Filter主要的入口函數(shù),它根據(jù)當(dāng)前的事件的不同作出不同的處理。服務(wù)器通過如下的參數(shù)塊和Filter進(jìn)行交互,這個參數(shù)塊的作用和ISA中的ECB類似。?
typedef?struct?_HTTP_FILTER_CONTEXT?
{?
DWORD?cbSize;?//IN,本參數(shù)塊的大小?
DWORD?Revision;?//IN?
PVOID?ServerContext;?//IN,由server使用本參數(shù)?
DWORD?ulReserved;?//IN,由server使用本參數(shù)?
BOOL?fIsSecurePort;?//IN,事件是否發(fā)生在安全端口上?
PVOID?pFilterContext;?//IN/OUT,與本次請求相關(guān)的上下文?
//回調(diào)函數(shù),取得關(guān)于服務(wù)器和本次連接的信息?
BOOL?(WINAPI?*?GetServerVariable)?(?
struct?_HTTP_FILTER_CONTEXT?*?pfc,?
LPSTR?lpszVariableName,?
LPVOID?lpvBuffer,?
LPDWORD?lpdwSize?
);?
BOOL?(WINAPI?*?AddResponseHeaders)?(?//回調(diào)函數(shù),給HTTP響應(yīng)添加一個標(biāo)頭?
struct?_HTTP_FILTER_CONTEXT?*?pfc,?
LPSTR?lpszHeaders,?
DWORD?dwReserved?
);?
BOOL?(WINAPI?*?WriteClient)?(?//回調(diào)函數(shù),將原始數(shù)據(jù)發(fā)送給客戶端?
struct?_HTTP_FILTER_CONTEXT?*?pfc,?
LPVOID?Buffer,?
LPDWORD?lpdwBytes,?
DWORD?dwReserved?
);?
VOID?*?(WINAPI?*?AllocMem)?(?//回調(diào)函數(shù),分配內(nèi)存。?
struct?_HTTP_FILTER_CONTEXT?*?pfc,?
DWORD?cbSize,?
DWORD?dwReserved?
);?
BOOL?(WINAPI?*?ServerSupportFunction)?(?//回調(diào)函數(shù),訪問服務(wù)器的一般和特定功能?
struct?_HTTP_FILTER_CONTEXT?*?pfc,?
enum?SF_REQ_TYPE?sfReq,?
PVOID?pData,?
DWORD?ul1,?
DWORD?ul2?
);?
}?HTTP_FILTER_CONTEXT,?*PHTTP_FILTER_CONTEXT;?
四、VC++?6.0中對ISAPI的支持?
VC++?6.0中定義了5個相關(guān)的類以簡化ISAPI的編程工作:CHttpServer、CHttpServerContext、CHttpFilter、CHttpFilterContext、CHtmlStream,這5個類都沒有父類。其中CHttpServer和CHttpServerContext主要用來編寫ISA,CHttpFilter和CHttpFilterContext則用來編寫ISAPI?Filter,而CHtmlStream則用來操作內(nèi)存中的HTML文件,為其它的4個類提供服務(wù)。CHttpServer在每個ISA中只能有一個實(shí)例,一個CHttpServer可以對應(yīng)多個CHttpServerContext實(shí)例,每個?
CHttpServerContext處理一個客戶請求,這樣可以處理并發(fā)的HTTP請求;CttpFilter和CHttpFilterContext之間的關(guān)系與此類似,在每個ISAPI?Filter中只能有一個CHttpFilter實(shí)例,但是可以有多個CHttpFilterContext來處理并發(fā)的事件。CHttpServer和CHttpFilter是獨(dú)立的類,它們可以共存于一個DLL中,也可以分別在不同的DLL中。?
一個ISA可以提供多個命令,每個命令對應(yīng)于CHttpServer(或其子類)的一個成員函數(shù),客戶端可以在URL中指定命令名及其參數(shù)。在VC++?6.0中是通過parse?map來實(shí)現(xiàn)這種對應(yīng)的。?
Parse?map類似MFC中的Windows消息分發(fā)機(jī)制,通過使用VC提供的DECLARE_PARSE_MAP、BEGIN_PARSE_MAP、ON_PARSE_COMMAND、ON_PARSE_COMMAND_PARAMS、DEFAULT_PARSE_COMMAND、END_PARSE_MAP等宏,可以實(shí)現(xiàn)對不同的命令的處理。每個CHttpServer中只能建立一個parse?map,當(dāng)客戶端給ISA發(fā)來命令的時候,parse?map可以分析HTTP請求中的命令名及其參數(shù),將該命令與相應(yīng)的成員函數(shù)關(guān)聯(lián)起來,即由該成員函數(shù)處理該命令。以MSDN中的例子程序pinball為例,該例中有下面這樣一個表單:?
<form?method=get?action="pinball.dll?">?
<input?type="hidden"?name="MfcISAPICommand"?VALUE="GetImage">?
<input?type="radio"?name="Favorite"?value="1"?checked>?Attack?from?Mars<br>?
<input?type="radio"?name="Favorite"?value="2">?Twilight?Zone<br>?
<input?type="radio"?name="Favorite"?value="3">?The?Addams?Family<br>?
<input?type="radio"?name="Favorite"?value="4">?Cirqus?Voltaire<br>?
<input?type="radio"?name="Favorite"?value="0">?I?don't?see?it?here<br>?
<br>?
<input?type="submit"?value="Show?Me!">?
</form>?
當(dāng)客戶端選中了上面的表單中的“Attack?from?Mars”這一項(xiàng)并點(diǎn)擊了submit按鈕后,服務(wù)器端?
最終將得到如下的URL串:?
http://www.abc.com/pinball.dll?MfcISAPICommand=GetImage&Favorite=1?
在該URL串中,命令名是GetImage,參數(shù)Favorite的值是1,因此pinball.dll中的如下成員函數(shù)?
將被調(diào)用以處理該請求,其中參數(shù)dwChoice對應(yīng)URL中的參數(shù)Favorite:?
void?CPinballExtension::GetImage(CHttpServerContext*?pCtxt,?long?dwChoice);?
而parse?map需要按照下面的形式定義:?
//CPinballExtension從CHttpServer派生而來?
BEGIN_PARSE_MAP(CPinballExtension,?CHttpServer)?
//GetImage是CPinballExtension的成員函數(shù),且有一個long型的參數(shù)即dwChoice?
ON_PARSE_COMMAND(GetImage,?CPinballExtension,?ITS_I4)?
//該參數(shù)在URL中的名字為Favorite?
ON_PARSE_COMMAND_PARAMS("Favorite")?
END_PARSE_MAP(CPinballExtension)?
而對于ISAPI?Filter,在VC中可以通過重載CHttpFilter(或其子類)的不同的成員函數(shù)來實(shí)現(xiàn)對不同事件的處理。可重載的函數(shù)如下,每一個成員函數(shù)均對應(yīng)一個或多個事件:?
OnPreprocHeaders?
OnAuthentication?
OnUrlMap?
OnSendRawData?
OnReadRawData?
OnLog?
OnEndOfNetSession?
MSDN提供了4個關(guān)于ISAPI的編程實(shí)例:counter、MFCUCASE、pinball、wwwquote,有興趣的可看看,本文主要不是介紹編程,所以不再贅述。?
參考資料:?
1、MSDN?
2、《精通CGI編程》,丁一強(qiáng)等,清華大學(xué)出版社?
轉(zhuǎn)載于:https://www.cnblogs.com/xuxiaoguang/archive/2007/09/24/904216.html
總結(jié)
- 上一篇: 贺友人升学
- 下一篇: SqlServer一些用法