Hopes

          Start Here..

           

          轉(zhuǎn) IIS的ISAPI接口簡(jiǎn)介

          IIS的ISAPI接口簡(jiǎn)介

          摘要:IIS的ISAPI接口簡(jiǎn)介


          ISAPI(Internet Server Application Programming Interface)作為一種可用來(lái)替代CGI的方法,是由微軟和Process軟件公司聯(lián)合提出的Web服務(wù)器上的API標(biāo)準(zhǔn)。ISAPI與Web服務(wù)器結(jié)合緊密,功能強(qiáng)大,能夠獲得大量的信息,因此利用ISAPI可以開(kāi)發(fā)出靈活高效的Web服務(wù)器增強(qiáng)程序。由于ISAPI程序與Web服務(wù)器的關(guān)系,使得ISAPI接口在安全方面有一定的研究?jī)r(jià)值。本文主要討論ISAPI在IIS和VC++ 6.0中的實(shí)現(xiàn)。

          一、ISAPI接口和CGI接口的不同。

          ISAPI程序和CGI程序完成類似的功能,但是實(shí)現(xiàn)方法不同。

          1、ISAPI程序以DLL形式被Web服務(wù)器加載到自己的進(jìn)程空間中,因此和服務(wù)器共用同一個(gè)地址空間,且在沒(méi)有客戶請(qǐng)求時(shí)可以將其從內(nèi)存中卸載;而對(duì)客戶端發(fā)來(lái)的每個(gè)對(duì)CGI程序的請(qǐng)求則需要服務(wù)器為它單獨(dú)啟動(dòng)一個(gè)進(jìn)程,這需要耗費(fèi)大量的時(shí)間和內(nèi)存。當(dāng)并發(fā)的請(qǐng)求數(shù)目很大時(shí),使用CGI在效率上不如ISAPI。

          2、CGI程序通過(guò)環(huán)境塊和標(biāo)準(zhǔn)輸入輸出與Web服務(wù)器進(jìn)行通信,而ISAPI程序與服務(wù)器結(jié)合得更為緊密,與服務(wù)器共享同一個(gè)進(jìn)程上下文,主要通過(guò)一個(gè)參數(shù)塊與服務(wù)器進(jìn)行交互,可以從服務(wù)器那里獲得關(guān)于當(dāng)前HTTP連接的大量信息。

          ISAPI主要分為ISA和ISAPI Filter兩部分。ISA方法相對(duì)而言要傳統(tǒng)一些,利用一些特殊的鏈接,指向服務(wù)器的作業(yè),供程序開(kāi)發(fā)人員設(shè)計(jì)一些擴(kuò)展功能;而ISAPI過(guò)濾器則傾向于構(gòu)造服務(wù)器直接調(diào)用的模塊,提供一種無(wú)縫鏈接部件用于監(jiān)測(cè)直接來(lái)自于服務(wù)器的HTTP請(qǐng)求。


          二、ISA

          ISA(Internet Server Application)也可稱為ISAPI DLL,其功能和CGI程序的功能直接相對(duì)應(yīng),使用方法和CGI也類似,由客戶端在URL中指定其名稱而激活。例如下面的請(qǐng)求將調(diào)用服務(wù)器的虛擬可執(zhí)行目錄Scripts下的function.dll(ISAPI DLL必須放在服務(wù)器的虛擬可執(zhí)行目錄下):
          http://www.abc.com/Scripts/function.dll?

          ISA和服務(wù)器之間的接口主要有兩個(gè):GetExtentionVersion( )和HttpExtentionProc( )。任何ISA都必須在其PE文件頭的引出表中定義這兩個(gè)引出函數(shù),以供Web服務(wù)器在適當(dāng)?shù)臅r(shí)候調(diào)用。

          1、當(dāng)服務(wù)器剛加載ISA時(shí),它會(huì)調(diào)用ISA提供的GetExtentionVersion( )來(lái)獲得該ISA所需要的服務(wù)器版本,并與自己的版本相比較,以保證版本兼容。函數(shù)原型如下:

          BOOL WINAPI GetExtentionVersion(HSE_VERSION_INFO *version);
          typedef struct _HSE_VERSION_INFO
          {
          DWORD dwExtensionVersion; //版本號(hào)
          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ù),在這個(gè)函數(shù)中根據(jù)不同的客戶請(qǐng)求作不同的處理。服務(wù)器和HttpExtentionProc( )之間是通過(guò)擴(kuò)展控制塊(Extention Control Block)來(lái)進(jìn)行通信的,即ECB中存放入口參數(shù)和出口參數(shù),包括服務(wù)器提供的幾個(gè)回調(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,版本號(hào),高16位為主版本號(hào),低16位為次版本號(hào)
          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ā)來(lái)的數(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請(qǐng)求中讀取數(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èn)服務(wù)器的一般和特定功能
          ( HCONN hConn,
          DWORD dwHSERRequest,
          LPVOID lpvBuffer,
          LPDWORD lpdwSize,
          LPDWORD lpdwDataType );

          } EXTENSION_CONTROL_BLOCK, *LPEXTENSION_CONTROL_BLOCK;

          在上述ECB中,服務(wù)器不但提供了當(dāng)前HTTP連接的句柄和一些變量,而且提供了4個(gè)回調(diào)函數(shù)給ISA調(diào)用,從而使ISA可以獲得更詳盡的信息。

          三、ISAPI Filter

          ISAPI Filter位于服務(wù)器和客戶端之間,能夠?qū)Ψ?wù)器和客戶端之間的通信進(jìn)行預(yù)處理和后處理,比如對(duì)通信進(jìn)行加密/解密、提供對(duì)客戶進(jìn)行身份驗(yàn)證的新方法、提供自定義的日志記錄等,在CGI中沒(méi)有與ISAPI Filter直接相對(duì)應(yīng)的部分。

          ISAPI Filter與服務(wù)器之間的接口有兩個(gè):GetFilterVersion( )和HttpFilterProc( )。任何
          ISAPI Filter都必須引出這兩個(gè)函數(shù)以供服務(wù)器調(diào)用。

          1、在注冊(cè)表的如下鍵值中存放著所有ISAPI Filter的文件名,IIS服務(wù)器啟動(dòng)時(shí)從該鍵值中獲得
          Filter的文件名并加載它們。

          HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/W3SVC/Parameters/FilterDLL

          2、然后服務(wù)器調(diào)用每個(gè)Filter提供的GetFilterVersion( )函數(shù),獲得版本號(hào)以及該Filter希望處理的事件,即ISAPI Filter通過(guò)引出GetFilterVersion( )函數(shù)來(lái)告知服務(wù)器自己希望處理什么類型的事件,因?yàn)镮SAPI Filter是通過(guò)事件來(lái)激活的,當(dāng)滿足條件的事件到達(dá)時(shí),服務(wù)器就會(huì)調(diào)用Filter引出的主函數(shù)HttpFilterProc( )對(duì)該事件進(jìn)行處理。GetFilterVersion( )的原型如下:

          BOOL WINAPI GetFilterVersion(
          DWORD dwServerFilterVersion; //IN,服務(wù)器使用的版本規(guī)范
          DWORD dwFilterVersion; //OUT,過(guò)濾器使用的版本規(guī)范
          CHAR lpszFilterDesc[SF_MAX_FILTER_DESC_LEN+1]; //OUT,對(duì)該過(guò)濾器的描述字符串
          DWORD dwFlags //OUT,事件和優(yōu)先級(jí)標(biāo)志
          );

          事件和優(yōu)先級(jí)標(biāo)志dwFlasg的取值在MSDN中有詳細(xì)解釋,其中包括該Filter被調(diào)用的優(yōu)先級(jí),一般應(yīng)使用默認(rèn)的低優(yōu)先級(jí),否則可能會(huì)對(duì)系統(tǒng)的性能造成很大影響。

          3、HttpFilterProc( )是ISAPI Filter主要的入口函數(shù),它根據(jù)當(dāng)前的事件的不同作出不同的處理。服務(wù)器通過(guò)如下的參數(shù)塊和Filter進(jìn)行交互,這個(gè)參數(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,與本次請(qǐng)求相關(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)添加一個(gè)標(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èn)服務(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中對(duì)ISAPI的支持

          VC++ 6.0中定義了5個(gè)相關(guān)的類以簡(jiǎn)化ISAPI的編程工作:CHttpServer、CHttpServerContext、CHttpFilter、CHttpFilterContext、CHtmlStream,這5個(gè)類都沒(méi)有父類。其中CHttpServer和CHttpServerContext主要用來(lái)編寫ISA,CHttpFilter和CHttpFilterContext則用來(lái)編寫ISAPI Filter,而CHtmlStream則用來(lái)操作內(nèi)存中的HTML文件,為其它的4個(gè)類提供服務(wù)。CHttpServer在每個(gè)ISA中只能有一個(gè)實(shí)例,一個(gè)CHttpServer可以對(duì)應(yīng)多個(gè)CHttpServerContext實(shí)例,每個(gè)
          CHttpServerContext處理一個(gè)客戶請(qǐng)求,這樣可以處理并發(fā)的HTTP請(qǐng)求;CttpFilter和CHttpFilterContext之間的關(guān)系與此類似,在每個(gè)ISAPI Filter中只能有一個(gè)CHttpFilter實(shí)例,但是可以有多個(gè)CHttpFilterContext來(lái)處理并發(fā)的事件。CHttpServer和CHttpFilter是獨(dú)立的類,它們可以共存于一個(gè)DLL中,也可以分別在不同的DLL中。

          一個(gè)ISA可以提供多個(gè)命令,每個(gè)命令對(duì)應(yīng)于CHttpServer(或其子類)的一個(gè)成員函數(shù),客戶端可以在URL中指定命令名及其參數(shù)。在VC++ 6.0中是通過(guò)parse map來(lái)實(shí)現(xiàn)這種對(duì)應(yīng)的。

          Parse map類似MFC中的Windows消息分發(fā)機(jī)制,通過(guò)使用VC提供的DECLARE_PARSE_MAP、BEGIN_PARSE_MAP、ON_PARSE_COMMAND、ON_PARSE_COMMAND_PARAMS、DEFAULT_PARSE_COMMAND、END_PARSE_MAP等宏,可以實(shí)現(xiàn)對(duì)不同的命令的處理。每個(gè)CHttpServer中只能建立一個(gè)parse map,當(dāng)客戶端給ISA發(fā)來(lái)命令的時(shí)候,parse map可以分析HTTP請(qǐng)求中的命令名及其參數(shù),將該命令與相應(yīng)的成員函數(shù)關(guān)聯(lián)起來(lái),即由該成員函數(shù)處理該命令。以MSDN中的例子程序pinball為例,該例中有下面這樣一個(gè)表單:

          <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)用以處理該請(qǐng)求,其中參數(shù)dwChoice對(duì)應(yīng)URL中的參數(shù)Favorite:

          void CPinballExtension::GetImage(CHttpServerContext* pCtxt, long dwChoice);

          而parse map需要按照下面的形式定義:

          //CPinballExtension從CHttpServer派生而來(lái)
          BEGIN_PARSE_MAP(CPinballExtension, CHttpServer)

          //GetImage是CPinballExtension的成員函數(shù),且有一個(gè)long型的參數(shù)即dwChoice
          ON_PARSE_COMMAND(GetImage, CPinballExtension, ITS_I4)

          //該參數(shù)在URL中的名字為Favorite
          ON_PARSE_COMMAND_PARAMS("Favorite")

          END_PARSE_MAP(CPinballExtension)

          而對(duì)于ISAPI Filter,在VC中可以通過(guò)重載CHttpFilter(或其子類)的不同的成員函數(shù)來(lái)實(shí)現(xiàn)對(duì)不同事件的處理。可重載的函數(shù)如下,每一個(gè)成員函數(shù)均對(duì)應(yīng)一個(gè)或多個(gè)事件:

          OnPreprocHeaders
          OnAuthentication
          OnUrlMap
          OnSendRawData
          OnReadRawData
          OnLog
          OnEndOfNetSession

          MSDN提供了4個(gè)關(guān)于ISAPI的編程實(shí)例:counter、MFCUCASE、pinball、wwwquote,有興趣的可看看,本文主要不是介紹編程,所以不再贅述。

          參考資料:

          1、MSDN
          2、《精通CGI編程》,丁一強(qiáng)等,清華大學(xué)出版社

          posted on 2012-12-29 11:08 ** 閱讀(193) 評(píng)論(0)  編輯  收藏


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           

          導(dǎo)航

          統(tǒng)計(jì)

          公告

          你好!

          常用鏈接

          留言簿(2)

          隨筆檔案

          文章分類

          文章檔案

          新聞檔案

          相冊(cè)

          收藏夾

          C#學(xué)習(xí)

          友情鏈接

          搜索

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 齐齐哈尔市| 平果县| 庆元县| 张北县| 石屏县| 阿巴嘎旗| 怀来县| 天门市| 定日县| 永吉县| 沁阳市| 海淀区| 莫力| 陇南市| 大连市| 东明县| 宝山区| 苗栗县| 祥云县| 老河口市| 新乡县| 陵川县| 刚察县| 连平县| 汉阴县| 鲁山县| 长春市| 津南区| 洞口县| 石景山区| 祁东县| 蒲城县| 金乡县| 长汀县| 南岸区| 洪雅县| 乐都县| 望都县| 宁海县| 绍兴市| 舒城县|