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

IIS的ISAPI接口简介

[复制链接]
发表于 2003-10-13 13:22:17 | 显示全部楼层 |阅读模式
作者:netguy (mailto:netguy@nsfocus.com)
+ ?6 k* [, ], q& @4 ^
' R7 M& d/ H6 g$ B# l- uISAPI(Internet Server Application Programming Interface)作为一种可用来替代CGI的方法,是由微软和Process软件公司联合提出的Web服务器上的API标准。ISAPI与Web服务器结合紧密,功能强大,能够获得大量的信息,因此利用ISAPI可以开发出灵活高效的Web服务器增强程序。由于ISAPI程序与Web服务器的关系,使得ISAPI接口在安全方面有一定的研究价值。本文主要讨论ISAPI在IIS和VC++ 6.0中的实现。
% p" g1 X  z' @: \5 [4 y7 o8 f* ]. g2 X; D/ w" Q
一、ISAPI接口和CGI接口的不同。
! o+ @# d" }- D- j$ @$ B( ]
' X7 Z9 A4 u9 E! H% y* d! AISAPI程序和CGI程序完成类似的功能,但是实现方法不同。" v! R$ s$ U9 v3 }

( Z; x3 [6 ~9 H3 N& i+ H9 [$ a1、ISAPI程序以DLL形式被Web服务器加载到自己的进程空间中,因此和服务器共用同一个地址空间,且在没有客户请求时可以将其从内存中卸载;而对客户端发来的每个对CGI程序的请求则需要服务器为它单独启动一个进程,这需要耗费大量的时间和内存。当并发的请求数目很大时,使用CGI在效率上不如ISAPI。6 W3 \1 E+ N0 n5 T' @& B
5 j5 T4 C* u1 z% a6 }( y1 E
2、CGI程序通过环境块和标准输入输出与Web服务器进行通信,而ISAPI程序与服务器结合得更为紧密,与服务器共享同一个进程上下文,主要通过一个参数块与服务器进行交互,可以从服务器那里获得关于当前HTTP连接的大量信息。1 G3 }/ g8 X9 o5 @- {8 `& e" ~
# [" @4 L! b- d& b& J
ISAPI主要分为ISA和ISAPI Filter两部分。ISA方法相对而言要传统一些,利用一些特殊的链接,指向服务器的作业,供程序开发人员设计一些扩展功能;而ISAPI过滤器则倾向于构造服务器直接调用的模块,提供一种无缝链接部件用于监测直接来自于服务器的HTTP请求。; \9 ~: v: M) d2 U  }2 q
3 v1 M; I: O$ |2 D! C7 b
7 @. X2 Z6 c1 A- R+ A4 v
二、ISA
  s8 A! n: a! A' e) H% q" U9 C, U; O; V6 r" c$ Y
ISA(Internet Server Application)也可称为ISAPI DLL,其功能和CGI程序的功能直接相对应,使用方法和CGI也类似,由客户端在URL中指定其名称而激活。例如下面的请求将调用服务器的虚拟可执行目录Scripts下的function.dll(ISAPI DLL必须放在服务器的虚拟可执行目录下):8 H5 S* o7 a7 @
http://www.abc.com/Scripts/function.dll?, O; Y$ ?5 u( X* ^, `

# `4 B2 J9 @1 H' iISA和服务器之间的接口主要有两个:GetExtentionVersion( )和HttpExtentionProc( )。任何ISA都必须在其PE文件头的引出表中定义这两个引出函数,以供Web服务器在适当的时候调用。
' u: ?) A5 D: s' }
7 f/ F6 C% v# ?5 x1 E1、当服务器刚加载ISA时,它会调用ISA提供的GetExtentionVersion( )来获得该ISA所需要的服务器版本,并与自己的版本相比较,以保证版本兼容。函数原型如下:& p; P  A  Q; z8 {" B) J

& Z# R" g0 U0 a5 h( I' F/ N  Q, D: BBOOL WINAPI GetExtentionVersion(HSE_VERSION_INFO *version);
+ y9 O# v0 V# j  v3 j2 T' Stypedef struct _HSE_VERSION_INFO
3 Z8 h4 `+ G) O2 {{
4 I% w6 J, S1 ^9 zDWORD dwExtensionVersion; //版本号
% H6 J! ]! U2 T$ r) NCHAR lpszExtensionDesc[HSE_MAX_EXT_DLL_NAME_LEN]; //关于ISA的描述字符串, s& E# M* D% A% M
} HSE_VERSION_INFO, *LPHSE_VERSION_INFO;4 U. }1 ?% z6 N4 \* C" r

+ o) C9 b' b- \2 R) Y/ r2、ISA的真正入口是HttpExtentionProc( ),它相当于普通C程序的main( )函数,在这个函数中根据不同的客户请求作不同的处理。服务器和HttpExtentionProc( )之间是通过扩展控制块(Extention Control Block)来进行通信的,即ECB中存放入口参数和出口参数,包括服务器提供的几个回调函数的入口地址。函数原型如下:
9 {, O  e8 L* {
! ?1 o- s! `2 H& w3 M1 fDWORD HttpExtensionProc( EXTENSION_CONTROL_BLOCK *pECB );
1 @8 {5 e% k/ ~) u6 x8 b
9 w/ h" k# @) q) Q, \3 g9 ?* [ECB的结构定义如下(IN表示入口参数,OUT表示出口参数):
" r- C& a3 b# D) h: r3 g: m7 R- `2 ~8 K( k2 I2 G7 a9 f
typedef struct _EXTENSION_CONTROL_BLOCK 2 ]2 h/ h) }8 W
{
+ a5 q: Q  I, }& iDWORD cbSize; //IN,本结构的大小,只读
7 @) }, _' o" v* cDWORD dwVersion //IN,版本号,高16位为主版本号,低16位为次版本号
( m2 G( D( V/ O9 @HCONN ConnID; //IN,连接句柄,由服务器分配,ISA只能读取该值
5 c  s& Q/ R4 b, c' @DWORD dwHttpStatusCode; //OUT,当前完成的事务状态
; ]# Z; T  _8 O& S5 n) [5 VCHAR lpszLogData[HSE_LOG_BUFFER_LEN]; //OUT,需要写入到日志文件中的内容
0 N! G" w$ U( t  GLPSTR lpszMethod; //IN,等价于CGI的环境变量REQUEST_METHOD
5 U! @* y. L6 K6 B/ rLPSTR lpszQueryString; //IN,等价于环境变量QUERY_STRING
# b; M& h7 u' e1 \% [: e" oLPSTR lpszPathInfo; //IN,等价于环境变量PATH_INFO- ?' n# o, I3 X2 e. E) i* S+ b
LPSTR lpszPathTranslated; //IN,等价于环境变量PATH_TRANSLATED. x: _! b, e+ }) k: d3 z. ?6 S
DWORD cbTotalBytes; //IN,等价于环境变量CONTENT_LENGTH( p6 U) V( F- E% O
DWORD cbAvailable; //IN,缓冲区中的可用字节数
( v+ C0 w& S) ~( Z  O/ tLPBYTE lpbData; //IN,缓冲区指针,指向客户端发来的数据$ A) t& ]& Y3 i( x% x
LPSTR lpszContentType; //IN,等价于环境变量CONTENT_TYPE
/ M; |2 t& p; K
3 E! v! \& i- x2 a  F//回调函数,用于返回服务器的连接信息或特定的服务器详细情况" H" C3 v' k8 P9 s" u8 V' W' _  m
BOOL ( WINAPI * GetServerVariable ) ) x: A" q  C; w" j4 d% V9 t- _
( HCONN hConn,
7 t0 y" l7 ~3 g  O6 K/ B6 JLPSTR lpszVariableName,
% x# R* u8 v8 D( @LPVOID lpvBuffer,5 @/ ^( X: a( a7 _
LPDWORD lpdwSize );' @5 F0 t* G. g2 W- t

3 a0 t/ S) g" TBOOL ( WINAPI * WriteClient ) //回调函数,从客户端的HTTP请求中读取数据
* k( Z' p! R7 X1 C/ _% t: s, E( HCONN ConnID,
- Z8 V0 N) r% Q8 g$ X4 gLPVOID Buffer,
. |  E. }5 U' I6 G' S3 }- PLPDWORD lpdwBytes,
' q3 q9 [" Y* E5 Q+ fDWORD dwReserved );- H* O3 d1 m3 }- ?
# Q! J1 m1 u$ {5 f. e
BOOL ( WINAPI * ReadClient ) //回调函数,向客户端发送数据
: L3 {1 v5 k& }6 P" P& b9 C3 {( HCONN ConnID,
6 B/ u! f/ G3 z5 kLPVOID lpvBuffer,3 H1 I. `8 P2 ^! L4 Z. n8 x
LPDWORD lpdwSize );
) j: h& Z; E: s& Q. b8 e4 g! _; w; J2 l7 _# L
BOOL ( WINAPI * ServerSupportFunction ) //回调函数,访问服务器的一般和特定功能8 K5 S" O6 @/ Q+ X
( HCONN hConn,9 k2 S; z! H8 M8 W
DWORD dwHSERRequest,
. W7 R& U1 K; V! ZLPVOID lpvBuffer,* u' [  `% Q$ l+ g
LPDWORD lpdwSize,
, Q6 e) H: ^* LLPDWORD lpdwDataType );
' K- E4 s  y' ^. c/ y( s6 I. J& O3 ^: M. ?
} EXTENSION_CONTROL_BLOCK, *LPEXTENSION_CONTROL_BLOCK;
# _$ @% t/ ^& o  f9 b* S. n  D! P' e* o
在上述ECB中,服务器不但提供了当前HTTP连接的句柄和一些变量,而且提供了4个回调函数给ISA调用,从而使ISA可以获得更详尽的信息。! c4 f" X; J! L; @7 ]8 E! y
5 T: d' r5 a4 C5 y
三、ISAPI Filter) O. @% e1 p! `% [& V
- S- E4 U: s1 o# |* d( J% X8 |0 a' `
ISAPI Filter位于服务器和客户端之间,能够对服务器和客户端之间的通信进行预处理和后处理,比如对通信进行加密/解密、提供对客户进行身份验证的新方法、提供自定义的日志记录等,在CGI中没有与ISAPI Filter直接相对应的部分。$ t" b4 }  v5 E5 N$ [

9 V% ^; L9 i' A' IISAPI Filter与服务器之间的接口有两个:GetFilterVersion( )和HttpFilterProc( )。任何& Q9 o; l6 g  d: _+ y: s
ISAPI Filter都必须引出这两个函数以供服务器调用。* _3 j4 K  L* n, `
0 M/ v9 z$ \; b: ^' a
1、在注册表的如下键值中存放着所有ISAPI Filter的文件名,IIS服务器启动时从该键值中获得
2 o; `5 R: ]" a0 I4 T6 e/ uFilter的文件名并加载它们。( E/ ^3 K; C2 ^+ \1 r; D, q! |1 @

9 c9 d" k5 X2 v4 Q  w* C+ zHKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/W3SVC/Parameters/FilterDLL
: z# m- V+ ^" x0 t. l7 n( n
# Z& i0 e1 r4 z! D2、然后服务器调用每个Filter提供的GetFilterVersion( )函数,获得版本号以及该Filter希望处理的事件,即ISAPI Filter通过引出GetFilterVersion( )函数来告知服务器自己希望处理什么类型的事件,因为ISAPI Filter是通过事件来激活的,当满足条件的事件到达时,服务器就会调用Filter引出的主函数HttpFilterProc( )对该事件进行处理。GetFilterVersion( )的原型如下:' b1 J+ l$ `/ Z2 z8 ^( n8 ]

. ^, i% u7 O" c/ PBOOL WINAPI GetFilterVersion(
% n9 A: z  k3 q8 DDWORD dwServerFilterVersion; //IN,服务器使用的版本规范
1 |0 f" ]! ^( oDWORD dwFilterVersion; //OUT,过滤器使用的版本规范
* Q+ d) i1 t, OCHAR lpszFilterDesc[SF_MAX_FILTER_DESC_LEN+1]; //OUT,对该过滤器的描述字符串
) a( }+ A' F- XDWORD dwFlags //OUT,事件和优先级标志# G4 x2 v- }! f+ i% x  A
);
1 W/ o( c9 Q% C+ D- U/ H- i5 s7 Z! w. ^! v
事件和优先级标志dwFlasg的取值在MSDN中有详细解释,其中包括该Filter被调用的优先级,一般应使用默认的低优先级,否则可能会对系统的性能造成很大影响。
/ C! L& P& E; L; `, n7 ]6 Q0 s0 F  ]  v. ~2 y& |: P/ E8 ~
3、HttpFilterProc( )是ISAPI Filter主要的入口函数,它根据当前的事件的不同作出不同的处理。服务器通过如下的参数块和Filter进行交互,这个参数块的作用和ISA中的ECB类似。
9 H+ j' k2 B  [1 ]. Q7 x  h5 a6 X. V
typedef struct _HTTP_FILTER_CONTEXT
/ x3 L, J1 |! b5 h  e{3 Y; ~$ p7 g  F

1 _: E; N- g$ m1 n- _* B( q$ RDWORD cbSize; //IN,本参数块的大小
4 \3 Y7 o* @# T7 n! ZDWORD Revision; //IN8 m' ]" ]8 ?9 \& P7 G
PVOID ServerContext; //IN,由server使用本参数- L% ^. ?0 q' v( [! `" M  {; [9 s! N9 V
DWORD ulReserved; //IN,由server使用本参数# k* a6 n" z$ K. h" N
BOOL fIsSecurePort; //IN,事件是否发生在安全端口上* G4 m0 Y( c% h3 Y# R
PVOID pFilterContext; //IN/OUT,与本次请求相关的上下文% X( r" z7 ^1 k5 `
2 x: G# o+ W: D- B+ \) e2 l
//回调函数,取得关于服务器和本次连接的信息6 E. Y" ~5 U. D: Q) Q/ ^7 |$ P
BOOL (WINAPI * GetServerVariable) (
8 ^! L/ R- L$ l5 e' c0 Ystruct _HTTP_FILTER_CONTEXT * pfc,( S+ }1 k9 `) C+ j
LPSTR lpszVariableName,
5 R* D1 w( h8 G. hLPVOID lpvBuffer,' H! C* h& a1 @- |
LPDWORD lpdwSize# A- R' _" x* K% F
); % [  c) [- z! n$ V

3 Y+ O5 e. V! G7 h! f  I9 R! J% PBOOL (WINAPI * AddResponseHeaders) ( //回调函数,给HTTP响应添加一个标头- ^1 v) S. l( j9 G7 S9 |
struct _HTTP_FILTER_CONTEXT * pfc,
+ ^6 \+ S4 O! D. X1 T# H7 DLPSTR lpszHeaders,4 ~4 C' |( o5 }% I
DWORD dwReserved
0 q# O: h% _' T); 7 M4 k$ ?( k, l  w; i
4 W8 Y1 i* Q1 u* H, g3 e6 p
BOOL (WINAPI * WriteClient) ( //回调函数,将原始数据发送给客户端
) z" b  w4 `- |3 P5 D  `struct _HTTP_FILTER_CONTEXT * pfc,' m+ k7 G- M) y# |2 h) ^* q: P
LPVOID Buffer,
* x2 x) N, P4 s7 A& {. {: iLPDWORD lpdwBytes,
" q2 z: k9 `/ w# S1 IDWORD dwReserved
' l1 s( l: A) G);
5 J2 v' J/ h; M5 l. o. B( _+ j+ x* L
VOID * (WINAPI * AllocMem) ( //回调函数,分配内存。
8 y% w* ~* K: V' Jstruct _HTTP_FILTER_CONTEXT * pfc,
# o7 @6 O: V8 Q" KDWORD cbSize,
  w+ y1 n6 X' A6 @5 VDWORD dwReserved
# I1 B, I: t8 P+ g. _: P# w); 9 Z* g: k1 f4 I9 S+ T; A& j- v
- t+ y5 S4 k! r3 @. r: t+ d  K
BOOL (WINAPI * ServerSupportFunction) ( //回调函数,访问服务器的一般和特定功能
# N' d0 Q+ ^) ]1 h. j  ostruct _HTTP_FILTER_CONTEXT * pfc,
. P2 x/ Q' r8 _5 X! A* d- {enum SF_REQ_TYPE sfReq,
7 i9 g! M. K3 y3 o" H' {PVOID pData,0 d7 E/ A/ D2 R" Q
DWORD ul1,; T$ b- P" ?6 p* g6 ^
DWORD ul2
4 f  ?: B$ M, G$ A0 }- d);
! u2 ]4 |& w8 B8 ?. ?3 g+ O2 f" K5 X" d2 H+ R5 X  y  ]+ P+ w3 q
} HTTP_FILTER_CONTEXT, *PHTTP_FILTER_CONTEXT;7 J: A. _- _: r
9 |" b6 @7 ~/ l( l2 Z7 _; P$ q) U, F
四、VC++ 6.0中对ISAPI的支持
' Q5 `8 c8 \& a, x! B' F
4 Z5 k' D: S* o4 D% wVC++ 6.0中定义了5个相关的类以简化ISAPI的编程工作:CHttpServer、CHttpServerContext、CHttpFilter、CHttpFilterContext、CHtmlStream,这5个类都没有父类。其中CHttpServer和CHttpServerContext主要用来编写ISA,CHttpFilter和CHttpFilterContext则用来编写ISAPI Filter,而CHtmlStream则用来操作内存中的HTML文件,为其它的4个类提供服务。CHttpServer在每个ISA中只能有一个实例,一个CHttpServer可以对应多个CHttpServerContext实例,每个
2 P; {3 i. @6 h: Z/ L1 V5 [- `# ZCHttpServerContext处理一个客户请求,这样可以处理并发的HTTP请求;CttpFilter和CHttpFilterContext之间的关系与此类似,在每个ISAPI Filter中只能有一个CHttpFilter实例,但是可以有多个CHttpFilterContext来处理并发的事件。CHttpServer和CHttpFilter是独立的类,它们可以共存于一个DLL中,也可以分别在不同的DLL中。& V4 U# p8 \- j& V2 y% \8 V- W

# r1 c; l' u$ M$ c& S0 p% i一个ISA可以提供多个命令,每个命令对应于CHttpServer(或其子类)的一个成员函数,客户端可以在URL中指定命令名及其参数。在VC++ 6.0中是通过parse map来实现这种对应的。0 R2 U9 v( K& b# q5 o! p

- h( ^! w/ K  Q' {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为例,该例中有下面这样一个表单:
. G, y8 S1 o0 t
" _- u2 m! A* p/ l; G( i1 W<form method=get action="pinball.dll?">0 M* V$ V0 E2 u( e% l2 w  [
<input type="hidden" name="MfcISAPICommand" VALUE="GetImage">7 i0 I1 `2 f4 {) x9 e6 u6 q) k9 h% e
<input type="radio" name="Favorite" value="1" checked> Attack from Mars<br>( O  k8 D3 x- h& M5 U
<input type="radio" name="Favorite" value="2"> Twilight Zone<br>
$ X* |1 A* w5 v3 y<input type="radio" name="Favorite" value="3"> The Addams Family<br>8 e  v3 E1 C& e9 ?9 N# S
<input type="radio" name="Favorite" value="4"> Cirqus Voltaire<br>
9 B- v. T# t$ e" I<input type="radio" name="Favorite" value="0"> I don't see it here<br>; j3 Q  f- R7 n9 `6 u' H
<br>" T) _' o' x( K4 T% F+ V
<input type="submit" value="Show Me!">
$ p* c9 b" f8 c</form>
1 s7 S9 \! F8 {+ A  ~
% `3 s' K2 ~# c0 f7 ?: j2 p) {当客户端选中了上面的表单中的“Attack from Mars”这一项并点击了submit按钮后,服务器端
# M9 D) ]' h2 _3 ^9 c最终将得到如下的URL串:' c) X+ F& Q( y$ _0 p
1 D9 Y! o# F5 G! c: X8 H' |
http://www.abc.com/pinball.dll?M ... GetImage&Favorite=16 \6 @: N$ |" ]9 e3 m3 z0 \: U' h

0 R% `& R8 t6 P  `$ V* I# t& ?; i在该URL串中,命令名是GetImage,参数Favorite的值是1,因此pinball.dll中的如下成员函数& t& \6 l+ H, Z  J: S% C4 P! h
将被调用以处理该请求,其中参数dwChoice对应URL中的参数Favorite:! V( e$ w$ D& ~; Z# n4 ?
$ I; ~. U, }& a- F# X' X! z
void CPinballExtension::GetImage(CHttpServerContext* pCtxt, long dwChoice);: T% R  N3 I& f! J  {: ?; b

$ U! B' c& ]4 s9 O而parse map需要按照下面的形式定义:+ ~: i; D2 G/ y0 x# ^

  Y. F% {( y! n9 B' T. O# q//CPinballExtension从CHttpServer派生而来/ l1 _+ ], g* z- ~$ q
BEGIN_PARSE_MAP(CPinballExtension, CHttpServer) 1 x5 [, N/ S+ b" b+ A5 g1 o: l
, T, v7 k* S9 L1 E
//GetImage是CPinballExtension的成员函数,且有一个long型的参数即dwChoice
4 x% I- P* T# O1 Y7 \# h1 y# NON_PARSE_COMMAND(GetImage, CPinballExtension, ITS_I4)
- @& b9 m3 @. |! z' G. V1 Y
) C3 P' _0 `2 L* R. Z$ b1 u0 n//该参数在URL中的名字为Favorite* z5 m  Z0 C, u, X
ON_PARSE_COMMAND_PARAMS("Favorite")
+ o4 }, P! p" W) U. z# |& j: I" A* J, D9 T. {5 Q
END_PARSE_MAP(CPinballExtension)* m6 G, o2 _6 g9 E6 k' n

" C9 V3 X/ G3 f5 s. \  w: c( O而对于ISAPI Filter,在VC中可以通过重载CHttpFilter(或其子类)的不同的成员函数来实现对不同事件的处理。可重载的函数如下,每一个成员函数均对应一个或多个事件:
) A$ `: b8 {5 |. J8 I* a6 n, |; n8 U0 w+ n) {7 T- m  r
OnPreprocHeaders
" ^/ p4 f+ b% p' \4 O$ mOnAuthentication: Y3 @6 C  Q7 H- ]% X" @
OnUrlMap6 J% W1 p! n( {: h1 b
OnSendRawData
9 _; x2 y. S: X% w0 _- JOnReadRawData6 w7 N+ t5 T+ Q3 w! M# a
OnLog
+ U  m& g1 S2 tOnEndOfNetSession
& \0 J; G7 v. v$ o. H% l0 `
9 P' n; w# H0 M8 `MSDN提供了4个关于ISAPI的编程实例:counter、MFCUCASE、pinball、wwwquote,有兴趣的可看看,本文主要不是介绍编程,所以不再赘述。
# \" f$ _$ t8 m! q. P+ f6 E! c
参考资料:% W- r' H. u: z0 q% c2 B

, a: \4 u$ @0 j8 w1、MSDN$ U" n, u; U1 }3 g
2、《精通CGI编程》,丁一强等,清华大学出版社
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-8-13 22:41 , Processed in 0.019735 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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