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

IIS的ISAPI接口简介

[复制链接]
发表于 2003-10-13 13:22:17 | 显示全部楼层 |阅读模式
作者:netguy (mailto:netguy@nsfocus.com)
4 x& j* W' I" [" N5 G5 v
% `. l$ r% O$ G/ NISAPI(Internet Server Application Programming Interface)作为一种可用来替代CGI的方法,是由微软和Process软件公司联合提出的Web服务器上的API标准。ISAPI与Web服务器结合紧密,功能强大,能够获得大量的信息,因此利用ISAPI可以开发出灵活高效的Web服务器增强程序。由于ISAPI程序与Web服务器的关系,使得ISAPI接口在安全方面有一定的研究价值。本文主要讨论ISAPI在IIS和VC++ 6.0中的实现。$ S, I, Z% R- S5 c  v' U

. G1 r# b& Z" q' t) N5 R一、ISAPI接口和CGI接口的不同。. C4 V3 a- }  P$ g

' d9 E2 K* v$ }$ [ISAPI程序和CGI程序完成类似的功能,但是实现方法不同。
3 R+ X1 ^% ?5 M; I9 }1 s  ~$ B% _  c& g/ b- l
1、ISAPI程序以DLL形式被Web服务器加载到自己的进程空间中,因此和服务器共用同一个地址空间,且在没有客户请求时可以将其从内存中卸载;而对客户端发来的每个对CGI程序的请求则需要服务器为它单独启动一个进程,这需要耗费大量的时间和内存。当并发的请求数目很大时,使用CGI在效率上不如ISAPI。
( N3 V' e& y5 w- l; v: I! _
. f3 x0 ?3 M/ S! i2、CGI程序通过环境块和标准输入输出与Web服务器进行通信,而ISAPI程序与服务器结合得更为紧密,与服务器共享同一个进程上下文,主要通过一个参数块与服务器进行交互,可以从服务器那里获得关于当前HTTP连接的大量信息。
. E0 v9 D/ W  S: ]/ k  Y  m! H, ~' T- l
ISAPI主要分为ISA和ISAPI Filter两部分。ISA方法相对而言要传统一些,利用一些特殊的链接,指向服务器的作业,供程序开发人员设计一些扩展功能;而ISAPI过滤器则倾向于构造服务器直接调用的模块,提供一种无缝链接部件用于监测直接来自于服务器的HTTP请求。% i5 h5 n. H" i3 r# T) W9 o
  y: H6 |9 U: y+ }+ m
5 b5 i9 l8 v. V
二、ISA- C, n, g! @* ]( U( m1 ^3 P0 A
- r$ ?/ K5 U+ I! @& U
ISA(Internet Server Application)也可称为ISAPI DLL,其功能和CGI程序的功能直接相对应,使用方法和CGI也类似,由客户端在URL中指定其名称而激活。例如下面的请求将调用服务器的虚拟可执行目录Scripts下的function.dll(ISAPI DLL必须放在服务器的虚拟可执行目录下):
9 \7 e8 S% [, P0 F# Y5 jhttp://www.abc.com/Scripts/function.dll?! i) a$ ~# G5 o( w6 j6 v
* I! M/ _: q+ ]6 a
ISA和服务器之间的接口主要有两个:GetExtentionVersion( )和HttpExtentionProc( )。任何ISA都必须在其PE文件头的引出表中定义这两个引出函数,以供Web服务器在适当的时候调用。( k- E5 l1 N2 C2 Z9 y6 s

. y6 X& h. l' v2 A  G1、当服务器刚加载ISA时,它会调用ISA提供的GetExtentionVersion( )来获得该ISA所需要的服务器版本,并与自己的版本相比较,以保证版本兼容。函数原型如下:
/ q. A( s# ^, b! M9 L8 a& M# z1 Q- a: J2 K2 {! x
BOOL WINAPI GetExtentionVersion(HSE_VERSION_INFO *version);
. T, Z% M( s5 _/ L3 R  ytypedef struct _HSE_VERSION_INFO
; D/ N4 \$ j2 }( w! i' T{
$ \* T. X$ P  p! O# M* k! K8 `DWORD dwExtensionVersion; //版本号
5 D  [8 L* n+ v0 A6 dCHAR lpszExtensionDesc[HSE_MAX_EXT_DLL_NAME_LEN]; //关于ISA的描述字符串
& i% ?" f% I: i" N: h6 P} HSE_VERSION_INFO, *LPHSE_VERSION_INFO;
6 x! f5 |" c: l) S/ V' U+ }: c# X/ N& Q' ^* K7 S) i' s. c* I
2、ISA的真正入口是HttpExtentionProc( ),它相当于普通C程序的main( )函数,在这个函数中根据不同的客户请求作不同的处理。服务器和HttpExtentionProc( )之间是通过扩展控制块(Extention Control Block)来进行通信的,即ECB中存放入口参数和出口参数,包括服务器提供的几个回调函数的入口地址。函数原型如下:* r. Z; ]% v) J  i& n9 o

, Q. d! N9 V" A! ?/ d4 V- u! B- k0 ^DWORD HttpExtensionProc( EXTENSION_CONTROL_BLOCK *pECB );
! d8 H' m) P$ @" _: r6 `3 \: @5 t$ e' P; \3 P
ECB的结构定义如下(IN表示入口参数,OUT表示出口参数):5 o$ _6 _( e' L. F4 b* f
3 Q% e9 ?: J* L% w2 [' T
typedef struct _EXTENSION_CONTROL_BLOCK ( K. x: L* |. v- v  v
{& t5 I3 z9 n/ |7 J* y( T, l
DWORD cbSize; //IN,本结构的大小,只读3 {& v( G# Z4 I2 X+ P% Y/ j
DWORD dwVersion //IN,版本号,高16位为主版本号,低16位为次版本号0 q6 [. q( f; a* c
HCONN ConnID; //IN,连接句柄,由服务器分配,ISA只能读取该值- E. W& m/ R' y' I9 k% t
DWORD dwHttpStatusCode; //OUT,当前完成的事务状态
- \' M& `0 P; @/ PCHAR lpszLogData[HSE_LOG_BUFFER_LEN]; //OUT,需要写入到日志文件中的内容
  G" Q* _1 A% `+ ILPSTR lpszMethod; //IN,等价于CGI的环境变量REQUEST_METHOD0 E. q# c. Q: F* f
LPSTR lpszQueryString; //IN,等价于环境变量QUERY_STRING* J. p9 h+ k+ U& C
LPSTR lpszPathInfo; //IN,等价于环境变量PATH_INFO4 \3 U! x+ H. q6 i7 n
LPSTR lpszPathTranslated; //IN,等价于环境变量PATH_TRANSLATED- v' v' b- P: {( k; K9 Q
DWORD cbTotalBytes; //IN,等价于环境变量CONTENT_LENGTH
! O$ z; q& S/ K. k, l, UDWORD cbAvailable; //IN,缓冲区中的可用字节数. p1 U* ~* v9 |2 c. T- T
LPBYTE lpbData; //IN,缓冲区指针,指向客户端发来的数据8 |! V3 a! G8 @3 B
LPSTR lpszContentType; //IN,等价于环境变量CONTENT_TYPE/ i* K! ^2 z* o! ?; ?4 a
6 w. A: R) i/ i. \" I$ l/ q
//回调函数,用于返回服务器的连接信息或特定的服务器详细情况
  ^% E# G( [8 q. e4 U6 rBOOL ( WINAPI * GetServerVariable ) 3 V8 e6 Z$ _: ]9 F& K7 Q
( HCONN hConn,$ o' J/ l/ H& H) @! [
LPSTR lpszVariableName,
' Z' u: Y7 [' e& `; m4 gLPVOID lpvBuffer,
. g# J' c/ |3 s. {, yLPDWORD lpdwSize );" w! ~' b, d" r! [
+ X5 A* p5 K4 J
BOOL ( WINAPI * WriteClient ) //回调函数,从客户端的HTTP请求中读取数据: C# F( \1 x  E* ~& q# S) F0 ^
( HCONN ConnID,3 c' x; i6 {3 ~7 O, n; f
LPVOID Buffer,
/ b. z1 O) w3 w# o# H9 SLPDWORD lpdwBytes,0 m8 R+ l+ E6 {4 `$ w* L* H
DWORD dwReserved );" Z! @( B$ d, P& Z9 u

0 ^7 y* `" X8 h2 N' TBOOL ( WINAPI * ReadClient ) //回调函数,向客户端发送数据6 i9 A, U1 T3 V' `. {5 [! k1 ^% M! D
( HCONN ConnID,. d( G. @& }  X9 A7 F, e* Q* r" M
LPVOID lpvBuffer,
1 G* r# s: `% b# nLPDWORD lpdwSize );
; |% m/ Z# w5 e& r$ Y: n3 g
7 l+ |8 z1 z, N3 S, CBOOL ( WINAPI * ServerSupportFunction ) //回调函数,访问服务器的一般和特定功能
: s3 U( a; B3 W5 @: R( HCONN hConn,3 T9 h. V' h" w
DWORD dwHSERRequest,0 c; [8 z% y: z+ J' f4 q& c9 ^
LPVOID lpvBuffer,  L. u( ^0 l8 }7 r0 n! N
LPDWORD lpdwSize,- i1 a$ R* w! S1 l# i) M# |
LPDWORD lpdwDataType );
9 H) ~* ^7 u9 }0 L& ]; H8 B3 o8 I. I: n- `& w% ?
} EXTENSION_CONTROL_BLOCK, *LPEXTENSION_CONTROL_BLOCK;; P! s, L! A* E; m# T3 B

5 \( z: h7 C) J在上述ECB中,服务器不但提供了当前HTTP连接的句柄和一些变量,而且提供了4个回调函数给ISA调用,从而使ISA可以获得更详尽的信息。6 F2 O/ O/ P& m7 s8 a" {  x
: c" c( L* q% s9 k+ a* Y
三、ISAPI Filter) q: Y8 A* h/ g

7 I8 O  R7 l  L' E8 S( S# pISAPI Filter位于服务器和客户端之间,能够对服务器和客户端之间的通信进行预处理和后处理,比如对通信进行加密/解密、提供对客户进行身份验证的新方法、提供自定义的日志记录等,在CGI中没有与ISAPI Filter直接相对应的部分。
7 \% k) O4 Z& [2 g& ~& l' Q. w: A& }+ t) V7 ^2 M
ISAPI Filter与服务器之间的接口有两个:GetFilterVersion( )和HttpFilterProc( )。任何
, ^' w. m6 ^3 B3 P6 gISAPI Filter都必须引出这两个函数以供服务器调用。1 B# Z' K# F( E

! [2 [4 B/ J; G, M& l- a8 }1、在注册表的如下键值中存放着所有ISAPI Filter的文件名,IIS服务器启动时从该键值中获得
7 `7 d! R& H2 }5 s& X$ PFilter的文件名并加载它们。) M5 T0 ?- x# {. r9 d/ T
* x6 _- Y' i9 b( p9 c
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/W3SVC/Parameters/FilterDLL
$ s/ n! q8 Q' j$ e' h& K$ a: k( v: C9 R# I1 ]8 @. A
2、然后服务器调用每个Filter提供的GetFilterVersion( )函数,获得版本号以及该Filter希望处理的事件,即ISAPI Filter通过引出GetFilterVersion( )函数来告知服务器自己希望处理什么类型的事件,因为ISAPI Filter是通过事件来激活的,当满足条件的事件到达时,服务器就会调用Filter引出的主函数HttpFilterProc( )对该事件进行处理。GetFilterVersion( )的原型如下:
7 r$ d# l: W0 R# Z2 |0 u' h! v7 Z9 t$ @; A' U2 W/ ]
BOOL WINAPI GetFilterVersion( 2 V% r( r' [) V5 L: }
DWORD dwServerFilterVersion; //IN,服务器使用的版本规范
/ W' g2 y& i% Z3 ~7 e6 sDWORD dwFilterVersion; //OUT,过滤器使用的版本规范
1 u! x( W' \. o0 b& d+ OCHAR lpszFilterDesc[SF_MAX_FILTER_DESC_LEN+1]; //OUT,对该过滤器的描述字符串! X9 |' D# U( e- @. W# Y% n
DWORD dwFlags //OUT,事件和优先级标志
$ B, k6 x# g; t; T);; R2 i9 l4 n- `- t, E0 g! o
: y. L& t  Z( l' z* k4 W. o
事件和优先级标志dwFlasg的取值在MSDN中有详细解释,其中包括该Filter被调用的优先级,一般应使用默认的低优先级,否则可能会对系统的性能造成很大影响。1 B/ z# W2 Q* A, w3 W  l, r
( Q) x6 [  r- P4 X- B( R
3、HttpFilterProc( )是ISAPI Filter主要的入口函数,它根据当前的事件的不同作出不同的处理。服务器通过如下的参数块和Filter进行交互,这个参数块的作用和ISA中的ECB类似。
: p1 h' y) k$ g0 E9 `! P& K! N' c8 w" V* U9 H. a
typedef struct _HTTP_FILTER_CONTEXT! _3 D1 Y0 `% p5 S5 j4 `  t$ j! F
{: t6 H" i1 |3 B- q) r0 ?3 u# _
" I. j# W. y- O" O9 _4 i  V
DWORD cbSize; //IN,本参数块的大小$ l6 T% i2 ~& r2 ^3 W
DWORD Revision; //IN
. `' @: m2 N0 ]0 R' i* rPVOID ServerContext; //IN,由server使用本参数0 s# Z( E) N5 P' K% Y0 c) j
DWORD ulReserved; //IN,由server使用本参数% q7 {" w, m0 \, D# x
BOOL fIsSecurePort; //IN,事件是否发生在安全端口上9 x" O; t! Q" C5 _9 x& J
PVOID pFilterContext; //IN/OUT,与本次请求相关的上下文7 k! V8 b" @7 v. D
6 ~' t; ]$ ~( R* g- P+ u
//回调函数,取得关于服务器和本次连接的信息; r0 e3 l2 a  W6 H
BOOL (WINAPI * GetServerVariable) (
) b/ Z' X* s% s- istruct _HTTP_FILTER_CONTEXT * pfc,$ x- Q" i" F; o0 d! W- \& P# h
LPSTR lpszVariableName,
& v# J# Y0 e! p+ Z8 Y& f: k/ NLPVOID lpvBuffer,6 i# Y6 N" ~$ U$ o$ E9 k4 R. \
LPDWORD lpdwSize, t1 T* f& {+ b7 O  T! v$ _
);
% ]- f; D/ ?. L1 U; p
1 V9 C+ U" V) B( w, X4 UBOOL (WINAPI * AddResponseHeaders) ( //回调函数,给HTTP响应添加一个标头
: \0 o: r2 N/ N) c! }6 wstruct _HTTP_FILTER_CONTEXT * pfc,/ W' B1 y4 R1 Z* G
LPSTR lpszHeaders,
: x5 ]' `% P9 Y  r4 b6 M$ bDWORD dwReserved% y% P2 f! e8 v. }
); + v  a* o( L+ s( c+ J" o) Z# t1 A9 @
6 }/ I* u6 y/ m, |, \
BOOL (WINAPI * WriteClient) ( //回调函数,将原始数据发送给客户端
) `& U* u- s2 Ystruct _HTTP_FILTER_CONTEXT * pfc,! d: K2 h% H, l( V
LPVOID Buffer,
  X; }# f3 |3 X7 T" ELPDWORD lpdwBytes,
1 m8 I0 Y/ \% [6 J& O, jDWORD dwReserved+ e7 J( R) J! a  h! K+ G6 r
); ( C5 r6 A- R, y

9 f8 k# N1 N6 ^5 E1 VVOID * (WINAPI * AllocMem) ( //回调函数,分配内存。
: q% R1 I- o5 C# \7 estruct _HTTP_FILTER_CONTEXT * pfc,
) Y5 q- K. p; K9 dDWORD cbSize,+ t, [; G9 L, m3 T
DWORD dwReserved6 v! j. y" p" T0 l6 C7 ~. u
); 2 I' r: r8 f3 h7 v
5 B2 P$ b0 P: M$ r) H( t
BOOL (WINAPI * ServerSupportFunction) ( //回调函数,访问服务器的一般和特定功能
. f/ a0 E5 z' h* J2 h' pstruct _HTTP_FILTER_CONTEXT * pfc,; n  u* O2 p: ^2 Y9 m6 D  }4 a
enum SF_REQ_TYPE sfReq,4 ^# P, V4 B+ X$ W
PVOID pData,% q% `6 i. M6 t8 \. \$ v, s
DWORD ul1,
& ~" V8 X4 _- H% }. p! Z. s7 WDWORD ul2
3 i) {$ H4 K! S; v2 ]);
- ?" @. Z% K, z7 y4 \2 O8 a6 f& B
} HTTP_FILTER_CONTEXT, *PHTTP_FILTER_CONTEXT;& H7 ]7 ^1 D" g+ x: M! g
3 }6 [  y0 E6 G
四、VC++ 6.0中对ISAPI的支持
( N8 K, U6 w- a: e1 f8 }4 q0 r$ G, q, @
VC++ 6.0中定义了5个相关的类以简化ISAPI的编程工作:CHttpServer、CHttpServerContext、CHttpFilter、CHttpFilterContext、CHtmlStream,这5个类都没有父类。其中CHttpServer和CHttpServerContext主要用来编写ISA,CHttpFilter和CHttpFilterContext则用来编写ISAPI Filter,而CHtmlStream则用来操作内存中的HTML文件,为其它的4个类提供服务。CHttpServer在每个ISA中只能有一个实例,一个CHttpServer可以对应多个CHttpServerContext实例,每个+ k/ H& W: x3 X+ c9 S  A% n5 N" Y
CHttpServerContext处理一个客户请求,这样可以处理并发的HTTP请求;CttpFilter和CHttpFilterContext之间的关系与此类似,在每个ISAPI Filter中只能有一个CHttpFilter实例,但是可以有多个CHttpFilterContext来处理并发的事件。CHttpServer和CHttpFilter是独立的类,它们可以共存于一个DLL中,也可以分别在不同的DLL中。7 ~  P7 W' t& f6 h8 o
  K: W5 h/ m" q" H: X4 E" b) G
一个ISA可以提供多个命令,每个命令对应于CHttpServer(或其子类)的一个成员函数,客户端可以在URL中指定命令名及其参数。在VC++ 6.0中是通过parse map来实现这种对应的。2 u& |/ `, a" D9 r% l$ o

6 H6 x3 u; Z0 cParse 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为例,该例中有下面这样一个表单:
% }0 Y4 c& a' f# h# w
$ M* d$ _; w6 r0 ~, i" q/ ~<form method=get action="pinball.dll?">; X/ r7 ?1 j3 F9 K
<input type="hidden" name="MfcISAPICommand" VALUE="GetImage">6 d7 ~# x1 m/ v( Z$ i' P
<input type="radio" name="Favorite" value="1" checked> Attack from Mars<br>
' {% R" a2 O" h8 g% A<input type="radio" name="Favorite" value="2"> Twilight Zone<br>
' A6 A( C. \3 _2 e( T5 V<input type="radio" name="Favorite" value="3"> The Addams Family<br>
+ g/ _, d& Y! b) _+ t<input type="radio" name="Favorite" value="4"> Cirqus Voltaire<br>
4 A0 |, p* u* u" @<input type="radio" name="Favorite" value="0"> I don't see it here<br>, e) B: `$ x# o+ g
<br>
9 h4 ?) P6 U/ m0 a& J7 M<input type="submit" value="Show Me!">
& F+ U( _* n. L</form>
# G1 V8 V, @6 T! }: G! X3 k" q  y  w
当客户端选中了上面的表单中的“Attack from Mars”这一项并点击了submit按钮后,服务器端% f+ i3 N+ I6 W
最终将得到如下的URL串:
/ D3 h0 \" o. w: `: l: }: P' i* Z  d& }% b3 y, ~. j7 S
http://www.abc.com/pinball.dll?M ... GetImage&Favorite=1
' ^% J1 w4 n3 R/ ]2 Y3 M( i9 f' E8 I6 j
在该URL串中,命令名是GetImage,参数Favorite的值是1,因此pinball.dll中的如下成员函数
, ^. u2 v8 S  H将被调用以处理该请求,其中参数dwChoice对应URL中的参数Favorite:
  Z, j- j$ Y( v5 h4 E: F. C9 f( c! _1 k. o9 U& Y% \& h: c6 g  N% {
void CPinballExtension::GetImage(CHttpServerContext* pCtxt, long dwChoice);
7 E6 [( d' l& d0 t
0 G" `% _  k( P% J0 W' z* o7 M7 p而parse map需要按照下面的形式定义:
, j6 t. W4 i% c/ `- s8 s/ v+ q" [- R8 n) D1 `1 I0 u
//CPinballExtension从CHttpServer派生而来- n+ N9 ]0 l4 b) f) c/ ^5 J. t8 O
BEGIN_PARSE_MAP(CPinballExtension, CHttpServer) 5 j# o9 N: p7 N/ [- ?. `

% a0 T& M( M% s2 a. S//GetImage是CPinballExtension的成员函数,且有一个long型的参数即dwChoice" b8 m4 }/ G. K. i- b- y6 S
ON_PARSE_COMMAND(GetImage, CPinballExtension, ITS_I4)
' o; b9 E! e6 b  L8 @0 ?  ~% F( T2 Q/ D2 e
//该参数在URL中的名字为Favorite
8 O" l4 B/ q  B8 LON_PARSE_COMMAND_PARAMS("Favorite")
6 B& Z" n* U' \. G" R" m8 H
6 z+ e; W! y) I  }END_PARSE_MAP(CPinballExtension)
# o  J) L2 [; X# I- g. L: f3 I0 w3 y0 f' l# r
而对于ISAPI Filter,在VC中可以通过重载CHttpFilter(或其子类)的不同的成员函数来实现对不同事件的处理。可重载的函数如下,每一个成员函数均对应一个或多个事件:
! ?1 L3 Q$ K9 x* W! L" r
' c7 [  d! a% A6 A5 m3 I( m! _OnPreprocHeaders) G; {0 F, v$ N$ _2 g8 m2 h
OnAuthentication6 i; `0 `' G% D% z. v6 W
OnUrlMap
7 W+ z: x( c( W( o- cOnSendRawData4 d6 E+ z* ]5 |( g
OnReadRawData
0 C( N0 s4 n$ H* yOnLog
% H' w% r) X" Y4 C: M" xOnEndOfNetSession) c" p2 j$ J* n9 L
+ E" k1 A& I5 Q
MSDN提供了4个关于ISAPI的编程实例:counter、MFCUCASE、pinball、wwwquote,有兴趣的可看看,本文主要不是介绍编程,所以不再赘述。+ Q7 H* w2 w. R; x  N
( u: w3 V  f  L1 q. N4 q& d
参考资料:7 ]1 z# K; }  o1 T$ j/ a# I0 m

( t5 j. t! Q8 C( d# g' z7 D" v1、MSDN5 i5 z3 p& a% J
2、《精通CGI编程》,丁一强等,清华大学出版社
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-5-4 16:51 , Processed in 0.023308 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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