找回密码
 注册
搜索
查看: 4003|回复: 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中的实现。 - u4 \* ^& g1 E* T. o

7 u" ?. }! B$ |" h* i% O一、ISAPI接口和CGI接口的不同。
, ~: u6 T% W5 }$ N: T
. L. G2 I4 J7 _ISAPI程序和CGI程序完成类似的功能,但是实现方法不同。 5 a. I; Y. A3 X6 C5 ^/ h
; M: N3 ^3 s; \  T  H4 x
1、ISAPI程序以DLL形式被Web服务器加载到自己的进程空间中,因此和服务器共用同一个地址空间,且在没有客户请求时可以将其从内存中卸载;而对客户端发来的每个对CGI程序的请求则需要服务器为它单独启动一个进程,这需要耗费大量的时间和内存。当并发的请求数目很大时,使用CGI在效率上不如ISAPI。
! X& d+ U) e- j' ^: b
/ {) e) C, ^6 g. k1 [: j% |2、CGI程序通过环境块和标准输入输出与Web服务器进行通信,而ISAPI程序与服务器结合得更为紧密,与服务器共享同一个进程上下文,主要通过一个参数块与服务器进行交互,可以从服务器那里获得关于当前HTTP连接的大量信息。 " }$ A/ ~0 d: V% _8 s
* f) M# Y7 s6 Y" V& M. b% C8 m: K/ C% Q
ISAPI主要分为ISA和ISAPI Filter两部分。ISA方法相对而言要传统一些,利用一些特殊的链接,指向服务器的作业,供程序开发人员设计一些扩展功能;而ISAPI过滤器则倾向于构造服务器直接调用的模块,提供一种无缝链接部件用于监测直接来自于服务器的HTTP请求。 6 H! t  C6 R" w2 C: M; T

0 g) c+ N& e9 S) R' p$ T" x6 `7 c. v# J. J6 O1 c0 F& W1 ^
二、ISA   p, Z; V3 p8 n' I* B) `

+ q( m- i; ~$ d( i8 S$ O4 ^ISA(Internet Server Application)也可称为ISAPI DLL,其功能和CGI程序的功能直接相对应,使用方法和CGI也类似,由客户端在URL中指定其名称而激活。例如下面的请求将调用服务器的虚拟可执行目录Scripts下的function.dll(ISAPI DLL必须放在服务器的虚拟可执行目录下): $ u5 ^# R! O" l6 k+ f
http://www.abc.com/Scripts/function.dll?
8 K  z' R! r. \, r9 \, M% P
7 C0 u) h  o$ r+ tISA和服务器之间的接口主要有两个:GetExtentionVersion( )和HttpExtentionProc( )。任何ISA都必须在其PE文件头的引出表中定义这两个引出函数,以供Web服务器在适当的时候调用。 * U; l# f) \( W. i9 U$ ?/ F
7 r# @0 c% U2 F; |  c
1、当服务器刚加载ISA时,它会调用ISA提供的GetExtentionVersion( )来获得该ISA所需要的服务器版本,并与自己的版本相比较,以保证版本兼容。函数原型如下:
1 P( i6 i$ f9 @! Z
3 W# o* k3 p# H2 kBOOL WINAPI GetExtentionVersion(HSE_VERSION_INFO *version); " u' E2 H# @' T& N; W
typedef struct _HSE_VERSION_INFO
& [' r4 R% I/ m& A' Q{
( J  d; X+ B, U8 cDWORD dwExtensionVersion; //版本号
9 m+ \0 R+ O+ }( f2 G6 }$ Y9 uCHAR lpszExtensionDesc[HSE_MAX_EXT_DLL_NAME_LEN]; //关于ISA的描述字符串 3 P! d( S3 @1 @
} HSE_VERSION_INFO, *LPHSE_VERSION_INFO;
; B* T' O1 ^* Q& p4 o: Z$ c4 h4 |1 k2 r' b2 o2 v! c: B- r
2、ISA的真正入口是HttpExtentionProc( ),它相当于普通C程序的main( )函数,在这个函数中根据不同的客户请求作不同的处理。服务器和HttpExtentionProc( )之间是通过扩展控制块(Extention Control Block)来进行通信的,即ECB中存放入口参数和出口参数,包括服务器提供的几个回调函数的入口地址。函数原型如下:
5 E% X! V# Y; `; O8 z, t. S4 ]
" F9 |/ ^* }$ r8 N& CDWORD HttpExtensionProc( EXTENSION_CONTROL_BLOCK *pECB ); $ u% v% A: k% j: |( m. a( M$ z
8 {: S5 a5 V4 e% k
ECB的结构定义如下(IN表示入口参数,OUT表示出口参数):
3 Q/ Y/ F( t3 @3 j% K
! ?: R. w* N0 B5 O5 G4 Ptypedef struct _EXTENSION_CONTROL_BLOCK
* T; z9 p- A# F/ |' \{ ( [4 U" E; j' {5 b
DWORD cbSize; //IN,本结构的大小,只读
; f, _2 n6 n2 TDWORD dwVersion //IN,版本号,高16位为主版本号,低16位为次版本号 5 x( u, `: V  ]# ^1 f
HCONN ConnID; //IN,连接句柄,由服务器分配,ISA只能读取该值 3 ^% ?. R! g) _- l
DWORD dwHttpStatusCode; //OUT,当前完成的事务状态
" D' b" e( n  `( wCHAR lpszLogData[HSE_LOG_BUFFER_LEN]; //OUT,需要写入到日志文件中的内容 $ H2 {( x' w6 f
LPSTR lpszMethod; //IN,等价于CGI的环境变量REQUEST_METHOD 8 ^/ j, j) Z$ z; D' J
LPSTR lpszQueryString; //IN,等价于环境变量QUERY_STRING
8 ?5 H( S) e  M+ e; bLPSTR lpszPathInfo; //IN,等价于环境变量PATH_INFO
  h  s6 G; O2 P: nLPSTR lpszPathTranslated; //IN,等价于环境变量PATH_TRANSLATED
! {& l, U7 f) I. i) aDWORD cbTotalBytes; //IN,等价于环境变量CONTENT_LENGTH
' r9 F; R: D0 }3 XDWORD cbAvailable; //IN,缓冲区中的可用字节数
2 [* T/ T; c& W' |; e2 I) t% y$ RLPBYTE lpbData; //IN,缓冲区指针,指向客户端发来的数据 $ m$ G/ a7 S  J6 S, p6 c
LPSTR lpszContentType; //IN,等价于环境变量CONTENT_TYPE
9 ?+ S/ t: K/ `+ s6 o0 d
. Y2 H8 E3 U/ H( f, y1 y% B/ k//回调函数,用于返回服务器的连接信息或特定的服务器详细情况 4 j9 B! N; H! D( f# l
BOOL ( WINAPI * GetServerVariable )
8 L! g" }. g: J* R& }9 I8 G( HCONN hConn, # d" U9 K1 q$ `/ r) J& n
LPSTR lpszVariableName, / ~& ^  ?# G6 J5 y5 Z; f6 x% b
LPVOID lpvBuffer,
$ t5 [, y5 y0 [* t" P4 ?6 b! TLPDWORD lpdwSize ); ' z1 x4 S" ?% L% ~, N! o% ^: E
& q/ A% T% N6 R& z, ^' r( G
BOOL ( WINAPI * WriteClient ) //回调函数,从客户端的HTTP请求中读取数据 4 P' N# q5 o, t( o
( HCONN ConnID, + b, }' G' M0 z2 C$ i5 }6 v
LPVOID Buffer, " \4 K2 `+ Y4 s# O. a
LPDWORD lpdwBytes, : C7 |, b! F+ g/ s7 ]' y
DWORD dwReserved );
# N* e, \- d, z  x" u4 _% E# b, e4 M' O; g( N
BOOL ( WINAPI * ReadClient ) //回调函数,向客户端发送数据 9 R, d# P8 L- p+ ]
( HCONN ConnID,   X1 x* @) _, y. W3 ]
LPVOID lpvBuffer,
1 a" O7 e, H+ @$ X  u. oLPDWORD lpdwSize );
7 G* D/ ?' f" n# T7 j6 F5 v3 {4 S. ?& a. k) w
BOOL ( WINAPI * ServerSupportFunction ) //回调函数,访问服务器的一般和特定功能 * X) T1 S+ p/ o; J/ J, a
( HCONN hConn,
: l+ ^4 e: v8 U1 W) ?. mDWORD dwHSERRequest,
: h/ r" a$ n8 \% X# y- c8 v; KLPVOID lpvBuffer,
. v4 F: p0 x( n0 ^) b0 D) {1 dLPDWORD lpdwSize, 5 q4 N7 m0 W( s7 x  b1 s8 A
LPDWORD lpdwDataType );
6 q( w0 p; v6 V
% E1 x, D- }6 m0 f( i7 e0 g& Q} EXTENSION_CONTROL_BLOCK, *LPEXTENSION_CONTROL_BLOCK;
0 w1 U- t# U% |  U% L9 j' n2 S
* C+ m8 v, R9 t3 A9 N$ }) |在上述ECB中,服务器不但提供了当前HTTP连接的句柄和一些变量,而且提供了4个回调函数给ISA调用,从而使ISA可以获得更详尽的信息。
; B4 r3 w* L3 f9 W0 l* |2 P) E3 s; _6 \7 N1 ]) L5 K5 i
三、ISAPI Filter
, Q4 g) y" {- _0 `8 ?1 ~0 p$ w  ?  M9 N. q* Y: K' [0 V
ISAPI Filter位于服务器和客户端之间,能够对服务器和客户端之间的通信进行预处理和后处理,比如对通信进行加密/解密、提供对客户进行身份验证的新方法、提供自定义的日志记录等,在CGI中没有与ISAPI Filter直接相对应的部分。 ( \# ]+ P6 W) B- X( h, R
  p; \- u$ ?2 o' k
ISAPI Filter与服务器之间的接口有两个:GetFilterVersion( )和HttpFilterProc( )。任何 * D) ^2 c* u. T: @$ i$ T
ISAPI Filter都必须引出这两个函数以供服务器调用。
  ^) \+ h6 Z* f& Q6 n9 p# p3 Z/ y6 \6 w1 L0 h
1、在注册表的如下键值中存放着所有ISAPI Filter的文件名,IIS服务器启动时从该键值中获得
4 e' b1 e* D" c; E" eFilter的文件名并加载它们。
6 V6 y& N2 y/ `$ c5 I$ C1 S* u* v0 i0 M4 N5 e# V; s; F8 ], f9 i
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/W3SVC/Parameters/FilterDLL . y7 j% n4 s3 h( i
# ]7 Y* W/ \5 `9 W+ [7 B
2、然后服务器调用每个Filter提供的GetFilterVersion( )函数,获得版本号以及该Filter希望处理的事件,即ISAPI Filter通过引出GetFilterVersion( )函数来告知服务器自己希望处理什么类型的事件,因为ISAPI Filter是通过事件来激活的,当满足条件的事件到达时,服务器就会调用Filter引出的主函数HttpFilterProc( )对该事件进行处理。GetFilterVersion( )的原型如下: ' g% @* j; d9 X4 m5 H

' Y$ }+ S" A( c8 {$ ?# BBOOL WINAPI GetFilterVersion(
7 y' L. N' S1 h  ?) J( jDWORD dwServerFilterVersion; //IN,服务器使用的版本规范 5 m) ^0 \) T% A7 P
DWORD dwFilterVersion; //OUT,过滤器使用的版本规范
9 f: Z3 @1 C% ~) bCHAR lpszFilterDesc[SF_MAX_FILTER_DESC_LEN+1]; //OUT,对该过滤器的描述字符串 . k1 @3 h" [3 c" R+ L2 t; ?# Q6 @( s( p
DWORD dwFlags //OUT,事件和优先级标志 # k$ G. y8 m+ ?2 \& H! f7 x
);
; U5 F4 F" B* Q1 p8 t( @- P+ _( J9 P/ j
事件和优先级标志dwFlasg的取值在MSDN中有详细解释,其中包括该Filter被调用的优先级,一般应使用默认的低优先级,否则可能会对系统的性能造成很大影响。
6 V( p  y: o8 B2 n! a6 `( u1 G4 |, p" ]3 E; E
3、HttpFilterProc( )是ISAPI Filter主要的入口函数,它根据当前的事件的不同作出不同的处理。服务器通过如下的参数块和Filter进行交互,这个参数块的作用和ISA中的ECB类似。 " R3 K* h" Y2 C

/ ^( x1 R; }/ ]7 I; }4 ]% G! @typedef struct _HTTP_FILTER_CONTEXT , \2 {; {" G2 {7 f+ m
{ " M; e8 ]0 v1 ^1 ~& ]
' I' O% |7 V# }
DWORD cbSize; //IN,本参数块的大小
; H' z5 ]9 {; r+ k2 rDWORD Revision; //IN
8 y1 t& W( l* u/ X' [' o7 vPVOID ServerContext; //IN,由server使用本参数 ' q: J/ F# |7 U" n5 @/ t9 n2 n
DWORD ulReserved; //IN,由server使用本参数
3 ]1 B  \* r; a$ Y5 {: [8 q8 GBOOL fIsSecurePort; //IN,事件是否发生在安全端口上 / P: V5 e# m1 j  z
PVOID pFilterContext; //IN/OUT,与本次请求相关的上下文
9 h; J& T7 o. J0 \7 s
* H' I6 v2 x* J' W# w: U1 t* K3 r2 g//回调函数,取得关于服务器和本次连接的信息 5 B* X2 f  v. T8 ~6 c, l% w
BOOL (WINAPI * GetServerVariable) (
! w& X0 W' P' v  z  i  q' gstruct _HTTP_FILTER_CONTEXT * pfc,
1 y% s2 A$ M6 B' w4 }LPSTR lpszVariableName,
# k# r; K9 ^4 w2 e, r0 fLPVOID lpvBuffer, ! j' n. `" l' m8 {3 E7 A
LPDWORD lpdwSize
4 y, _. {% M" `9 W+ s);
+ Y( i6 d+ C3 [" s8 A' l! T
, P  D. v4 S) fBOOL (WINAPI * AddResponseHeaders) ( //回调函数,给HTTP响应添加一个标头
) u* q; B) A4 f2 F( Y3 Z3 @struct _HTTP_FILTER_CONTEXT * pfc, 0 C4 y/ R1 T1 I# w; y3 S
LPSTR lpszHeaders,
% Z% h) V( x5 ~- ]' Z, WDWORD dwReserved . y  I- I( h8 [
); " L, X5 ^) T( f+ J* T3 Q7 U. ~7 V
* t+ ~3 X0 Y( U$ ~
BOOL (WINAPI * WriteClient) ( //回调函数,将原始数据发送给客户端   k6 v1 ~0 x$ C- ?4 k6 L  [" r( R
struct _HTTP_FILTER_CONTEXT * pfc,
2 f+ b9 N( p) LLPVOID Buffer,
& s9 o1 ]; U4 m2 W9 g! cLPDWORD lpdwBytes, 2 D& m2 @: Z/ M3 j
DWORD dwReserved 8 _* T. V2 S* L0 y) F) }
);
) c! x9 X3 f* T! A% e8 L! W+ U1 C4 N2 a7 p4 X' {
VOID * (WINAPI * AllocMem) ( //回调函数,分配内存。
1 h. S2 l4 t9 W, q$ astruct _HTTP_FILTER_CONTEXT * pfc,
7 }8 Y4 s. Q! C: |  N( G; |; @, pDWORD cbSize, ! A. r5 ?" H/ T$ k7 w1 G$ z
DWORD dwReserved % x* v7 z' U) u9 M  _
); * z1 y" G1 X1 D; k2 {
, `3 r/ }& `$ O
BOOL (WINAPI * ServerSupportFunction) ( //回调函数,访问服务器的一般和特定功能 * @  V' a! P" t/ w$ E
struct _HTTP_FILTER_CONTEXT * pfc,
) a& m8 T: S. J! cenum SF_REQ_TYPE sfReq,   w# V+ w8 f9 d7 ?$ l, s
PVOID pData, 7 b4 l, s: N- g4 w/ c  T
DWORD ul1,
/ F. v9 D* }& ?DWORD ul2
* X. D9 I) H9 b8 v% {); ( K7 w. A& _4 y, G. r' I3 }
' x, a- o' q) e* ?- J
} HTTP_FILTER_CONTEXT, *PHTTP_FILTER_CONTEXT; % N# d  ?3 e% A
5 h$ v2 @* q+ }) L$ _
四、VC++ 6.0中对ISAPI的支持
0 C' {  {' @& g0 G1 y7 |9 g6 l: d& s5 P/ q# T
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实例,每个 : {3 K, o5 w! F4 Y
CHttpServerContext处理一个客户请求,这样可以处理并发的HTTP请求;CttpFilter和CHttpFilterContext之间的关系与此类似,在每个ISAPI Filter中只能有一个CHttpFilter实例,但是可以有多个CHttpFilterContext来处理并发的事件。CHttpServer和CHttpFilter是独立的类,它们可以共存于一个DLL中,也可以分别在不同的DLL中。
" e; h; h" t: y0 p
; \! D+ l$ a5 F; l一个ISA可以提供多个命令,每个命令对应于CHttpServer(或其子类)的一个成员函数,客户端可以在URL中指定命令名及其参数。在VC++ 6.0中是通过parse map来实现这种对应的。
3 Q" I; C- o- W4 s8 [# [9 n) u2 _7 \1 _' d* V6 S! G
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为例,该例中有下面这样一个表单: # [2 V/ d: W! e; w+ a0 j9 J
3 m, R+ W2 C) \+ i0 ~" a3 N
<form method=get action="pinball.dll?"> % A$ d3 S3 P' G4 j# J$ d
<input type="hidden" name="MfcISAPICommand" VALUE="GetImage"> ) k7 A2 D6 o& J  u
<input type="radio" name="Favorite" value="1" checked> Attack from Mars<br>
5 ^( D5 A. V  y<input type="radio" name="Favorite" value="2"> Twilight Zone<br>
/ {7 T* R4 s# D/ W<input type="radio" name="Favorite" value="3"> The Addams Family<br>
4 ^9 w* l+ d  e0 s2 j8 u& \! n<input type="radio" name="Favorite" value="4"> Cirqus Voltaire<br>
2 m, y) z4 g5 s4 b4 h9 l<input type="radio" name="Favorite" value="0"> I don't see it here<br> 7 N% U- [' R! X, f5 [
<br> " A' y. v! t9 J& j0 {4 n2 U
<input type="submit" value="Show Me!">
7 z7 v; ]8 S% _4 e</form> 3 z7 P" U1 U/ r2 S! |  I8 L1 E
8 j% k8 E" q  ?$ M9 g5 G  e% x
当客户端选中了上面的表单中的“Attack from Mars”这一项并点击了submit按钮后,服务器端
/ ?- F- [; N% o1 z最终将得到如下的URL串:
6 V2 n' o. k- `0 m2 w6 u' O  f7 s5 f$ D( H; @1 {: ]% V
http://www.abc.com/pinball.dll?M ... mage&Favorite=1 # x9 j! Q  S- z; {" R. n
1 A% X% p- [1 g2 u  a
在该URL串中,命令名是GetImage,参数Favorite的值是1,因此pinball.dll中的如下成员函数
! {& R7 L" D( o, c0 _将被调用以处理该请求,其中参数dwChoice对应URL中的参数Favorite:
3 {1 H7 |$ m+ L7 p/ q. y6 W
# `; V" P- H$ G7 w7 [% G- s- Yvoid CPinballExtension::GetImage(CHttpServerContext* pCtxt, long dwChoice); / r9 u( _7 `* Q! e8 U

- X2 l, x9 _) S% E' N* M而parse map需要按照下面的形式定义: * O% W( |6 a; L- {' {

  t7 u' ^1 S9 Z& _//CPinballExtension从CHttpServer派生而来
/ q0 V6 n  T' y5 ?0 }" u8 `+ JBEGIN_PARSE_MAP(CPinballExtension, CHttpServer)
2 Z6 |7 Z4 a! n" y5 u! p- G3 z5 r2 ]1 H" ^- R/ d( M$ `
//GetImage是CPinballExtension的成员函数,且有一个long型的参数即dwChoice . F) T' o" i6 B
ON_PARSE_COMMAND(GetImage, CPinballExtension, ITS_I4)
  D4 _. h: s( L" m# ]! S5 ~9 D  {5 u/ }8 ~0 J4 _
//该参数在URL中的名字为Favorite
1 U8 C9 O5 a; O3 z0 zON_PARSE_COMMAND_PARAMS("Favorite")
- A3 g  ~( S1 I0 _8 U* Q% X: f6 u9 w+ H( ]6 n" X+ ]; {
END_PARSE_MAP(CPinballExtension)
% `& Q: z- d/ H) {5 }4 E) \+ z/ k3 J/ n, S1 p
而对于ISAPI Filter,在VC中可以通过重载CHttpFilter(或其子类)的不同的成员函数来实现对不同事件的处理。可重载的函数如下,每一个成员函数均对应一个或多个事件:
* ], d$ ?8 {' R" |0 E6 J4 \, t8 a" Z" _9 Y  K
OnPreprocHeaders
5 V  f# K2 O) X! t* l4 M* _OnAuthentication
+ O+ a/ N2 Z4 c+ ~. b6 o+ }OnUrlMap 1 ?* L9 C- E4 M4 f
OnSendRawData : A& ~, D* W/ L& E; m( `4 v
OnReadRawData
# L6 @% z/ j, D0 }, O- AOnLog
0 p. z9 ]& i& I% g- p8 h, }OnEndOfNetSession
& D; g6 h1 s+ [3 {/ l1 R; J4 k' ]3 t, Q! b* e8 w, `
MSDN提供了4个关于ISAPI的编程实例:counter、MFCUCASE、pinball、wwwquote,有兴趣的可看看,本文主要不是介绍编程,所以不再赘述。
5 z. |5 w: \/ [  N
% z' Y6 J! F7 l; a' F参考资料:
/ k8 o. P3 A# z2 U! t; j) W( c4 q2 G6 ~. H
1、MSDN 0 R3 s7 m. ?4 B& e/ C
2、《精通CGI编程》,丁一强等,清华大学出版社
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2025-6-19 08:59 , Processed in 0.016559 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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