找回密码
 注册
搜索
查看: 4117|回复: 0

[转载]IIS的ISAPI接口简介

[复制链接]
发表于 2004-6-9 04:43:55 | 显示全部楼层 |阅读模式
ISAPI(Internet Server Application Programming Interface)作为一种可用来替代CGI的方法,是由微软和Process软件公司联合提出的Web服务器上的API标准。ISAPI与Web服务器结合紧密,功能强大,能够获得大量的信息,因此利用ISAPI可以开发出灵活高效的Web服务器增强程序。由于ISAPI程序与Web服务器的关系,使得ISAPI接口在安全方面有一定的研究价值。本文主要讨论ISAPI在IIS和VC++ 6.0中的实现。
" {0 e4 w6 k4 T8 ]" a0 I! Z& y( E' K0 P. E6 ~
一、ISAPI接口和CGI接口的不同。 , `* S$ C- M# v, |$ r6 v5 R7 F
" E7 R* F7 J/ g" R! f
ISAPI程序和CGI程序完成类似的功能,但是实现方法不同。
) X0 s3 Q7 ^: ~
6 c" V, m/ W8 o" T  b( ?# q/ D1、ISAPI程序以DLL形式被Web服务器加载到自己的进程空间中,因此和服务器共用同一个地址空间,且在没有客户请求时可以将其从内存中卸载;而对客户端发来的每个对CGI程序的请求则需要服务器为它单独启动一个进程,这需要耗费大量的时间和内存。当并发的请求数目很大时,使用CGI在效率上不如ISAPI。 ' `, x0 ~1 A1 _  N* v& \* I/ I6 w

3 O5 J7 |; K, Z; _: h2、CGI程序通过环境块和标准输入输出与Web服务器进行通信,而ISAPI程序与服务器结合得更为紧密,与服务器共享同一个进程上下文,主要通过一个参数块与服务器进行交互,可以从服务器那里获得关于当前HTTP连接的大量信息。
7 H( G) ^# Z8 l! ~  o& m) q% g; b! U1 ~6 x* A
ISAPI主要分为ISA和ISAPI Filter两部分。ISA方法相对而言要传统一些,利用一些特殊的链接,指向服务器的作业,供程序开发人员设计一些扩展功能;而ISAPI过滤器则倾向于构造服务器直接调用的模块,提供一种无缝链接部件用于监测直接来自于服务器的HTTP请求。 & A+ |3 \: r" s! R/ n# S% W
" w& |/ C7 r. a7 p
- n6 j( p- b. W
二、ISA ' b3 c$ X, N; I5 x9 u/ u5 H. [

- h5 n, [3 y$ l3 LISA(Internet Server Application)也可称为ISAPI DLL,其功能和CGI程序的功能直接相对应,使用方法和CGI也类似,由客户端在URL中指定其名称而激活。例如下面的请求将调用服务器的虚拟可执行目录Scripts下的function.dll(ISAPI DLL必须放在服务器的虚拟可执行目录下):
: g0 N6 f3 _& vhttp://www.abc.com/Scripts/function.dll? + t; z9 T$ |8 _* `' k: H- W) Y
$ z, r. P; ^' O! ~
ISA和服务器之间的接口主要有两个:GetExtentionVersion( )和HttpExtentionProc( )。任何ISA都必须在其PE文件头的引出表中定义这两个引出函数,以供Web服务器在适当的时候调用。
2 v7 @) C% H& a$ ?7 L5 G8 ^  ~# Q, B, q: a
1、当服务器刚加载ISA时,它会调用ISA提供的GetExtentionVersion( )来获得该ISA所需要的服务器版本,并与自己的版本相比较,以保证版本兼容。函数原型如下:
) W2 Y8 ^* }8 `; A
9 m- }! w8 s6 @9 q- sBOOL WINAPI GetExtentionVersion(HSE_VERSION_INFO *version);
; B0 A2 j; f8 j) g: ~1 V  c; qtypedef struct _HSE_VERSION_INFO
0 K5 F$ e6 C' D  Z/ O& A# z{
) Y7 r; X+ |+ R/ `( o* EDWORD dwExtensionVersion; //版本号
  E5 o$ x; x5 Q( f( }- E3 ~CHAR lpszExtensionDesc[HSE_MAX_EXT_DLL_NAME_LEN]; //关于ISA的描述字符串 0 a, Q' w+ s) b; K( P* |: @, P( R5 K
} HSE_VERSION_INFO, *LPHSE_VERSION_INFO;
1 D1 J' V) O  T- N5 A4 S$ B+ R" Q$ J
2、ISA的真正入口是HttpExtentionProc( ),它相当于普通C程序的main( )函数,在这个函数中根据不同的客户请求作不同的处理。服务器和HttpExtentionProc( )之间是通过扩展控制块(Extention Control Block)来进行通信的,即ECB中存放入口参数和出口参数,包括服务器提供的几个回调函数的入口地址。函数原型如下:
% E* \% M4 q# n8 o, p7 p9 d. m. _
DWORD HttpExtensionProc( EXTENSION_CONTROL_BLOCK *pECB ); ' [$ m( C, F4 a5 d: T& y; C
) B0 [, w5 F4 ~/ n+ v! ]3 K
ECB的结构定义如下(IN表示入口参数,OUT表示出口参数): ' ^' E8 @5 i7 y
" {. g0 b1 T7 B  b
typedef struct _EXTENSION_CONTROL_BLOCK
0 Y7 g: g% A5 h5 G{ + q2 C% x% h+ r  v: t; d
DWORD cbSize; //IN,本结构的大小,只读
3 ?; X: B  C% k; c* N& aDWORD dwVersion //IN,版本号,高16位为主版本号,低16位为次版本号
3 B' U* M) X/ w* r  |' I* ~8 BHCONN ConnID; //IN,连接句柄,由服务器分配,ISA只能读取该值
1 p6 L/ Y, x$ ]$ Y% j# _8 gDWORD dwHttpStatusCode; //OUT,当前完成的事务状态 ( A# [* l5 s3 ^7 U
CHAR lpszLogData[HSE_LOG_BUFFER_LEN]; //OUT,需要写入到日志文件中的内容 ( Z% Q. q- A" E* \+ m
LPSTR lpszMethod; //IN,等价于CGI的环境变量REQUEST_METHOD ' |" a; Q. A' P
LPSTR lpszQueryString; //IN,等价于环境变量QUERY_STRING   l2 [9 F; Z4 w% C. `# C
LPSTR lpszPathInfo; //IN,等价于环境变量PATH_INFO + w$ l3 D! \+ a% @; y: x4 P
LPSTR lpszPathTranslated; //IN,等价于环境变量PATH_TRANSLATED
/ g% V& j0 x" ^/ J" Q7 UDWORD cbTotalBytes; //IN,等价于环境变量CONTENT_LENGTH
/ i/ @# w$ }3 oDWORD cbAvailable; //IN,缓冲区中的可用字节数
7 B, {7 n5 G- E: M7 m! l" ]% R# MLPBYTE lpbData; //IN,缓冲区指针,指向客户端发来的数据
; n6 G0 N1 N8 d% f" j3 }1 k4 {LPSTR lpszContentType; //IN,等价于环境变量CONTENT_TYPE , t; \$ I  t% h- u7 y
7 o5 Q# y4 ?. c! L( @
//回调函数,用于返回服务器的连接信息或特定的服务器详细情况
7 ^2 Y, T9 H& b2 {' Y: b$ SBOOL ( WINAPI * GetServerVariable ) 5 g; B8 M( n* x. g
( HCONN hConn,
# P. n( N! v& G1 CLPSTR lpszVariableName,
1 R. t" S! w# iLPVOID lpvBuffer,
+ `& B6 }: w, m- h' }3 i& y/ ]6 S( i7 y( `LPDWORD lpdwSize ); / E" K  m  V: ^/ e7 a
  S& _% |( [  r& r9 ~2 B. q  V
BOOL ( WINAPI * WriteClient ) //回调函数,从客户端的HTTP请求中读取数据
$ B/ E' u, c, J2 ]( HCONN ConnID, 5 Q- P: E. O, L0 k# d
LPVOID Buffer,
  c) s- b& S( P7 ZLPDWORD lpdwBytes, 3 ]! t. y: ]7 d; W
DWORD dwReserved ); 6 S2 c& W7 N3 F: u- w
* g8 ]+ Y- x* @: A+ g
BOOL ( WINAPI * ReadClient ) //回调函数,向客户端发送数据
8 r$ A# x8 D5 @/ c( HCONN ConnID, & |' [$ j5 F2 z# s
LPVOID lpvBuffer, 7 W/ G0 ]  ?7 C3 Y/ G
LPDWORD lpdwSize );
+ @# Y1 @0 p% q
, i: [% {: C- k/ C- a$ L6 IBOOL ( WINAPI * ServerSupportFunction ) //回调函数,访问服务器的一般和特定功能 . C; X" i% \# Q) S4 J9 C& c' W- V
( HCONN hConn,
) m, |1 W7 `+ s1 g8 S: wDWORD dwHSERRequest, 9 w; E& k; j, x/ z
LPVOID lpvBuffer, ! e6 r+ K& b7 Q9 n" s! A# B
LPDWORD lpdwSize,
! k" I8 O7 b9 FLPDWORD lpdwDataType );
( V" K. O3 g5 L0 P7 h9 g* B; m6 q1 c. t5 \
} EXTENSION_CONTROL_BLOCK, *LPEXTENSION_CONTROL_BLOCK;
# o* k( ?/ q9 U0 M( q3 B
( Z! z# q5 A* u  c" b* B在上述ECB中,服务器不但提供了当前HTTP连接的句柄和一些变量,而且提供了4个回调函数给ISA调用,从而使ISA可以获得更详尽的信息。
3 u7 b' x2 j9 v% r; q; w' R$ l+ z* x& N% G- f2 x6 m
三、ISAPI Filter , {" ^5 M+ o0 _; t2 a6 x' ?' i
) w$ r  X- a* V$ o3 X6 ?6 O6 ]! o
ISAPI Filter位于服务器和客户端之间,能够对服务器和客户端之间的通信进行预处理和后处理,比如对通信进行加密/解密、提供对客户进行身份验证的新方法、提供自定义的日志记录等,在CGI中没有与ISAPI Filter直接相对应的部分。
2 X' b# F" w! e( a) V
9 j& C- @4 ?# t/ ~9 tISAPI Filter与服务器之间的接口有两个:GetFilterVersion( )和HttpFilterProc( )。任何
$ w  P4 M: l* t2 S/ e" x: u3 O! TISAPI Filter都必须引出这两个函数以供服务器调用。 $ O. A& R) i6 m

# e5 L0 k+ X5 V9 j! M8 r  ~0 N1、在注册表的如下键值中存放着所有ISAPI Filter的文件名,IIS服务器启动时从该键值中获得
9 l( w* S! z) O5 K$ W; B8 J; i- EFilter的文件名并加载它们。
4 X3 R3 |0 P# Y. D* q7 z2 H* A5 l; z0 {% e' m. ^8 X1 t
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/W3SVC/Parameters/FilterDLL
2 d0 O2 V7 O( L2 Z' b9 D4 g2 E: h* s+ V7 k" K
2、然后服务器调用每个Filter提供的GetFilterVersion( )函数,获得版本号以及该Filter希望处理的事件,即ISAPI Filter通过引出GetFilterVersion( )函数来告知服务器自己希望处理什么类型的事件,因为ISAPI Filter是通过事件来激活的,当满足条件的事件到达时,服务器就会调用Filter引出的主函数HttpFilterProc( )对该事件进行处理。GetFilterVersion( )的原型如下:
7 ?9 o* B# a! |( `
8 w9 L7 b% T1 ?BOOL WINAPI GetFilterVersion(
8 a1 q3 O6 q' D9 g9 q' IDWORD dwServerFilterVersion; //IN,服务器使用的版本规范 " D' W+ z; e: a9 |) T7 A
DWORD dwFilterVersion; //OUT,过滤器使用的版本规范
, T: ]' Z% N) X: L- f; SCHAR lpszFilterDesc[SF_MAX_FILTER_DESC_LEN+1]; //OUT,对该过滤器的描述字符串 6 i3 ]& q. ^. _6 q; r
DWORD dwFlags //OUT,事件和优先级标志 , [9 f* [) A8 p& y
);
& |2 C* X; s. x
; {% v  W8 u1 d* }. J+ v事件和优先级标志dwFlasg的取值在MSDN中有详细解释,其中包括该Filter被调用的优先级,一般应使用默认的低优先级,否则可能会对系统的性能造成很大影响。
5 ]  U/ Z/ E+ v* {& U3 ^
0 w7 A# t* c& n3 C  X$ m/ _; z3、HttpFilterProc( )是ISAPI Filter主要的入口函数,它根据当前的事件的不同作出不同的处理。服务器通过如下的参数块和Filter进行交互,这个参数块的作用和ISA中的ECB类似。 0 J; Z6 ]& O# J

+ W, F0 V; t. h) j& Z8 Rtypedef struct _HTTP_FILTER_CONTEXT & ]7 x# f( E, Y6 a' z
{
3 q# c& {' h3 g2 c7 C7 M4 q: M; h8 T1 P
DWORD cbSize; //IN,本参数块的大小
1 f" d0 x& r' }; s# v+ m% y5 vDWORD Revision; //IN
# ]' h, j3 }0 {: X6 ~* g" b9 dPVOID ServerContext; //IN,由server使用本参数
4 {2 l9 A! Q  w1 u* {! Y6 }DWORD ulReserved; //IN,由server使用本参数 8 t* J: k' a' u/ w0 x, e
BOOL fIsSecurePort; //IN,事件是否发生在安全端口上
, j* C* t+ r9 bPVOID pFilterContext; //IN/OUT,与本次请求相关的上下文 & K- G. _( b/ M$ x! t
7 ^: h8 J, j& f" L  N! j
//回调函数,取得关于服务器和本次连接的信息
4 {+ I: K# x  X8 eBOOL (WINAPI * GetServerVariable) (
1 y+ ~, p4 v* l1 l9 G2 Vstruct _HTTP_FILTER_CONTEXT * pfc, & K' E1 ?, z1 U8 M0 K6 S
LPSTR lpszVariableName, $ W; F: r* E* k7 M) T
LPVOID lpvBuffer, - v! F2 c) b' M/ j0 J/ }
LPDWORD lpdwSize * x0 m" F) B3 E- C$ \* I
);
  K9 d; t% @5 R$ h. _# x$ @$ m
3 \; j9 [1 q* V7 jBOOL (WINAPI * AddResponseHeaders) ( //回调函数,给HTTP响应添加一个标头 , ^+ t& n" y; O6 A. s3 C; t
struct _HTTP_FILTER_CONTEXT * pfc, 8 ~; ?! X1 S& J- V# Z  C/ o
LPSTR lpszHeaders, 7 N9 S' v& s, K2 t6 S: B
DWORD dwReserved 3 X- H# S; e  j2 s
); 1 D6 D3 g" b' X* f/ H

9 j. g7 t) E3 pBOOL (WINAPI * WriteClient) ( //回调函数,将原始数据发送给客户端
3 \4 L! {. q4 @3 j" K( B3 a% hstruct _HTTP_FILTER_CONTEXT * pfc,
0 A5 O5 H) K2 o& y$ J  Q% TLPVOID Buffer,
" N' f! ]9 x0 T) r' f3 L. F5 gLPDWORD lpdwBytes,
1 b! Q4 G) O6 I5 t3 B# g* VDWORD dwReserved
9 G2 v) k- @& L1 R7 T' {); ) b, g0 n" b2 j) L3 @

' H3 C  n9 D, b  ]6 k* [. o& u1 \8 IVOID * (WINAPI * AllocMem) ( //回调函数,分配内存。 8 B3 Z% B' Q# w
struct _HTTP_FILTER_CONTEXT * pfc,   w: d; f+ s5 m
DWORD cbSize,
3 R6 O$ x+ w# ~; V5 JDWORD dwReserved
2 c5 o  C. r2 ]# ^7 j);
5 P: s7 B) `+ p( L' x
8 e3 K9 W- h1 [$ t! E: BBOOL (WINAPI * ServerSupportFunction) ( //回调函数,访问服务器的一般和特定功能
* }- c; D9 Q) p% Xstruct _HTTP_FILTER_CONTEXT * pfc, 6 v! o: T  W+ L
enum SF_REQ_TYPE sfReq,
4 B3 a4 H! ]7 p" F' f* z$ CPVOID pData,
8 R, H$ z9 x! ~" B) m: A0 ~DWORD ul1, 8 Y& d4 O; N- E
DWORD ul2 & K7 V3 n, i% k! R) g
); 7 p2 s, E- k- x( E) N3 R- e7 z" K
$ S" E5 S+ t! @7 s- G
} HTTP_FILTER_CONTEXT, *PHTTP_FILTER_CONTEXT;
5 d* |4 |# i9 y! Z
" `/ n/ J$ a% C8 r/ k* a! ~四、VC++ 6.0中对ISAPI的支持
# A" x' N+ k* ~5 A3 D( g
/ q  f( a3 S2 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实例,每个
+ n+ |/ m+ Y6 D. ^, ~* `CHttpServerContext处理一个客户请求,这样可以处理并发的HTTP请求;CttpFilter和CHttpFilterContext之间的关系与此类似,在每个ISAPI Filter中只能有一个CHttpFilter实例,但是可以有多个CHttpFilterContext来处理并发的事件。CHttpServer和CHttpFilter是独立的类,它们可以共存于一个DLL中,也可以分别在不同的DLL中。 4 m4 _% R+ H& w# I: P$ K

. V2 L" G% i8 z2 y5 V. k6 ~& N一个ISA可以提供多个命令,每个命令对应于CHttpServer(或其子类)的一个成员函数,客户端可以在URL中指定命令名及其参数。在VC++ 6.0中是通过parse map来实现这种对应的。 & }2 i$ D5 q3 k* q( W* N% v/ G

/ D" z. E/ J3 d5 Z; K8 KParse 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为例,该例中有下面这样一个表单:
* t! X+ k+ l, n: j& s5 _1 N8 K" ~% t& K- y- h
<form method=get action="pinball.dll?">
9 T! ^7 S$ }  m: B7 |<input type="hidden" name="MfcISAPICommand" VALUE="GetImage">
; g. [5 R6 P1 \* X! |3 W  I<input type="radio" name="Favorite" value="1" checked> Attack from Mars<br> " y2 @$ {5 M" r0 s* j% i
<input type="radio" name="Favorite" value="2"> Twilight Zone<br> % B+ d  u: r& T  {& [# V
<input type="radio" name="Favorite" value="3"> The Addams Family<br> / j4 K, Q: R& t) E6 u5 r! n/ M1 ]
<input type="radio" name="Favorite" value="4"> Cirqus Voltaire<br>
+ k5 @$ p  @) G# }, b8 ~3 A. v$ m5 n9 O<input type="radio" name="Favorite" value="0"> I don't see it here<br>
7 B% M' N% l4 F1 M. N- k<br>
* D& `( y* l) {/ I3 l<input type="submit" value="Show Me!">
- i# i% a% y& u7 Q</form> ; n0 ]8 k/ ^* t( G8 r

/ R2 a$ o. G, K; m* Q当客户端选中了上面的表单中的“Attack from Mars”这一项并点击了submit按钮后,服务器端 1 q0 d+ p# u7 O# ?4 l, U4 z
最终将得到如下的URL串:
* d& i9 c' f7 N/ T  a% G! x' w  @! |, y
http://www.abc.com/pinball.dll?M ... mage&Favorite=1 % N, T- G0 F) ?" h$ g) D
; ^" \6 k8 w" A2 y8 I
在该URL串中,命令名是GetImage,参数Favorite的值是1,因此pinball.dll中的如下成员函数 ! b9 F+ x- W7 ]$ q  o" _' y
将被调用以处理该请求,其中参数dwChoice对应URL中的参数Favorite: ( }7 Z% K* a0 M4 I2 h0 H% Y

7 A% n/ d' h1 e, Kvoid CPinballExtension::GetImage(CHttpServerContext* pCtxt, long dwChoice); ( P# A- c$ A: P: M/ i6 r" r9 L

: X" b7 f. I2 b7 k; E# C0 a) V9 M而parse map需要按照下面的形式定义:
( y7 s. M  e3 |! l0 H1 S! b
* n2 l8 d, w" Y9 g2 T//CPinballExtension从CHttpServer派生而来 ' k  `; u8 Q1 d5 \2 c; U& Q
BEGIN_PARSE_MAP(CPinballExtension, CHttpServer) % [. ~! s  ~7 S" k

" M$ ~- u) v6 j$ D+ _6 E//GetImage是CPinballExtension的成员函数,且有一个long型的参数即dwChoice 6 u/ c, C, B  Y; N
ON_PARSE_COMMAND(GetImage, CPinballExtension, ITS_I4)
& U/ |4 J" m8 T' t1 o7 q7 {; }" G$ w" o0 u+ z
//该参数在URL中的名字为Favorite
  z$ |6 A" I2 A, SON_PARSE_COMMAND_PARAMS("Favorite") & l# g: M& T" K9 T

+ {* @: c, _( }, c& _8 w: s, REND_PARSE_MAP(CPinballExtension)
! F. j) M/ p2 G/ b- M3 H2 X. M, ~, l) p7 j/ [4 ^! p, X
而对于ISAPI Filter,在VC中可以通过重载CHttpFilter(或其子类)的不同的成员函数来实现对不同事件的处理。可重载的函数如下,每一个成员函数均对应一个或多个事件:
/ e$ k/ o  t& o3 c6 L6 a' Z* \  Q, n5 D9 e8 j: w7 n
OnPreprocHeaders $ M- B, K+ f: x+ S) |- T/ U# f
OnAuthentication 3 G# {  s- y+ k. x1 x- d* ~+ Y
OnUrlMap
) v7 i* @; T1 a. h- h5 yOnSendRawData : `. n* g$ \$ W
OnReadRawData
* _3 B( ]  u8 D* p" ~/ o- t' WOnLog " M1 {+ [7 @3 X; S5 J! t, x
OnEndOfNetSession
4 O( b/ U1 J. @9 W& s
9 B$ c. z7 \% g' ?. I! y: [  vMSDN提供了4个关于ISAPI的编程实例:counter、MFCUCASE、pinball、wwwquote,有兴趣的可看看,本文主要不是介绍编程,所以不再赘述。
' Q, v% w5 L: y* N
. C# C. U8 r9 ^9 x9 Y, g) T参考资料: / B0 ]" |, _- M: ^
3 s7 n" a; N) X
1、MSDN ' i& c+ q& p* w+ M6 U: i: o
2、《精通CGI编程》,丁一强等,清华大学出版社
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2025-9-30 09:05 , Processed in 0.037650 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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