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

IIS的ISAPI接口简介

[复制链接]
发表于 2003-10-13 13:22:17 | 显示全部楼层 |阅读模式
作者:netguy (mailto:netguy@nsfocus.com)
4 n+ C/ @) w/ N) ?: E! J: a' x$ o6 x+ {- I3 b& |
ISAPI(Internet Server Application Programming Interface)作为一种可用来替代CGI的方法,是由微软和Process软件公司联合提出的Web服务器上的API标准。ISAPI与Web服务器结合紧密,功能强大,能够获得大量的信息,因此利用ISAPI可以开发出灵活高效的Web服务器增强程序。由于ISAPI程序与Web服务器的关系,使得ISAPI接口在安全方面有一定的研究价值。本文主要讨论ISAPI在IIS和VC++ 6.0中的实现。
2 y# u! Z, p' f2 h' z2 M$ F( |
9 a1 M4 l  c9 y8 r1 M+ `一、ISAPI接口和CGI接口的不同。: L' \& z7 n% a

% k( i. L7 E/ s/ AISAPI程序和CGI程序完成类似的功能,但是实现方法不同。
8 |: r9 p7 C7 k; ~' F$ v9 }6 G* ^3 L
1、ISAPI程序以DLL形式被Web服务器加载到自己的进程空间中,因此和服务器共用同一个地址空间,且在没有客户请求时可以将其从内存中卸载;而对客户端发来的每个对CGI程序的请求则需要服务器为它单独启动一个进程,这需要耗费大量的时间和内存。当并发的请求数目很大时,使用CGI在效率上不如ISAPI。
* i1 @, k  W8 H: o+ b# W" J9 b: `" U
2、CGI程序通过环境块和标准输入输出与Web服务器进行通信,而ISAPI程序与服务器结合得更为紧密,与服务器共享同一个进程上下文,主要通过一个参数块与服务器进行交互,可以从服务器那里获得关于当前HTTP连接的大量信息。
. _/ T  K3 a9 ~
6 `$ h3 b: s" U8 A& u/ c; OISAPI主要分为ISA和ISAPI Filter两部分。ISA方法相对而言要传统一些,利用一些特殊的链接,指向服务器的作业,供程序开发人员设计一些扩展功能;而ISAPI过滤器则倾向于构造服务器直接调用的模块,提供一种无缝链接部件用于监测直接来自于服务器的HTTP请求。
8 n2 D. j; J0 L9 V& P- O6 D" q  D9 r0 w  A  O: C1 B
  ~1 [$ [- N# M9 V- C% G) D( r
二、ISA0 ^$ Q/ L9 L6 X3 ~/ c* I

; F. \9 E* F) l0 V/ uISA(Internet Server Application)也可称为ISAPI DLL,其功能和CGI程序的功能直接相对应,使用方法和CGI也类似,由客户端在URL中指定其名称而激活。例如下面的请求将调用服务器的虚拟可执行目录Scripts下的function.dll(ISAPI DLL必须放在服务器的虚拟可执行目录下):
0 }; y- Y! N0 v; H+ b3 Ehttp://www.abc.com/Scripts/function.dll?
  X9 h! e0 Q8 _9 m. R
2 @; i; P9 k  G" H3 _( ^ISA和服务器之间的接口主要有两个:GetExtentionVersion( )和HttpExtentionProc( )。任何ISA都必须在其PE文件头的引出表中定义这两个引出函数,以供Web服务器在适当的时候调用。- ^5 W+ X1 i. P6 A# p/ l& O

, G3 d# j3 M) N% l" i) Z- d3 F' n1、当服务器刚加载ISA时,它会调用ISA提供的GetExtentionVersion( )来获得该ISA所需要的服务器版本,并与自己的版本相比较,以保证版本兼容。函数原型如下:, V7 b4 N6 i" p( M, Y% H2 l
, N6 a7 B' O' e: y. [( r, P
BOOL WINAPI GetExtentionVersion(HSE_VERSION_INFO *version);. X$ D, c! h+ j1 k6 b# r! o
typedef struct _HSE_VERSION_INFO
$ [% \) c$ R2 U% U3 N{- r1 q7 u0 m8 B! O/ U
DWORD dwExtensionVersion; //版本号
% s( n  q3 l: n- M' gCHAR lpszExtensionDesc[HSE_MAX_EXT_DLL_NAME_LEN]; //关于ISA的描述字符串& w2 f) J1 d5 B
} HSE_VERSION_INFO, *LPHSE_VERSION_INFO;
/ o5 Z5 K, f/ y2 Y
1 T( E- {& \0 Q4 Q4 k. T2、ISA的真正入口是HttpExtentionProc( ),它相当于普通C程序的main( )函数,在这个函数中根据不同的客户请求作不同的处理。服务器和HttpExtentionProc( )之间是通过扩展控制块(Extention Control Block)来进行通信的,即ECB中存放入口参数和出口参数,包括服务器提供的几个回调函数的入口地址。函数原型如下:8 L! F0 A" ~! K, q. {  J8 X7 _+ E
  b! D5 O, c* }
DWORD HttpExtensionProc( EXTENSION_CONTROL_BLOCK *pECB );
8 |+ k! u$ ^6 ?3 ^7 E8 F; S# R
( e9 D+ w2 p$ K; EECB的结构定义如下(IN表示入口参数,OUT表示出口参数):( a7 a$ W" `& L( w. X

6 }0 O" q0 m7 {8 ~typedef struct _EXTENSION_CONTROL_BLOCK . P7 j, B' Z# j/ C
{
, j' w6 a- \5 g* p6 ]$ q; dDWORD cbSize; //IN,本结构的大小,只读
7 h8 t+ K( q- R1 J: {$ CDWORD dwVersion //IN,版本号,高16位为主版本号,低16位为次版本号# x7 N  X' l( G
HCONN ConnID; //IN,连接句柄,由服务器分配,ISA只能读取该值
, h& Z8 a* O: yDWORD dwHttpStatusCode; //OUT,当前完成的事务状态. v9 }6 ^! b; d3 U$ G  e9 Y- ^
CHAR lpszLogData[HSE_LOG_BUFFER_LEN]; //OUT,需要写入到日志文件中的内容4 Q5 m0 d8 N) D& H
LPSTR lpszMethod; //IN,等价于CGI的环境变量REQUEST_METHOD% }6 s! j! i. ^" o6 l
LPSTR lpszQueryString; //IN,等价于环境变量QUERY_STRING
3 H  L' S% Z& v) l2 k) P+ ELPSTR lpszPathInfo; //IN,等价于环境变量PATH_INFO
+ U) p; z: q8 G9 Q% MLPSTR lpszPathTranslated; //IN,等价于环境变量PATH_TRANSLATED
3 L! r" _( M" P% F/ zDWORD cbTotalBytes; //IN,等价于环境变量CONTENT_LENGTH9 O1 Z9 E. c3 U% K$ u
DWORD cbAvailable; //IN,缓冲区中的可用字节数/ `  `  \2 A& \! r3 A+ }
LPBYTE lpbData; //IN,缓冲区指针,指向客户端发来的数据
0 I( O5 S+ A, C% d, n$ D8 s! ?( k# PLPSTR lpszContentType; //IN,等价于环境变量CONTENT_TYPE
; F6 {$ N3 T" p8 A6 O9 ?% \8 D3 |1 M5 R" J, L# n
//回调函数,用于返回服务器的连接信息或特定的服务器详细情况9 ^* B2 X8 z- G( U. w3 A
BOOL ( WINAPI * GetServerVariable ) : F# B. F2 h: t& x0 @# |
( HCONN hConn,+ _6 l' z, y$ N: d7 x$ d
LPSTR lpszVariableName,, O* h& q! m# N) M4 E, c2 n: _% I  s
LPVOID lpvBuffer,
6 F0 B- g1 n* F5 W: k. [; BLPDWORD lpdwSize );
$ m5 |  ?1 m* V" a0 e% G9 Z. b( a1 W7 v5 l5 N6 j5 ]
BOOL ( WINAPI * WriteClient ) //回调函数,从客户端的HTTP请求中读取数据
0 X, f, E3 x. C* X1 }( HCONN ConnID,5 B( j+ k# _8 |1 T9 ]
LPVOID Buffer,
) Y5 t, g  m/ \/ rLPDWORD lpdwBytes,
4 l' _% c" v* d% e6 {9 A, `DWORD dwReserved );2 u3 y- G3 C! x- u+ \+ x* V# B2 q

, ]$ I* @  i6 ~+ Q+ c1 D- WBOOL ( WINAPI * ReadClient ) //回调函数,向客户端发送数据
( C  Y; A+ n2 H1 V( HCONN ConnID,( W% \; {) ], w3 C
LPVOID lpvBuffer,
# U" O9 `, Z: O$ v- c" qLPDWORD lpdwSize );8 J$ _4 n. Z6 J; R& z8 p1 X
% x, ?" W4 i6 P* ?. e% i; P
BOOL ( WINAPI * ServerSupportFunction ) //回调函数,访问服务器的一般和特定功能4 j" r" e, S  F6 Z" o
( HCONN hConn,
( t0 a: q( c$ d8 xDWORD dwHSERRequest,
9 K, t6 _& |) }3 ]LPVOID lpvBuffer,
, M3 U" ?. v% L$ D! {* MLPDWORD lpdwSize,% S" C4 A7 ?. R  \, H, ]/ h* Z! i: D
LPDWORD lpdwDataType );
  _( }7 q' _2 N& O7 S
7 r4 Y+ t$ D) K" ^' H} EXTENSION_CONTROL_BLOCK, *LPEXTENSION_CONTROL_BLOCK;3 i; \, A* P" c; b0 O

4 V) {9 J& f  M0 R' u+ W, w在上述ECB中,服务器不但提供了当前HTTP连接的句柄和一些变量,而且提供了4个回调函数给ISA调用,从而使ISA可以获得更详尽的信息。1 v+ \6 D1 C" @  V' g5 y

) Q+ P( A; F. q# E三、ISAPI Filter
. `" c6 o7 A0 x8 N+ f+ V  z3 l/ t3 o$ n' o: D# x/ @* u4 c0 N" ]
ISAPI Filter位于服务器和客户端之间,能够对服务器和客户端之间的通信进行预处理和后处理,比如对通信进行加密/解密、提供对客户进行身份验证的新方法、提供自定义的日志记录等,在CGI中没有与ISAPI Filter直接相对应的部分。
! e& C9 z0 g* u5 r! J3 H6 W6 e) U2 g% [
ISAPI Filter与服务器之间的接口有两个:GetFilterVersion( )和HttpFilterProc( )。任何. p0 C! q) j8 _& `+ B" x; Y
ISAPI Filter都必须引出这两个函数以供服务器调用。! g9 |& L: I6 E
7 ^/ a4 w; k8 G8 Z, F# ?
1、在注册表的如下键值中存放着所有ISAPI Filter的文件名,IIS服务器启动时从该键值中获得
$ A3 `  V+ ?1 _, H) C. Y9 XFilter的文件名并加载它们。$ Q! l  s% a* u6 q
+ y) ?7 m' g0 X6 e
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/W3SVC/Parameters/FilterDLL$ s, R( \' x( G" ?) u" q( k
& m% W5 p7 ~. R& k, N
2、然后服务器调用每个Filter提供的GetFilterVersion( )函数,获得版本号以及该Filter希望处理的事件,即ISAPI Filter通过引出GetFilterVersion( )函数来告知服务器自己希望处理什么类型的事件,因为ISAPI Filter是通过事件来激活的,当满足条件的事件到达时,服务器就会调用Filter引出的主函数HttpFilterProc( )对该事件进行处理。GetFilterVersion( )的原型如下:: }) S- v$ X- A0 e( D" P4 t  ~

" }. L0 H8 d5 t) O# Q2 D# ZBOOL WINAPI GetFilterVersion( # I5 V$ d- a- D( ~, b6 M
DWORD dwServerFilterVersion; //IN,服务器使用的版本规范
7 {+ `! J$ {% `. E1 B+ UDWORD dwFilterVersion; //OUT,过滤器使用的版本规范* g' h4 [1 z) u5 M" g
CHAR lpszFilterDesc[SF_MAX_FILTER_DESC_LEN+1]; //OUT,对该过滤器的描述字符串
% M5 ~8 [( n3 l; O$ y6 |DWORD dwFlags //OUT,事件和优先级标志, F8 `% ]3 |% p. l: T
);
' G5 l3 \; h2 _5 T9 a* m# C/ V
/ A  C3 G' n5 V事件和优先级标志dwFlasg的取值在MSDN中有详细解释,其中包括该Filter被调用的优先级,一般应使用默认的低优先级,否则可能会对系统的性能造成很大影响。. o3 U" T, u4 i8 N# D1 q$ ^

8 `4 U% u- f4 v6 \0 d1 u9 Q3、HttpFilterProc( )是ISAPI Filter主要的入口函数,它根据当前的事件的不同作出不同的处理。服务器通过如下的参数块和Filter进行交互,这个参数块的作用和ISA中的ECB类似。
+ ?$ i9 f+ [5 y2 \3 J. S0 |$ H$ Q2 z- f
typedef struct _HTTP_FILTER_CONTEXT
( K8 u! ~7 F3 t0 @! Z1 B{
  h! l, ?+ \; W" u9 S  U8 L/ k8 S0 [# S3 G, y2 H
DWORD cbSize; //IN,本参数块的大小, a( e$ w) l. r" |! e
DWORD Revision; //IN+ u) ~2 ^$ T9 n7 P/ r$ L, L' h
PVOID ServerContext; //IN,由server使用本参数, ?1 p# `2 \1 Q! {: G, X
DWORD ulReserved; //IN,由server使用本参数3 D- A, s4 ?5 v7 S$ Z- w0 r
BOOL fIsSecurePort; //IN,事件是否发生在安全端口上
! e5 A3 w# ], JPVOID pFilterContext; //IN/OUT,与本次请求相关的上下文
9 @1 b+ q) a2 S+ E0 e, e6 N( Z6 z& {  u# G3 ]/ c5 X1 p% n
//回调函数,取得关于服务器和本次连接的信息
+ f( {2 _8 d4 A9 B6 [+ VBOOL (WINAPI * GetServerVariable) ( $ V/ S$ v6 n, O! N- X8 P
struct _HTTP_FILTER_CONTEXT * pfc,% l+ ]) K4 `: U. l& d' a
LPSTR lpszVariableName,9 ~  |* |5 Q0 E4 q0 G7 a, o$ X
LPVOID lpvBuffer,6 B) {- s# @( M7 g1 Y
LPDWORD lpdwSize  s0 X* F) L  Y
); ; N  Z( R- f6 N( c- P/ v0 \

/ C" ~7 g8 M  zBOOL (WINAPI * AddResponseHeaders) ( //回调函数,给HTTP响应添加一个标头3 d, w7 c0 y0 k# |+ `
struct _HTTP_FILTER_CONTEXT * pfc,
! }* H2 f: Y* zLPSTR lpszHeaders,, [' L- m8 b" Z& i$ |7 p
DWORD dwReserved
2 G# E( y6 {0 A+ O% Z5 d);
% h' y# d$ w2 {+ I+ H9 k6 c" R+ f( s, C+ e4 L
BOOL (WINAPI * WriteClient) ( //回调函数,将原始数据发送给客户端2 C8 W+ w) r; ^# r9 k
struct _HTTP_FILTER_CONTEXT * pfc,
5 A; O  W* e7 k1 r+ Z9 JLPVOID Buffer,
' ~0 i2 B% l2 S  i1 LLPDWORD lpdwBytes,
3 {* b; T3 X3 UDWORD dwReserved
4 {2 e  G& s& P8 X# O);
+ ?/ G/ l( o& H) O/ H* v/ f* J& b
  i' g! t! m6 e& Y- c1 FVOID * (WINAPI * AllocMem) ( //回调函数,分配内存。4 ~1 O2 o( X& Y" u' e# f
struct _HTTP_FILTER_CONTEXT * pfc,( y; f2 x" Q! B+ L3 y% P3 i
DWORD cbSize,/ Y, c3 Z: Q* _) B
DWORD dwReserved1 c" x! [' x* P* F2 r
); 7 ]" m# K# k4 h" K, J' L
' |% w' ~/ d5 n
BOOL (WINAPI * ServerSupportFunction) ( //回调函数,访问服务器的一般和特定功能) v$ i& k! E) P2 }  m! V4 Q; S
struct _HTTP_FILTER_CONTEXT * pfc,
' ]& _/ }5 F3 v9 ?- genum SF_REQ_TYPE sfReq,: _# i3 h* ~6 p! z9 L8 X
PVOID pData,; S1 ]3 e- w! v" \# a0 q  N
DWORD ul1,3 I; j4 k+ i: l( [* T
DWORD ul2+ |4 q6 t% M( h! D# x
); 7 [' N) D# p- c4 g
0 E& T, f' e8 T) W, p2 K, _6 I
} HTTP_FILTER_CONTEXT, *PHTTP_FILTER_CONTEXT;
, a6 @3 O1 Z! W: V5 y- m! |% G8 t. J* H3 M7 ?
四、VC++ 6.0中对ISAPI的支持
, Y% U: i$ y8 A. V# c
; I: }( u8 z/ AVC++ 6.0中定义了5个相关的类以简化ISAPI的编程工作:CHttpServer、CHttpServerContext、CHttpFilter、CHttpFilterContext、CHtmlStream,这5个类都没有父类。其中CHttpServer和CHttpServerContext主要用来编写ISA,CHttpFilter和CHttpFilterContext则用来编写ISAPI Filter,而CHtmlStream则用来操作内存中的HTML文件,为其它的4个类提供服务。CHttpServer在每个ISA中只能有一个实例,一个CHttpServer可以对应多个CHttpServerContext实例,每个
5 {/ X/ H( l  {: |- Z% t: F  cCHttpServerContext处理一个客户请求,这样可以处理并发的HTTP请求;CttpFilter和CHttpFilterContext之间的关系与此类似,在每个ISAPI Filter中只能有一个CHttpFilter实例,但是可以有多个CHttpFilterContext来处理并发的事件。CHttpServer和CHttpFilter是独立的类,它们可以共存于一个DLL中,也可以分别在不同的DLL中。* V' u3 ]. V% B- w/ X) F  P0 _

4 @; U& @9 x8 N5 F- f% _  T一个ISA可以提供多个命令,每个命令对应于CHttpServer(或其子类)的一个成员函数,客户端可以在URL中指定命令名及其参数。在VC++ 6.0中是通过parse map来实现这种对应的。3 @/ x' X  ~6 K& E
; _, `7 Q  G! L! K/ E
Parse 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为例,该例中有下面这样一个表单:4 J# G6 j0 l6 ]( D: ^
' c, x, y1 G1 V: Z6 Q+ ]* W2 p- G
<form method=get action="pinball.dll?">% U  t9 T) m6 r
<input type="hidden" name="MfcISAPICommand" VALUE="GetImage">% k7 f/ l6 ~4 N% {2 q4 Z1 B0 u3 ~
<input type="radio" name="Favorite" value="1" checked> Attack from Mars<br>
7 G( b9 Z3 G6 [: K) V9 |" i$ S<input type="radio" name="Favorite" value="2"> Twilight Zone<br>' Z1 s* D" n7 j% I
<input type="radio" name="Favorite" value="3"> The Addams Family<br>
  ]. P1 F& T, `<input type="radio" name="Favorite" value="4"> Cirqus Voltaire<br>
) ^# n; c8 B+ `# Q& P/ |<input type="radio" name="Favorite" value="0"> I don't see it here<br>* h# S: ?$ C. {2 u" ]. u- D& i; J
<br>
, I1 N1 ?' B( s/ v& v<input type="submit" value="Show Me!">
2 u  X  G+ R8 n5 S. n3 R2 B</form>, o! U6 E+ V# k0 ^; L7 M. F# J2 Y
0 {* }3 e+ |( J2 ?  g& t
当客户端选中了上面的表单中的“Attack from Mars”这一项并点击了submit按钮后,服务器端, x; f7 U& d/ @
最终将得到如下的URL串:6 t2 K! }7 Z% M9 T) _" g% [
) x5 F! Q! O0 z) _. j
http://www.abc.com/pinball.dll?M ... GetImage&Favorite=1
+ y- m; P) `, q1 w
. h5 ]: i. j+ m3 \9 e9 n在该URL串中,命令名是GetImage,参数Favorite的值是1,因此pinball.dll中的如下成员函数" @4 P( A: h1 [
将被调用以处理该请求,其中参数dwChoice对应URL中的参数Favorite:
: G& k. M+ `# L# G! O. x
  U: ~/ }4 P+ x1 ?0 pvoid CPinballExtension::GetImage(CHttpServerContext* pCtxt, long dwChoice);
& ]8 f, [0 e. D: L! S: i- f' _
+ d/ a3 }1 \5 U# K7 ?而parse map需要按照下面的形式定义:/ |6 q8 |# A! I- S  C5 q+ d

% V& [6 y* R) W- ~# C//CPinballExtension从CHttpServer派生而来
9 ~; N5 Q7 @0 M2 r9 g" T( a8 E& rBEGIN_PARSE_MAP(CPinballExtension, CHttpServer)
  c" a( X5 w% Y  B; T$ u: T  V. G% E: H5 ^! q: N
//GetImage是CPinballExtension的成员函数,且有一个long型的参数即dwChoice
0 T) W; f  `' z! l. o3 {ON_PARSE_COMMAND(GetImage, CPinballExtension, ITS_I4) * X; e# q& z6 B7 R+ W5 y2 l

( W9 B% i, g* s//该参数在URL中的名字为Favorite# [( A& t; {: ^5 _8 _" ], t) o8 Y
ON_PARSE_COMMAND_PARAMS("Favorite") 5 q5 y" N6 M4 f$ i9 h

$ m& t1 f+ Z( t. I6 XEND_PARSE_MAP(CPinballExtension)' q- M2 X8 U" U8 T) N

- G. e9 J, l; R+ [- p7 b' J& v; }% B8 B+ y而对于ISAPI Filter,在VC中可以通过重载CHttpFilter(或其子类)的不同的成员函数来实现对不同事件的处理。可重载的函数如下,每一个成员函数均对应一个或多个事件:/ |5 {/ A( N: c6 E

3 Y' r+ b: l, xOnPreprocHeaders
" a+ [4 n, D" VOnAuthentication. J3 }: b7 Z/ m
OnUrlMap
. Y: P0 ?, g4 u$ l4 d; @OnSendRawData
2 u1 e" S3 A" ?0 ~2 p- T/ c  qOnReadRawData1 R% C- ^3 B! r  j
OnLog0 Q; b" T% O, g5 w' e" J
OnEndOfNetSession
' @6 U2 C" K8 y( w6 j+ o
5 ~' T3 S% B$ |. U7 O) ]MSDN提供了4个关于ISAPI的编程实例:counter、MFCUCASE、pinball、wwwquote,有兴趣的可看看,本文主要不是介绍编程,所以不再赘述。* T) f7 c# o! ]1 U) g! c! k
: |% s0 a# u' N: F/ V( x
参考资料:" V7 ^9 x* S# y4 g& h! t& A
* \& a& q5 \7 F% ]7 K
1、MSDN) s" X* V* v% }) R
2、《精通CGI编程》,丁一强等,清华大学出版社
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-6-18 12:17 , Processed in 0.018971 second(s), 14 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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