找回密码
 注册
查看: 4533|回复: 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中的实现。
, R5 ~, O" S& L5 I0 i! H3 F( D" _! W
一、ISAPI接口和CGI接口的不同。
' V" w! J9 K) d/ x, V! k7 Y: i6 M; h+ V- n  o) Q" O
ISAPI程序和CGI程序完成类似的功能,但是实现方法不同。 - `' `2 t) i; L, {6 z1 e
7 J/ ^0 k# x9 [0 L- t
1、ISAPI程序以DLL形式被Web服务器加载到自己的进程空间中,因此和服务器共用同一个地址空间,且在没有客户请求时可以将其从内存中卸载;而对客户端发来的每个对CGI程序的请求则需要服务器为它单独启动一个进程,这需要耗费大量的时间和内存。当并发的请求数目很大时,使用CGI在效率上不如ISAPI。 " u/ d; E- U; j! X3 h

, ]- c. d5 t3 \2、CGI程序通过环境块和标准输入输出与Web服务器进行通信,而ISAPI程序与服务器结合得更为紧密,与服务器共享同一个进程上下文,主要通过一个参数块与服务器进行交互,可以从服务器那里获得关于当前HTTP连接的大量信息。
3 \; t* P! \, a7 F; k( S/ o- i" b3 \: B0 }  a9 R* o
ISAPI主要分为ISA和ISAPI Filter两部分。ISA方法相对而言要传统一些,利用一些特殊的链接,指向服务器的作业,供程序开发人员设计一些扩展功能;而ISAPI过滤器则倾向于构造服务器直接调用的模块,提供一种无缝链接部件用于监测直接来自于服务器的HTTP请求。
1 f* i2 T3 }4 k) a0 J3 I; J8 {9 `

! r' ]% R1 r) E& s4 B二、ISA * I) u6 r* h# H, |' o) z
. c8 ?3 s! d& ~' m
ISA(Internet Server Application)也可称为ISAPI DLL,其功能和CGI程序的功能直接相对应,使用方法和CGI也类似,由客户端在URL中指定其名称而激活。例如下面的请求将调用服务器的虚拟可执行目录Scripts下的function.dll(ISAPI DLL必须放在服务器的虚拟可执行目录下):
/ ^( @: ~5 R  \: y& ^1 Whttp://www.abc.com/Scripts/function.dll? " D' P2 y! Z8 c% G
0 c- x+ [; C7 S5 ?' Y( c; g
ISA和服务器之间的接口主要有两个:GetExtentionVersion( )和HttpExtentionProc( )。任何ISA都必须在其PE文件头的引出表中定义这两个引出函数,以供Web服务器在适当的时候调用。 % \$ W& w+ R. D- c7 K

  D2 j& d$ M" j% e8 v1、当服务器刚加载ISA时,它会调用ISA提供的GetExtentionVersion( )来获得该ISA所需要的服务器版本,并与自己的版本相比较,以保证版本兼容。函数原型如下:
& a6 O! c  o" @) K& p# n1 r
  E4 h5 e1 K9 D) B! ZBOOL WINAPI GetExtentionVersion(HSE_VERSION_INFO *version); ; o( j! O9 ]* U) z6 w
typedef struct _HSE_VERSION_INFO / t) \1 C4 u/ e% w$ Z% E3 Y4 R7 q  }
{
$ ^, w: s* e, k2 A3 T: m* n3 E+ LDWORD dwExtensionVersion; //版本号
" ~. \- t) a; A( aCHAR lpszExtensionDesc[HSE_MAX_EXT_DLL_NAME_LEN]; //关于ISA的描述字符串
3 Z8 j8 Q1 r# r- \3 }+ ?} HSE_VERSION_INFO, *LPHSE_VERSION_INFO;
, _. |2 }/ I+ E* r4 B
# |4 }% |0 p# s* M4 l. A$ Q. d2、ISA的真正入口是HttpExtentionProc( ),它相当于普通C程序的main( )函数,在这个函数中根据不同的客户请求作不同的处理。服务器和HttpExtentionProc( )之间是通过扩展控制块(Extention Control Block)来进行通信的,即ECB中存放入口参数和出口参数,包括服务器提供的几个回调函数的入口地址。函数原型如下:   i, g3 s: @" d' i

, g3 Y$ e% T% h: F' l5 eDWORD HttpExtensionProc( EXTENSION_CONTROL_BLOCK *pECB ); 1 q3 A, x2 q) K. S
! S6 [+ O$ U9 C, D, ]6 J- i3 Q
ECB的结构定义如下(IN表示入口参数,OUT表示出口参数): 8 o1 n7 f: N0 V) W

7 X% l/ e$ @" M6 ytypedef struct _EXTENSION_CONTROL_BLOCK
3 Y3 \# Q, {/ {6 k0 g  R" a4 a$ Y  k" ~{ 9 d0 u% p! Y' x
DWORD cbSize; //IN,本结构的大小,只读 + x1 h, n! M1 J; }
DWORD dwVersion //IN,版本号,高16位为主版本号,低16位为次版本号 5 M$ s" {6 q% g! R
HCONN ConnID; //IN,连接句柄,由服务器分配,ISA只能读取该值 1 i: X, `6 g* z' E
DWORD dwHttpStatusCode; //OUT,当前完成的事务状态 % F/ c4 v( m/ |( S5 i
CHAR lpszLogData[HSE_LOG_BUFFER_LEN]; //OUT,需要写入到日志文件中的内容
6 Q. ]" ?' j9 Y- e, Q) ^LPSTR lpszMethod; //IN,等价于CGI的环境变量REQUEST_METHOD
' g/ |  a, t. _( VLPSTR lpszQueryString; //IN,等价于环境变量QUERY_STRING : {+ t2 B0 `! H3 S. J: e# j9 u+ _
LPSTR lpszPathInfo; //IN,等价于环境变量PATH_INFO " i9 b1 T6 j* v: ?  w# x/ I
LPSTR lpszPathTranslated; //IN,等价于环境变量PATH_TRANSLATED
3 O7 S& y8 H5 oDWORD cbTotalBytes; //IN,等价于环境变量CONTENT_LENGTH , v- V1 T8 J7 W& `: [( D( g
DWORD cbAvailable; //IN,缓冲区中的可用字节数 " v. K2 d# ^5 R" ?. I
LPBYTE lpbData; //IN,缓冲区指针,指向客户端发来的数据
( w8 c) k7 [+ r9 b+ K5 oLPSTR lpszContentType; //IN,等价于环境变量CONTENT_TYPE
/ E" k+ @& \, D4 G4 N( N7 Z% p- F' ?: {
//回调函数,用于返回服务器的连接信息或特定的服务器详细情况
) n) K+ u( T( QBOOL ( WINAPI * GetServerVariable ) 4 _: g8 f, j6 t4 |
( HCONN hConn, 2 ?1 r( v+ ^; d: n7 a
LPSTR lpszVariableName,
! e& d5 T+ m& V, x1 C* B- N! ]. o5 H) cLPVOID lpvBuffer,
: Q4 W$ S8 ^/ R' HLPDWORD lpdwSize );
% [- A, w3 G; _9 p% a) ]3 {; W' h( Z$ U+ k; y; J
BOOL ( WINAPI * WriteClient ) //回调函数,从客户端的HTTP请求中读取数据
$ s( ^  `" _0 L( HCONN ConnID,
2 ]% w/ L4 |! i$ d3 ~1 |3 N) CLPVOID Buffer, " {: {. C) I' j/ T& o5 j. I8 [
LPDWORD lpdwBytes,
+ K" f2 ^6 b: |0 X, P' k( S; MDWORD dwReserved ); : R2 r- J- |' {. ]! Z
$ C: E8 ~. n6 m* B$ g3 j, D9 m* a
BOOL ( WINAPI * ReadClient ) //回调函数,向客户端发送数据
0 A: z* s; }9 d! M( HCONN ConnID,
: N7 x, ^' @" q( a( d+ C3 I! V0 pLPVOID lpvBuffer,
: [8 C7 l  J% X; ?LPDWORD lpdwSize ); 8 |. [; |6 K5 C' y! m

; F$ o2 V8 ~) V: ^; o5 G' C; d. NBOOL ( WINAPI * ServerSupportFunction ) //回调函数,访问服务器的一般和特定功能 # u! a/ r" b8 P' M& [
( HCONN hConn,
' a7 ~; L' h2 D; p9 N: E7 YDWORD dwHSERRequest,
6 k' ]& i2 ?4 Z& Q7 l& MLPVOID lpvBuffer, " a6 k; G+ \9 s3 t
LPDWORD lpdwSize,
! K( C; }, [( Z- L- K( [LPDWORD lpdwDataType );
( O$ _; b1 p& B  {8 S" f/ M8 x5 i# l" h. Y7 s6 g& K# Z* e" C
} EXTENSION_CONTROL_BLOCK, *LPEXTENSION_CONTROL_BLOCK; 3 }( ~  \9 `# J, Z6 I( D
$ u1 A  C1 s2 y5 z
在上述ECB中,服务器不但提供了当前HTTP连接的句柄和一些变量,而且提供了4个回调函数给ISA调用,从而使ISA可以获得更详尽的信息。 " U$ x1 O& H) ?, G
# V, m; X( `! J* k, ^' ]
三、ISAPI Filter 2 o0 c* U* i) d& M) A

8 }: L3 }6 V2 Q( {5 }: G" ^ISAPI Filter位于服务器和客户端之间,能够对服务器和客户端之间的通信进行预处理和后处理,比如对通信进行加密/解密、提供对客户进行身份验证的新方法、提供自定义的日志记录等,在CGI中没有与ISAPI Filter直接相对应的部分。
9 t$ G0 p# c1 E2 V( ^( r+ t
$ D0 ]7 Y* L/ A6 X  \1 y9 A; |ISAPI Filter与服务器之间的接口有两个:GetFilterVersion( )和HttpFilterProc( )。任何 8 B+ K- E: n3 ^* c
ISAPI Filter都必须引出这两个函数以供服务器调用。 ) v+ {1 E% G! q' Y$ }$ y  s4 c  k
, K/ f/ w& d% ?' f- N# ^& d
1、在注册表的如下键值中存放着所有ISAPI Filter的文件名,IIS服务器启动时从该键值中获得 $ l& n& K+ m; }
Filter的文件名并加载它们。 . R* x( F+ j! W, ]% Z! F) g

% e8 c  o; R( _- h0 E# o, yHKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/W3SVC/Parameters/FilterDLL
$ \0 J) h+ q* Q& @1 x. e2 ^2 j% I$ o9 B: k8 v6 k9 ^7 u6 F
2、然后服务器调用每个Filter提供的GetFilterVersion( )函数,获得版本号以及该Filter希望处理的事件,即ISAPI Filter通过引出GetFilterVersion( )函数来告知服务器自己希望处理什么类型的事件,因为ISAPI Filter是通过事件来激活的,当满足条件的事件到达时,服务器就会调用Filter引出的主函数HttpFilterProc( )对该事件进行处理。GetFilterVersion( )的原型如下:
. ]4 `$ _  d3 Z0 }/ D8 ^3 v% V: x9 D  |0 ~, L3 y9 q9 b: P! z
BOOL WINAPI GetFilterVersion(
) F: y/ B1 x# o3 dDWORD dwServerFilterVersion; //IN,服务器使用的版本规范 8 |: I6 i. O  s+ x
DWORD dwFilterVersion; //OUT,过滤器使用的版本规范 / v1 L% f$ r% Q& l
CHAR lpszFilterDesc[SF_MAX_FILTER_DESC_LEN+1]; //OUT,对该过滤器的描述字符串
- \7 N' d/ W8 CDWORD dwFlags //OUT,事件和优先级标志 # k* h; a4 B- I8 h% E1 i) O1 V
);   Q- N9 E, \6 Z; `. L
9 y7 w: d4 [5 n
事件和优先级标志dwFlasg的取值在MSDN中有详细解释,其中包括该Filter被调用的优先级,一般应使用默认的低优先级,否则可能会对系统的性能造成很大影响。
' u3 T" z- \8 T; F  v$ V- w* x1 v! r9 V8 H! A$ P2 T5 s
3、HttpFilterProc( )是ISAPI Filter主要的入口函数,它根据当前的事件的不同作出不同的处理。服务器通过如下的参数块和Filter进行交互,这个参数块的作用和ISA中的ECB类似。 3 O4 [: ~$ ]. G# Z* q2 W

! d8 W2 K- d2 s  ntypedef struct _HTTP_FILTER_CONTEXT 4 P" {; }: }. z
{ : }: O$ t7 h( d( u" N6 }
* R1 e. i3 H& q, c; q& C# e
DWORD cbSize; //IN,本参数块的大小 3 Y3 U; _( d$ [; x2 r
DWORD Revision; //IN
# c4 v5 F; U; y4 a# GPVOID ServerContext; //IN,由server使用本参数 - I- m3 v: [4 Y( j5 u
DWORD ulReserved; //IN,由server使用本参数
% N! _# L% m% ]$ C% T" OBOOL fIsSecurePort; //IN,事件是否发生在安全端口上 5 w5 R: f5 \) w6 Q2 B' g
PVOID pFilterContext; //IN/OUT,与本次请求相关的上下文 7 }& f- d, `& q" F6 k* y5 x9 I

! F* Y* U: F0 w8 s7 S$ D: O//回调函数,取得关于服务器和本次连接的信息
2 i: X* \& T- d" W% Y% M* {  _$ vBOOL (WINAPI * GetServerVariable) (
2 I0 q" P- F/ N$ J; M+ Nstruct _HTTP_FILTER_CONTEXT * pfc,
# A0 D1 C3 x! t4 \' @3 N8 i- m4 OLPSTR lpszVariableName, 3 s5 J& f" E+ X3 |" U
LPVOID lpvBuffer,
5 P+ k& q: f  [9 tLPDWORD lpdwSize
1 O) O) H7 h( g; W) P3 d1 f); 5 K7 }2 |* J! b
0 L3 M5 H0 q& p8 v; l
BOOL (WINAPI * AddResponseHeaders) ( //回调函数,给HTTP响应添加一个标头
! p$ s5 J; x7 M! B- mstruct _HTTP_FILTER_CONTEXT * pfc,
7 I) }7 s4 \" l( ZLPSTR lpszHeaders,
1 c+ z/ r& j2 ?3 ^( bDWORD dwReserved # m7 X0 |$ N! r4 R8 N6 k3 q
);
' ?, i' D) g! A" r* B, z) V4 H8 U0 x5 R
BOOL (WINAPI * WriteClient) ( //回调函数,将原始数据发送给客户端 6 N7 Z1 U, v. F+ H
struct _HTTP_FILTER_CONTEXT * pfc, $ Q$ `" s. ^% V; K$ g% b) `
LPVOID Buffer, - w1 l, i- ^1 H2 ?
LPDWORD lpdwBytes,
4 g: @" X0 a( WDWORD dwReserved
* ]' H% t# g) n! [5 X/ ~+ ^); ( ?4 l; f; I5 S6 f
7 y  r: M) x& u, n
VOID * (WINAPI * AllocMem) ( //回调函数,分配内存。 ' \$ a1 p; j2 r$ K7 j4 ^0 B
struct _HTTP_FILTER_CONTEXT * pfc, 0 y4 W3 E% e7 j/ d. J
DWORD cbSize, 4 P& ^* b/ p; x, p9 p: R. x
DWORD dwReserved 6 [# X  b% u  v" O
);
2 P$ D3 n4 J- v$ b- O( p1 B- P9 y9 ~& P# L  `8 r, y3 N9 M( n9 O9 N
BOOL (WINAPI * ServerSupportFunction) ( //回调函数,访问服务器的一般和特定功能
. `8 Y/ s- D1 G* z1 k1 z) `struct _HTTP_FILTER_CONTEXT * pfc, 1 s- e: S4 D) f4 W# S
enum SF_REQ_TYPE sfReq, 7 l' G8 T4 q. Y+ ^3 o6 R6 \
PVOID pData, 2 V$ e9 \+ |3 X  u# A2 l" N
DWORD ul1, 2 v$ S; I9 X/ E7 b( X: C0 @* l! @  n+ t
DWORD ul2 8 e) [: k% r7 m" }/ l7 `6 h
);
8 K/ ?& t" ?! Q1 l: R" M; O8 M* A0 s) R0 P
} HTTP_FILTER_CONTEXT, *PHTTP_FILTER_CONTEXT;
8 Q/ m+ a0 {: E( }2 G& {9 A3 R0 t/ _4 X& ^
四、VC++ 6.0中对ISAPI的支持 4 r- I$ ]& Q! X$ a' q' g" F
% h2 ~5 `4 r$ Y! a" Z$ @+ ~
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实例,每个
  C! M1 w) F5 ^CHttpServerContext处理一个客户请求,这样可以处理并发的HTTP请求;CttpFilter和CHttpFilterContext之间的关系与此类似,在每个ISAPI Filter中只能有一个CHttpFilter实例,但是可以有多个CHttpFilterContext来处理并发的事件。CHttpServer和CHttpFilter是独立的类,它们可以共存于一个DLL中,也可以分别在不同的DLL中。
- N6 @3 u  R( T/ \
8 y; `% i% n  S( D2 A* a) l一个ISA可以提供多个命令,每个命令对应于CHttpServer(或其子类)的一个成员函数,客户端可以在URL中指定命令名及其参数。在VC++ 6.0中是通过parse map来实现这种对应的。 9 ], j3 }' B' m

8 Q1 j9 t' k8 gParse 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为例,该例中有下面这样一个表单: # r1 W9 l3 l/ [+ `

5 u0 O; l, f  _6 H; z<form method=get action="pinball.dll?"> - N' d, M/ |% U% L, g
<input type="hidden" name="MfcISAPICommand" VALUE="GetImage">
8 B! n* b6 X3 e- I0 R  I" U. y<input type="radio" name="Favorite" value="1" checked> Attack from Mars<br>
+ x* @1 z# B9 a, H+ O: i: M) l<input type="radio" name="Favorite" value="2"> Twilight Zone<br>
! a* p2 z& l% t2 G& Z<input type="radio" name="Favorite" value="3"> The Addams Family<br> ; M: R$ L% C- |- G
<input type="radio" name="Favorite" value="4"> Cirqus Voltaire<br> 6 T& D# [8 G) T" f% u# ~9 A
<input type="radio" name="Favorite" value="0"> I don't see it here<br>
  j( [& i: S! V2 d# i4 w0 U<br>
5 c* N& R$ p. X9 U( R* P8 M: w' z<input type="submit" value="Show Me!">
. e" _. z; M; V</form>
  E+ _7 F/ k) c1 u, e& ]
2 d& f& g9 p7 I9 N( j当客户端选中了上面的表单中的“Attack from Mars”这一项并点击了submit按钮后,服务器端
. Q9 J3 e0 p! f2 b最终将得到如下的URL串:
+ }! z% Q7 }5 s( n9 [; y
9 C+ Y5 C0 B5 N4 T2 g* g. o  Z8 Ohttp://www.abc.com/pinball.dll?M ... mage&Favorite=1 / n7 {9 O" u- [+ G

9 d9 _7 k+ U7 Z) h在该URL串中,命令名是GetImage,参数Favorite的值是1,因此pinball.dll中的如下成员函数 / D+ P  L* g' S- p9 Y
将被调用以处理该请求,其中参数dwChoice对应URL中的参数Favorite:
% |. g) J' V5 B0 p% g+ y! n0 E; d. h4 X, K- t6 m
void CPinballExtension::GetImage(CHttpServerContext* pCtxt, long dwChoice);   P# T1 I6 }2 q- G) j/ E7 P; `/ Q
8 `: O2 N: [2 O% n, V2 i7 V* p
而parse map需要按照下面的形式定义: & o, w, F5 N- V3 ]
2 A; T* k. w2 B1 \
//CPinballExtension从CHttpServer派生而来 1 A! C# M* O' T
BEGIN_PARSE_MAP(CPinballExtension, CHttpServer) * P3 j5 W% x' W* O. z

! j8 [7 F# i: ?& n; P* N//GetImage是CPinballExtension的成员函数,且有一个long型的参数即dwChoice 4 s- `* P! |% n" X/ \% Y. n' t
ON_PARSE_COMMAND(GetImage, CPinballExtension, ITS_I4)
  q# h& _6 M3 v- v4 i. i
8 w* Q4 R2 f7 o. T. g' m. D4 v//该参数在URL中的名字为Favorite
1 R( z  {6 O  q' w# WON_PARSE_COMMAND_PARAMS("Favorite") " A; Y# j( k$ `
0 Z6 q1 e" {& O+ p4 }# R' r
END_PARSE_MAP(CPinballExtension) 3 }# H+ G& v' R# c! V' q2 f
" ^1 A) o0 ]. T
而对于ISAPI Filter,在VC中可以通过重载CHttpFilter(或其子类)的不同的成员函数来实现对不同事件的处理。可重载的函数如下,每一个成员函数均对应一个或多个事件: 6 y* e2 ?7 l# `1 @
5 M3 g7 d( N2 N( F5 I: D
OnPreprocHeaders 3 _' a. ?9 R8 s. t, m, Y
OnAuthentication 5 N2 t, X0 z1 @* z( j! V3 T) c7 v! Q
OnUrlMap 1 {5 g; ]7 ], c/ `1 l7 _
OnSendRawData 9 h9 [# E; u4 o$ y7 f
OnReadRawData
% e/ \( V* n/ }; T- z% q$ s6 mOnLog ; }$ e3 F6 e. ]2 E
OnEndOfNetSession
1 }- N4 \: ~, t+ E7 s( S1 {) |* o& Q. g& r- }. e
MSDN提供了4个关于ISAPI的编程实例:counter、MFCUCASE、pinball、wwwquote,有兴趣的可看看,本文主要不是介绍编程,所以不再赘述。 ( t- g( V1 |- T2 t8 S
4 g- \  u* v: r9 R+ r# q
参考资料: ' n. y. |! k6 q7 e5 x
' D5 q# z. Z- [- V, A3 r' ?
1、MSDN
& X1 P9 W5 H/ r4 m" s2、《精通CGI编程》,丁一强等,清华大学出版社
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-10-1 20:51 , Processed in 0.019689 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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