找回密码
 注册
查看: 4602|回复: 0

IIS的ISAPI接口简介

[复制链接]
发表于 2003-10-13 13:22:17 | 显示全部楼层 |阅读模式
作者:netguy (mailto:netguy@nsfocus.com)
5 H7 |# }) o3 N9 V0 m1 @. o/ u& l# l+ t! X' @+ {7 l. {
ISAPI(Internet Server Application Programming Interface)作为一种可用来替代CGI的方法,是由微软和Process软件公司联合提出的Web服务器上的API标准。ISAPI与Web服务器结合紧密,功能强大,能够获得大量的信息,因此利用ISAPI可以开发出灵活高效的Web服务器增强程序。由于ISAPI程序与Web服务器的关系,使得ISAPI接口在安全方面有一定的研究价值。本文主要讨论ISAPI在IIS和VC++ 6.0中的实现。
* Q/ V3 E' P3 d
. i% B3 ?  m% t' `一、ISAPI接口和CGI接口的不同。
) j' Z! j# C& j* w' X  c$ S: P8 c: a  t* J
ISAPI程序和CGI程序完成类似的功能,但是实现方法不同。# M& g/ d( y$ P) {
+ T& {, i5 B" s* {8 r% e2 s# r) R5 O
1、ISAPI程序以DLL形式被Web服务器加载到自己的进程空间中,因此和服务器共用同一个地址空间,且在没有客户请求时可以将其从内存中卸载;而对客户端发来的每个对CGI程序的请求则需要服务器为它单独启动一个进程,这需要耗费大量的时间和内存。当并发的请求数目很大时,使用CGI在效率上不如ISAPI。
6 _" n* W/ R" g  J9 o! n0 g+ ?$ a+ u
2、CGI程序通过环境块和标准输入输出与Web服务器进行通信,而ISAPI程序与服务器结合得更为紧密,与服务器共享同一个进程上下文,主要通过一个参数块与服务器进行交互,可以从服务器那里获得关于当前HTTP连接的大量信息。
: g( M* B* P" v$ K& `8 e$ n, a, s* T1 |
ISAPI主要分为ISA和ISAPI Filter两部分。ISA方法相对而言要传统一些,利用一些特殊的链接,指向服务器的作业,供程序开发人员设计一些扩展功能;而ISAPI过滤器则倾向于构造服务器直接调用的模块,提供一种无缝链接部件用于监测直接来自于服务器的HTTP请求。
0 ~$ ]$ r- q$ i( P& t) h7 t! R5 _) }& s& i2 L/ H* `( \6 j/ |

! c) V0 o9 U! F6 x二、ISA5 ?6 {! B9 C5 ]
  ~$ J8 {0 N& ]: W4 d
ISA(Internet Server Application)也可称为ISAPI DLL,其功能和CGI程序的功能直接相对应,使用方法和CGI也类似,由客户端在URL中指定其名称而激活。例如下面的请求将调用服务器的虚拟可执行目录Scripts下的function.dll(ISAPI DLL必须放在服务器的虚拟可执行目录下):! [3 J2 g3 g% d$ E
http://www.abc.com/Scripts/function.dll?
+ h* _0 o0 C+ ~* d6 z. S; i; [2 ?0 b9 F9 z' v  ]  O& X8 a
ISA和服务器之间的接口主要有两个:GetExtentionVersion( )和HttpExtentionProc( )。任何ISA都必须在其PE文件头的引出表中定义这两个引出函数,以供Web服务器在适当的时候调用。
, f* k, F& B, S& s  i$ t- x  f
7 L( ?5 Q9 [) x; e1、当服务器刚加载ISA时,它会调用ISA提供的GetExtentionVersion( )来获得该ISA所需要的服务器版本,并与自己的版本相比较,以保证版本兼容。函数原型如下:' n" D3 w- }- V3 q, [

+ P5 m! g% M1 A; ^9 aBOOL WINAPI GetExtentionVersion(HSE_VERSION_INFO *version);
9 I5 R6 M+ B2 C2 Mtypedef struct _HSE_VERSION_INFO4 j8 B* R( q" E' }" d  c1 S* [8 ]
{
3 J' F, q0 E0 ?, `9 ^DWORD dwExtensionVersion; //版本号
, C! T* x. I9 {1 |. M# M( NCHAR lpszExtensionDesc[HSE_MAX_EXT_DLL_NAME_LEN]; //关于ISA的描述字符串
- O0 `" F- Y/ f7 L% G) K4 [} HSE_VERSION_INFO, *LPHSE_VERSION_INFO;' U7 }  L7 X  ~- C, I
  l" m* |9 r6 V! B4 N6 l6 K! }* m
2、ISA的真正入口是HttpExtentionProc( ),它相当于普通C程序的main( )函数,在这个函数中根据不同的客户请求作不同的处理。服务器和HttpExtentionProc( )之间是通过扩展控制块(Extention Control Block)来进行通信的,即ECB中存放入口参数和出口参数,包括服务器提供的几个回调函数的入口地址。函数原型如下:
6 V) z, t* S) C) w+ b8 e, P( R$ M. B8 Y' t5 w
DWORD HttpExtensionProc( EXTENSION_CONTROL_BLOCK *pECB );
- L' ?& o" Z# c6 j) ?0 U: r% V1 k! o
ECB的结构定义如下(IN表示入口参数,OUT表示出口参数):
7 ?: K/ I9 q! w1 L
/ J) C) f9 _  h- t) t6 btypedef struct _EXTENSION_CONTROL_BLOCK 5 W1 c% B" m! w8 l# b
{2 \+ ~, H% G. c
DWORD cbSize; //IN,本结构的大小,只读
+ `& ?/ z4 E0 y% NDWORD dwVersion //IN,版本号,高16位为主版本号,低16位为次版本号( M  ?0 T) V$ b% R( n
HCONN ConnID; //IN,连接句柄,由服务器分配,ISA只能读取该值7 L9 x7 v/ K8 o) u) z6 L
DWORD dwHttpStatusCode; //OUT,当前完成的事务状态( _& M( ^$ b) i0 g
CHAR lpszLogData[HSE_LOG_BUFFER_LEN]; //OUT,需要写入到日志文件中的内容
: o, g  N- j. ~9 `6 r6 dLPSTR lpszMethod; //IN,等价于CGI的环境变量REQUEST_METHOD
5 U+ O5 A/ I( R2 z8 f/ ILPSTR lpszQueryString; //IN,等价于环境变量QUERY_STRING# p, t) P! c1 g1 C1 @, ^5 E7 y, t
LPSTR lpszPathInfo; //IN,等价于环境变量PATH_INFO
) n6 f# i' n+ e' L' o+ s9 xLPSTR lpszPathTranslated; //IN,等价于环境变量PATH_TRANSLATED) O6 t1 b9 k% M, x( _) P
DWORD cbTotalBytes; //IN,等价于环境变量CONTENT_LENGTH
9 Q$ K* n: n0 A( |6 a6 ]! G6 nDWORD cbAvailable; //IN,缓冲区中的可用字节数
- I) f: e3 ^1 Z8 hLPBYTE lpbData; //IN,缓冲区指针,指向客户端发来的数据
/ m$ `! P' E; r3 }LPSTR lpszContentType; //IN,等价于环境变量CONTENT_TYPE
$ S: L( c. U5 }( w
; c; x( B- A: d6 e9 `3 n//回调函数,用于返回服务器的连接信息或特定的服务器详细情况
5 b4 p/ ]' T/ S% Q5 ~: l( xBOOL ( WINAPI * GetServerVariable )
: f" Q5 T) Q: [1 e' F( HCONN hConn,* X# g: M$ W9 l9 H) Z! Y  B& h0 d
LPSTR lpszVariableName,! i$ ~  W  u3 e/ t* H+ D
LPVOID lpvBuffer,
) l( O8 z7 _, h& F+ U6 ^: e9 q2 oLPDWORD lpdwSize );
* A3 q  E. x' ]0 _4 ^8 T
! N  C7 G3 B  P4 Q; U, {BOOL ( WINAPI * WriteClient ) //回调函数,从客户端的HTTP请求中读取数据
; N: f/ u9 r. L( HCONN ConnID,
+ M4 u! N$ E* ~1 w5 \/ _1 RLPVOID Buffer,
  ~' [2 I4 ?/ @3 S4 ?8 VLPDWORD lpdwBytes,
8 M; o. {# t$ l& kDWORD dwReserved );
  m( p# s: l6 f3 `5 R- R
+ r6 ~1 r( P/ C# ?1 z# WBOOL ( WINAPI * ReadClient ) //回调函数,向客户端发送数据
/ |: g: F# @/ x! I. z7 v* G' Z2 Q0 ]5 g( HCONN ConnID,' Q5 x& ?  G8 b5 R
LPVOID lpvBuffer,3 i6 d! g, \2 `! P& k5 J( V
LPDWORD lpdwSize );  J3 U5 N4 i. W& u& j
* z7 v" y/ x1 Q
BOOL ( WINAPI * ServerSupportFunction ) //回调函数,访问服务器的一般和特定功能4 B# Z$ S. k" ^! ], x
( HCONN hConn,
( Z: u2 R4 b* K3 [DWORD dwHSERRequest,
& ~1 m. @, r# T% e. @LPVOID lpvBuffer,, b" I$ i0 [  b7 ]. t9 _- d% @, s
LPDWORD lpdwSize,) U/ v* ~& a5 O2 @) H
LPDWORD lpdwDataType );- u* l! y4 @4 Y- h# P9 T0 \
6 P0 A) k% ]$ L7 S( ?0 y% \
} EXTENSION_CONTROL_BLOCK, *LPEXTENSION_CONTROL_BLOCK;$ Q& |. j* ~$ C) x3 \

0 I& d6 j* O0 M0 t" F, Q. `3 _在上述ECB中,服务器不但提供了当前HTTP连接的句柄和一些变量,而且提供了4个回调函数给ISA调用,从而使ISA可以获得更详尽的信息。
+ t" n  _; E" c3 N5 M& ^6 {3 y/ q) u# w* r7 P! J3 v
三、ISAPI Filter
$ L8 X- q& c( n' x* F4 Y1 J
' }5 u5 r& p1 [6 `& [7 }1 _# d) LISAPI Filter位于服务器和客户端之间,能够对服务器和客户端之间的通信进行预处理和后处理,比如对通信进行加密/解密、提供对客户进行身份验证的新方法、提供自定义的日志记录等,在CGI中没有与ISAPI Filter直接相对应的部分。
' `3 ^  I- k  T$ r4 L! O) ?5 T' U
ISAPI Filter与服务器之间的接口有两个:GetFilterVersion( )和HttpFilterProc( )。任何- r$ ?( C0 |" q  a9 ?3 K0 u/ b6 B
ISAPI Filter都必须引出这两个函数以供服务器调用。- F, {. y) Y0 D, j! \
- ?' o' d! v' J0 |+ \0 A
1、在注册表的如下键值中存放着所有ISAPI Filter的文件名,IIS服务器启动时从该键值中获得
& G6 T7 h/ [* Y" R/ Q3 E& jFilter的文件名并加载它们。
- L- P) [  A3 c4 j  c/ H' q: Q' F4 J0 [: A7 G% k' T: H8 b" _
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/W3SVC/Parameters/FilterDLL
( q/ [; T& |; g& ?: ^8 |, ^# \) C* B4 n: u1 k4 @' P9 w# t
2、然后服务器调用每个Filter提供的GetFilterVersion( )函数,获得版本号以及该Filter希望处理的事件,即ISAPI Filter通过引出GetFilterVersion( )函数来告知服务器自己希望处理什么类型的事件,因为ISAPI Filter是通过事件来激活的,当满足条件的事件到达时,服务器就会调用Filter引出的主函数HttpFilterProc( )对该事件进行处理。GetFilterVersion( )的原型如下:) Q7 _7 h: `6 x; j
, |! {7 F- F* L& a
BOOL WINAPI GetFilterVersion( & z4 N/ P2 ~5 ~% E; p1 r: ]8 W& ^
DWORD dwServerFilterVersion; //IN,服务器使用的版本规范
6 _# Y7 A7 l9 v8 Q6 X5 G% f7 t6 y/ _DWORD dwFilterVersion; //OUT,过滤器使用的版本规范; `3 K8 V+ |+ j
CHAR lpszFilterDesc[SF_MAX_FILTER_DESC_LEN+1]; //OUT,对该过滤器的描述字符串- e$ }% N6 g; W- X* V) ]+ R
DWORD dwFlags //OUT,事件和优先级标志9 l) U1 S0 c  f% K/ V: N
);
. f0 c2 d' ~0 N. |* h. b' Y+ ?9 F3 w7 r2 n1 R
事件和优先级标志dwFlasg的取值在MSDN中有详细解释,其中包括该Filter被调用的优先级,一般应使用默认的低优先级,否则可能会对系统的性能造成很大影响。% L. U# s( f( W, R2 Z

4 |& M: h. C8 u' `1 k& o3、HttpFilterProc( )是ISAPI Filter主要的入口函数,它根据当前的事件的不同作出不同的处理。服务器通过如下的参数块和Filter进行交互,这个参数块的作用和ISA中的ECB类似。( ~" x5 X/ A* p) t. H& S% Y7 R* F3 q

( U/ Y3 U+ H, i6 ~  V4 Ttypedef struct _HTTP_FILTER_CONTEXT* ~8 p% F( }/ `( f* b4 k& _
{
' K1 P& Y4 C9 D( U4 z; Z* P
% V% ?; ~- f# P1 r; f6 H/ Z0 _DWORD cbSize; //IN,本参数块的大小; _! k% u1 ?, P  r* V- K
DWORD Revision; //IN' `0 J' t; F) a
PVOID ServerContext; //IN,由server使用本参数2 C2 t: s6 x( [# e  Z0 Q  D
DWORD ulReserved; //IN,由server使用本参数% W- a1 O1 y! a0 ?, y
BOOL fIsSecurePort; //IN,事件是否发生在安全端口上
/ B! X8 g" Q; T- G+ A5 z8 lPVOID pFilterContext; //IN/OUT,与本次请求相关的上下文4 N" z" k5 e2 D9 o* y3 F7 W

# D' I) p3 }/ A//回调函数,取得关于服务器和本次连接的信息
4 P, J9 e6 y+ k/ I3 r9 b' V  hBOOL (WINAPI * GetServerVariable) (
  o$ l, i& @. O6 V" Q: Astruct _HTTP_FILTER_CONTEXT * pfc,! E# y* K' h; F
LPSTR lpszVariableName,) }- m+ Y$ t! V& f6 y: x1 q3 `7 e
LPVOID lpvBuffer,) q5 e, O' ~, n! @; j! f! ^0 \
LPDWORD lpdwSize! q, v, `. }7 d
); 9 {9 i0 X/ v& t; b0 T/ A$ f5 E! A

  x! K! a% Y! F0 \4 E& bBOOL (WINAPI * AddResponseHeaders) ( //回调函数,给HTTP响应添加一个标头
; g4 S5 @, h+ vstruct _HTTP_FILTER_CONTEXT * pfc,, g5 _  X% Y( @8 k/ S$ _: {) W2 E! }
LPSTR lpszHeaders,
0 v9 ^4 u4 A1 C6 EDWORD dwReserved
3 Z/ P  \% Q. d; ~# {); ( D" Y6 i7 s& x2 x

* ]# y! @% Q# ~( g' N, G+ g$ wBOOL (WINAPI * WriteClient) ( //回调函数,将原始数据发送给客户端" ?- o+ w; F1 C2 W+ E, {+ U
struct _HTTP_FILTER_CONTEXT * pfc,
; ?' x+ a5 H8 QLPVOID Buffer,
; X5 j" \  X! Z4 z2 |LPDWORD lpdwBytes,- e- }1 y% p- ?" C, m
DWORD dwReserved0 t' g: Z$ `: R( [
); 4 K# |5 A, i0 A8 w, ?
, Z5 P; n6 B. _" {- @6 K; Q
VOID * (WINAPI * AllocMem) ( //回调函数,分配内存。, X+ N" ?. }6 P  U/ P& U
struct _HTTP_FILTER_CONTEXT * pfc,
! ?  L/ A! h  h9 H) RDWORD cbSize,. u, T1 c" I2 y8 X
DWORD dwReserved- Y8 t" x& B1 U: T
); 3 h6 _* A% P2 q$ x' [
( V0 p0 T, V) m  |
BOOL (WINAPI * ServerSupportFunction) ( //回调函数,访问服务器的一般和特定功能
7 V3 \+ {" n$ x6 C" @4 b6 `* e" [struct _HTTP_FILTER_CONTEXT * pfc,& u8 y7 ~8 S1 C' l- |! c
enum SF_REQ_TYPE sfReq,
/ i& {' q$ v9 J, ^0 P, ~PVOID pData,
4 \6 d% _; H$ a8 H$ t' ZDWORD ul1,
5 A2 r4 {2 }" Y. ?4 ~7 jDWORD ul2, A* c! ^/ _, N9 w' k
); 5 x1 M8 N. y  u' n$ c# I
  J& ^/ ]* l3 }0 O6 j$ Z, I; I
} HTTP_FILTER_CONTEXT, *PHTTP_FILTER_CONTEXT;
5 i$ U: ]+ F, R3 W
# y6 W* ~& P4 ^四、VC++ 6.0中对ISAPI的支持
% M$ f6 }% o+ W, @
. T; D9 }  e. F5 Q0 r3 P$ N: tVC++ 6.0中定义了5个相关的类以简化ISAPI的编程工作:CHttpServer、CHttpServerContext、CHttpFilter、CHttpFilterContext、CHtmlStream,这5个类都没有父类。其中CHttpServer和CHttpServerContext主要用来编写ISA,CHttpFilter和CHttpFilterContext则用来编写ISAPI Filter,而CHtmlStream则用来操作内存中的HTML文件,为其它的4个类提供服务。CHttpServer在每个ISA中只能有一个实例,一个CHttpServer可以对应多个CHttpServerContext实例,每个: u0 m1 P( d. A% A5 \; h
CHttpServerContext处理一个客户请求,这样可以处理并发的HTTP请求;CttpFilter和CHttpFilterContext之间的关系与此类似,在每个ISAPI Filter中只能有一个CHttpFilter实例,但是可以有多个CHttpFilterContext来处理并发的事件。CHttpServer和CHttpFilter是独立的类,它们可以共存于一个DLL中,也可以分别在不同的DLL中。2 c9 \8 a9 ^# E; P" r
* W8 _3 Q& E1 q7 z" b- u
一个ISA可以提供多个命令,每个命令对应于CHttpServer(或其子类)的一个成员函数,客户端可以在URL中指定命令名及其参数。在VC++ 6.0中是通过parse map来实现这种对应的。
' W9 ^* H0 W$ I% v7 r$ ~( s
9 e6 l2 v8 ], l, F" }& a0 vParse map类似MFC中的Windows消息分发机制,通过使用VC提供的DECLARE_PARSE_MAP、BEGIN_PARSE_MAP、ON_PARSE_COMMAND、ON_PARSE_COMMAND_PARAMS、DEFAULT_PARSE_COMMAND、END_PARSE_MAP等宏,可以实现对不同的命令的处理。每个CHttpServer中只能建立一个parse map,当客户端给ISA发来命令的时候,parse map可以分析HTTP请求中的命令名及其参数,将该命令与相应的成员函数关联起来,即由该成员函数处理该命令。以MSDN中的例子程序pinball为例,该例中有下面这样一个表单:/ L7 V2 N. q6 _+ m8 J  q
) x: b: F: |$ j, t4 |* \7 F
<form method=get action="pinball.dll?">4 h- F7 F: B' k" y8 {
<input type="hidden" name="MfcISAPICommand" VALUE="GetImage">
2 L1 j5 e7 q+ `) x  ]+ _<input type="radio" name="Favorite" value="1" checked> Attack from Mars<br>7 g  ~$ s$ s0 z1 s8 [* R% q
<input type="radio" name="Favorite" value="2"> Twilight Zone<br>; f+ O+ t1 t  K- P  _& c9 ]- ?
<input type="radio" name="Favorite" value="3"> The Addams Family<br>
! C! B5 u& L( T0 `5 r<input type="radio" name="Favorite" value="4"> Cirqus Voltaire<br>
: @# k7 r: W  d7 h, l<input type="radio" name="Favorite" value="0"> I don't see it here<br>
( g+ `3 U. K$ p9 o<br>
5 k8 {$ L% h* f6 f<input type="submit" value="Show Me!">$ Q+ T  Z7 w& D
</form>! u% g5 |/ @- M
! o$ p* b: K- Y' ~( @% I- l
当客户端选中了上面的表单中的“Attack from Mars”这一项并点击了submit按钮后,服务器端% i) j4 z2 H3 f: k# t/ Z8 q$ Z
最终将得到如下的URL串:
0 F+ U& R1 n( \, j: ]
4 _" f- w8 i% i" ohttp://www.abc.com/pinball.dll?M ... GetImage&Favorite=1
8 ^) p( r2 @9 B8 D$ i  m5 l- L8 ~
在该URL串中,命令名是GetImage,参数Favorite的值是1,因此pinball.dll中的如下成员函数
* C, v* {+ [# R* A. k  l将被调用以处理该请求,其中参数dwChoice对应URL中的参数Favorite:/ j7 g/ i2 i" f& s2 v
( @/ k! u' k2 {7 Y1 ?2 \( \" ]
void CPinballExtension::GetImage(CHttpServerContext* pCtxt, long dwChoice);
1 P1 k, Y2 ?9 c: Q
9 {; n) b! o* q6 M1 X$ Z( {9 S而parse map需要按照下面的形式定义:- x# u  D' V$ K* q) |# q  X

' \1 s% q/ t+ W: `3 K& U- ]//CPinballExtension从CHttpServer派生而来) C; d( P9 j' v; |8 V: k, V9 \
BEGIN_PARSE_MAP(CPinballExtension, CHttpServer)
7 z/ Z; b' @* Q
' ]6 p$ A1 q% T//GetImage是CPinballExtension的成员函数,且有一个long型的参数即dwChoice+ d6 F( ^0 ~2 {& l
ON_PARSE_COMMAND(GetImage, CPinballExtension, ITS_I4)
7 K& R9 p; h6 P( }- h- |
, i' k' g" c3 I9 H$ p! `//该参数在URL中的名字为Favorite6 q- }: u2 k2 O3 P& o9 D5 a
ON_PARSE_COMMAND_PARAMS("Favorite") , L7 x) L9 O1 o* n

6 m8 |! U- G5 }- N( s( ]% q& IEND_PARSE_MAP(CPinballExtension)& c% h) \3 Q+ l3 g8 `

+ @2 ~) x( C) S# a而对于ISAPI Filter,在VC中可以通过重载CHttpFilter(或其子类)的不同的成员函数来实现对不同事件的处理。可重载的函数如下,每一个成员函数均对应一个或多个事件:5 }( k, e& u* M9 `) o5 _

* c( y+ Z! e' I- T0 aOnPreprocHeaders
  E2 @; C8 i! \* \: m0 IOnAuthentication* [7 k9 q$ j! |6 B$ l
OnUrlMap
$ e3 _- m: ]( J+ G" F  }OnSendRawData0 j! c3 D% u0 Q! e5 c
OnReadRawData% X0 J! D0 S% K% {: t
OnLog
7 s1 j) n3 n/ M) q3 w* |) MOnEndOfNetSession
2 {) Z$ u- U7 `5 d& h9 K  }  z$ A! {4 c- I% C8 q
MSDN提供了4个关于ISAPI的编程实例:counter、MFCUCASE、pinball、wwwquote,有兴趣的可看看,本文主要不是介绍编程,所以不再赘述。
1 c% q7 S) Z  S9 Y0 H  h6 U& n' T5 x: x' t1 i
参考资料:
. j$ Y, L, I1 q% r* \/ J' q" t  Y
1、MSDN# g2 U+ m, w* B, V! I( ^
2、《精通CGI编程》,丁一强等,清华大学出版社
您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|手机版|小黑屋|宁德市腾云网络科技有限公司 ( 闽ICP备2022007940号-5|闽公网安备 35092202000206号 )

GMT+8, 2026-10-2 00:30 , Processed in 0.017231 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表