找回密码
 注册
搜索
查看: 4155|回复: 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中的实现。
6 o' e' k8 V; q3 O" ^! b2 {" {' T" X
一、ISAPI接口和CGI接口的不同。 0 m' ], _1 X. w0 N1 _+ @9 B, ?; @, ?5 H
$ m/ B- p0 ?8 |+ }& i
ISAPI程序和CGI程序完成类似的功能,但是实现方法不同。 $ A7 W' Z; ?2 `' d
; u& v  [4 [, {3 k# M
1、ISAPI程序以DLL形式被Web服务器加载到自己的进程空间中,因此和服务器共用同一个地址空间,且在没有客户请求时可以将其从内存中卸载;而对客户端发来的每个对CGI程序的请求则需要服务器为它单独启动一个进程,这需要耗费大量的时间和内存。当并发的请求数目很大时,使用CGI在效率上不如ISAPI。 ' S) X. j1 Q3 z: X9 d
& J! g" N2 ]4 E1 ~' f0 _
2、CGI程序通过环境块和标准输入输出与Web服务器进行通信,而ISAPI程序与服务器结合得更为紧密,与服务器共享同一个进程上下文,主要通过一个参数块与服务器进行交互,可以从服务器那里获得关于当前HTTP连接的大量信息。 $ ]4 i6 C. d& b  o
/ w# [8 ~3 y' ]) H& L, O1 X
ISAPI主要分为ISA和ISAPI Filter两部分。ISA方法相对而言要传统一些,利用一些特殊的链接,指向服务器的作业,供程序开发人员设计一些扩展功能;而ISAPI过滤器则倾向于构造服务器直接调用的模块,提供一种无缝链接部件用于监测直接来自于服务器的HTTP请求。
/ a: @) o3 _: ?* R' y
. i: h; `# E( I+ N2 V+ e$ s8 O* @9 m( W
二、ISA 5 V5 z) [/ t6 B  P

& D+ `3 E7 Q  K/ Z+ y1 JISA(Internet Server Application)也可称为ISAPI DLL,其功能和CGI程序的功能直接相对应,使用方法和CGI也类似,由客户端在URL中指定其名称而激活。例如下面的请求将调用服务器的虚拟可执行目录Scripts下的function.dll(ISAPI DLL必须放在服务器的虚拟可执行目录下): ) q% w. t# S/ x7 q
http://www.abc.com/Scripts/function.dll? & ^5 `5 m: E6 |; t

" w+ g& x4 l4 t; c: J0 e1 {ISA和服务器之间的接口主要有两个:GetExtentionVersion( )和HttpExtentionProc( )。任何ISA都必须在其PE文件头的引出表中定义这两个引出函数,以供Web服务器在适当的时候调用。
& h( {9 A- d7 N
) V9 [7 G5 M# X# W. `* N/ [1、当服务器刚加载ISA时,它会调用ISA提供的GetExtentionVersion( )来获得该ISA所需要的服务器版本,并与自己的版本相比较,以保证版本兼容。函数原型如下: * c& Z0 w; ?9 ?- A1 [

- H; u9 P6 o) ]; f4 JBOOL WINAPI GetExtentionVersion(HSE_VERSION_INFO *version);
5 v2 u- `7 ^0 o4 F6 [8 Jtypedef struct _HSE_VERSION_INFO 4 F; G. L) r% w( D
{
8 k. q9 O' X. o  d+ j% e- r: cDWORD dwExtensionVersion; //版本号 4 V% Q7 L2 l$ w2 ]5 |
CHAR lpszExtensionDesc[HSE_MAX_EXT_DLL_NAME_LEN]; //关于ISA的描述字符串
+ Y9 T5 n1 j$ m" R. L% v- U& K} HSE_VERSION_INFO, *LPHSE_VERSION_INFO; ( X, m+ B4 x) p# u

. o) J* x* v$ Q% A" b6 E  t4 {2、ISA的真正入口是HttpExtentionProc( ),它相当于普通C程序的main( )函数,在这个函数中根据不同的客户请求作不同的处理。服务器和HttpExtentionProc( )之间是通过扩展控制块(Extention Control Block)来进行通信的,即ECB中存放入口参数和出口参数,包括服务器提供的几个回调函数的入口地址。函数原型如下: 3 n* }% J2 T7 l

! ~0 H7 x3 X' WDWORD HttpExtensionProc( EXTENSION_CONTROL_BLOCK *pECB ); * Q8 @- C9 w( D; |: b8 x8 \

5 m- Q; K! U' J  R- |8 pECB的结构定义如下(IN表示入口参数,OUT表示出口参数): " L6 p+ f, a, C2 \( n* ~% C6 ~

4 n' f9 i1 {- W0 O& i1 mtypedef struct _EXTENSION_CONTROL_BLOCK
, O6 i4 Q+ m4 b4 @! ~{
1 v5 }  j# {$ A2 X9 @2 W. U8 I7 Z/ VDWORD cbSize; //IN,本结构的大小,只读
/ ^/ u8 C& g  w, x6 _8 DDWORD dwVersion //IN,版本号,高16位为主版本号,低16位为次版本号
& `. A2 @" @4 |HCONN ConnID; //IN,连接句柄,由服务器分配,ISA只能读取该值 " V& J- K4 m! v7 Q# f4 P
DWORD dwHttpStatusCode; //OUT,当前完成的事务状态
0 z2 p* H9 u+ C2 J6 V' LCHAR lpszLogData[HSE_LOG_BUFFER_LEN]; //OUT,需要写入到日志文件中的内容 $ p+ Z' z) I+ R- B3 \( H" F; z/ ]
LPSTR lpszMethod; //IN,等价于CGI的环境变量REQUEST_METHOD 6 S* B. }9 H- I3 d2 @
LPSTR lpszQueryString; //IN,等价于环境变量QUERY_STRING 3 M3 t  _8 N9 f9 {% z
LPSTR lpszPathInfo; //IN,等价于环境变量PATH_INFO 6 I0 s3 d  x5 a# t, ?
LPSTR lpszPathTranslated; //IN,等价于环境变量PATH_TRANSLATED + [2 q/ }! v& W1 D/ t+ n  g4 Q
DWORD cbTotalBytes; //IN,等价于环境变量CONTENT_LENGTH 9 _: k  V6 i( Z6 d, k. W
DWORD cbAvailable; //IN,缓冲区中的可用字节数
  J. u+ I: K) R, X" L  V) H$ g6 mLPBYTE lpbData; //IN,缓冲区指针,指向客户端发来的数据
8 r+ [  w' q& e9 HLPSTR lpszContentType; //IN,等价于环境变量CONTENT_TYPE
) N6 V/ Z# w* s7 w% u
. u5 F4 |# O( L5 x' Z//回调函数,用于返回服务器的连接信息或特定的服务器详细情况 2 ?) F9 m% Y7 x$ P; E
BOOL ( WINAPI * GetServerVariable )
3 O% g& W9 ^7 U3 F, J( HCONN hConn,
0 h3 }) Z! [6 U6 E: jLPSTR lpszVariableName,
, o" _( K7 n# J3 J2 ULPVOID lpvBuffer,
- \- Q" [- }9 x! ?6 K+ fLPDWORD lpdwSize ); 6 O8 h5 c5 g$ W6 i

3 G8 F" ~8 L' Q4 PBOOL ( WINAPI * WriteClient ) //回调函数,从客户端的HTTP请求中读取数据
) e6 [% n9 Z' b0 v$ ~4 d2 b* X/ b, ]( HCONN ConnID,
. G5 T& S0 x% z; t$ LLPVOID Buffer,
" F  W/ q% B$ z$ mLPDWORD lpdwBytes, * I3 @* K7 N! @4 k2 `& b
DWORD dwReserved );
6 Z3 U' I5 L7 k( T2 T) @" k: @& q& Q# Y! ]  j5 E# D. d. D
BOOL ( WINAPI * ReadClient ) //回调函数,向客户端发送数据
; `8 W) s8 Z% t+ B- @( HCONN ConnID,
8 n, T$ G$ ^! S* j5 g; S0 ^LPVOID lpvBuffer,
; j; r( Y0 ?/ [0 T  H: V; x! fLPDWORD lpdwSize );
2 L0 L' ^; |. Q" Z* \& p) K  e, n" v6 K: T- J% Z7 H
BOOL ( WINAPI * ServerSupportFunction ) //回调函数,访问服务器的一般和特定功能
* |3 S2 C* M: X5 y( ~% x  A( HCONN hConn, ( y% u* V6 R& g& F
DWORD dwHSERRequest,
+ R3 c" ~# `% E5 T+ U$ z8 fLPVOID lpvBuffer,
* q) O5 \) ]% ]: m2 ?0 f: a3 KLPDWORD lpdwSize,
+ Y0 _$ b8 [3 q  }" V0 lLPDWORD lpdwDataType );
7 k% S! S5 }# h& q6 y8 r6 s
, O4 E# v* t7 p6 _& t- w} EXTENSION_CONTROL_BLOCK, *LPEXTENSION_CONTROL_BLOCK; : ?6 {3 E" t6 E# C' n
: A. i" s; I' t; y; ?3 N! I
在上述ECB中,服务器不但提供了当前HTTP连接的句柄和一些变量,而且提供了4个回调函数给ISA调用,从而使ISA可以获得更详尽的信息。
% ?! B# e! C9 `* I
3 d9 Y0 }; p# ~) x5 a三、ISAPI Filter
9 s, U- E. Y) F/ R$ r& j. m) b; f, H% ~8 `1 X
ISAPI Filter位于服务器和客户端之间,能够对服务器和客户端之间的通信进行预处理和后处理,比如对通信进行加密/解密、提供对客户进行身份验证的新方法、提供自定义的日志记录等,在CGI中没有与ISAPI Filter直接相对应的部分。 ) X6 o. G6 b6 i% \- j- T

9 P" b. _1 z9 HISAPI Filter与服务器之间的接口有两个:GetFilterVersion( )和HttpFilterProc( )。任何 % R2 w/ g) d" H0 F7 v* }7 ?9 p  L
ISAPI Filter都必须引出这两个函数以供服务器调用。
' h3 K3 O5 ~9 Z& Y8 p
, Z$ g  c, ]4 {, K: O! t- @) ~1、在注册表的如下键值中存放着所有ISAPI Filter的文件名,IIS服务器启动时从该键值中获得 : A1 j+ J9 l! f* S
Filter的文件名并加载它们。 " E- ^( P3 c/ c) x0 n! W' ?- o

& B9 M+ @+ y2 D6 p4 P5 oHKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/W3SVC/Parameters/FilterDLL
  Y: @. q. w6 y7 }
7 d+ J' ]7 F. j8 H$ [7 \. _- [" ^2、然后服务器调用每个Filter提供的GetFilterVersion( )函数,获得版本号以及该Filter希望处理的事件,即ISAPI Filter通过引出GetFilterVersion( )函数来告知服务器自己希望处理什么类型的事件,因为ISAPI Filter是通过事件来激活的,当满足条件的事件到达时,服务器就会调用Filter引出的主函数HttpFilterProc( )对该事件进行处理。GetFilterVersion( )的原型如下:
1 \5 D2 J3 h6 V$ r7 ?+ m1 i6 [7 W* w4 H. y2 d, l3 ?
BOOL WINAPI GetFilterVersion(
" Y4 g; W7 [" O% `  n" X5 \DWORD dwServerFilterVersion; //IN,服务器使用的版本规范 " X& F, @! p4 ?7 T4 K+ H
DWORD dwFilterVersion; //OUT,过滤器使用的版本规范
# X+ \% O  x' x! v( fCHAR lpszFilterDesc[SF_MAX_FILTER_DESC_LEN+1]; //OUT,对该过滤器的描述字符串 " Q% M' x/ v+ i0 {, |! b) ^+ v
DWORD dwFlags //OUT,事件和优先级标志 " d) Z7 i0 b9 N4 G# X5 }
); + g# E  v  `% A; c+ R

2 M7 I6 g$ A1 ~- m9 u  f事件和优先级标志dwFlasg的取值在MSDN中有详细解释,其中包括该Filter被调用的优先级,一般应使用默认的低优先级,否则可能会对系统的性能造成很大影响。
* A1 W5 t+ V! I) ]" m9 @2 y0 A, P& t5 y  S" J* c* n$ D
3、HttpFilterProc( )是ISAPI Filter主要的入口函数,它根据当前的事件的不同作出不同的处理。服务器通过如下的参数块和Filter进行交互,这个参数块的作用和ISA中的ECB类似。
6 \1 Q9 T- i6 a$ T6 {! X/ ]3 F- A. J& M7 N4 Q( H9 J; N0 T
typedef struct _HTTP_FILTER_CONTEXT
5 y; u7 O% C% l9 T  z0 X{
  u, ?1 y( C& N4 F6 w2 \, C! d# k; N$ \2 ]7 i4 x' Q+ ?
DWORD cbSize; //IN,本参数块的大小 % [5 w+ O4 ]# e7 h; H# D
DWORD Revision; //IN ! ^# G) j1 V$ L- J) a2 z( Z
PVOID ServerContext; //IN,由server使用本参数
5 X1 R) o0 D; Z( h, T6 {$ K  qDWORD ulReserved; //IN,由server使用本参数 6 {6 S" A4 a6 P$ K6 F
BOOL fIsSecurePort; //IN,事件是否发生在安全端口上 2 q" F% L: I# r$ G. @+ Q% [
PVOID pFilterContext; //IN/OUT,与本次请求相关的上下文
4 i4 S5 {  E+ M5 n! ]& s% {. p! s; o- W3 N& r6 l! `. Z5 ^
//回调函数,取得关于服务器和本次连接的信息
/ }, h; q. F4 ?3 T" I$ eBOOL (WINAPI * GetServerVariable) (
( ^6 n$ [9 H9 F/ h) T( d, P) astruct _HTTP_FILTER_CONTEXT * pfc, ; }# Q+ Y/ q  _) w( Y- J' Q5 f
LPSTR lpszVariableName,
/ b4 H  c/ T/ e8 \7 S1 H/ ?LPVOID lpvBuffer,
* J! w* _% s. [3 b' `7 fLPDWORD lpdwSize
. w& ~( O+ y) Z! M2 Q+ J);
5 y$ ~8 y* Q5 v0 p7 E
! p, {8 O8 o4 |BOOL (WINAPI * AddResponseHeaders) ( //回调函数,给HTTP响应添加一个标头
7 ?: n& D+ O, O5 Bstruct _HTTP_FILTER_CONTEXT * pfc,
: F" ?5 T" Z! A/ PLPSTR lpszHeaders, / c/ A( B, X, K) {( C2 L; E& t
DWORD dwReserved
0 g& H3 [# J" E+ X" o( I4 S3 ?4 |);
5 A# P) c% Q. ~$ Z( k
; D/ W6 `" L% F$ NBOOL (WINAPI * WriteClient) ( //回调函数,将原始数据发送给客户端
; u+ y6 |2 U& D0 k- g& i, y: r! w' estruct _HTTP_FILTER_CONTEXT * pfc,
+ Z# m9 G$ G+ K% QLPVOID Buffer, : i3 ?: J9 F0 U2 n$ X: V+ v
LPDWORD lpdwBytes, . a0 Z# }: g% h- K! x' \
DWORD dwReserved 8 @; j5 Y% P( V. \* f+ @! d
);
* m. ~* v# b/ ~: t! a4 w/ s, c% s* ?6 ^0 V2 \
VOID * (WINAPI * AllocMem) ( //回调函数,分配内存。 * `. Z8 g  g; A8 D3 L9 K" z  N4 H
struct _HTTP_FILTER_CONTEXT * pfc,
* ^- P  F9 w; T$ l# Z( @DWORD cbSize,
' Y# G1 [. Y9 FDWORD dwReserved / s' Y! W3 w0 X, ?. b  i. s
);
+ E1 u' g8 @: \8 @* l$ U6 d; j' i
BOOL (WINAPI * ServerSupportFunction) ( //回调函数,访问服务器的一般和特定功能
- e4 @8 r7 g8 j; R" t8 ?0 Q  `struct _HTTP_FILTER_CONTEXT * pfc, * t' E" ^& T* `' s( O* S7 `/ V3 U- ?
enum SF_REQ_TYPE sfReq, 5 y/ P) t8 K* U: n
PVOID pData,
4 U- k8 M$ F1 ?0 G1 Y3 t0 |DWORD ul1, 4 N% [/ l: S# g6 z0 v
DWORD ul2
5 [! f6 b- `5 Q/ v0 y);
( q/ o  z/ a" a3 b' ?
+ _; V2 m: g3 j6 U} HTTP_FILTER_CONTEXT, *PHTTP_FILTER_CONTEXT; ! Y/ }& W: q0 ~6 Z. ~8 i" G1 c3 T2 n
; O' C1 Y, V5 a* D; [: a* S) u
四、VC++ 6.0中对ISAPI的支持 6 _- [7 U$ T4 f! U9 ]( M9 V
/ u4 B6 x; L; y* v$ n' {
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实例,每个
+ p) ^& w3 i( j9 WCHttpServerContext处理一个客户请求,这样可以处理并发的HTTP请求;CttpFilter和CHttpFilterContext之间的关系与此类似,在每个ISAPI Filter中只能有一个CHttpFilter实例,但是可以有多个CHttpFilterContext来处理并发的事件。CHttpServer和CHttpFilter是独立的类,它们可以共存于一个DLL中,也可以分别在不同的DLL中。 7 l" l5 s2 B( H3 g' v% c

; a8 L+ j% Y# e3 O: u3 s' n% j' }0 B一个ISA可以提供多个命令,每个命令对应于CHttpServer(或其子类)的一个成员函数,客户端可以在URL中指定命令名及其参数。在VC++ 6.0中是通过parse map来实现这种对应的。 * s4 R2 u# a4 T
. R& E% z8 Q& U& R3 O3 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为例,该例中有下面这样一个表单: ; D- \) _% s8 `+ H) a! j8 g

! k# \7 b" |+ t<form method=get action="pinball.dll?">
$ ?+ g; x' I( u$ E; A& V" J<input type="hidden" name="MfcISAPICommand" VALUE="GetImage"> 6 f" G8 }. c+ q/ w3 A0 n* {+ R
<input type="radio" name="Favorite" value="1" checked> Attack from Mars<br> 2 G9 {" K# v- [4 K5 Z9 y
<input type="radio" name="Favorite" value="2"> Twilight Zone<br> 5 P& u" K* C0 Z
<input type="radio" name="Favorite" value="3"> The Addams Family<br>
9 e! l# k3 J+ j! Y# t9 [1 P<input type="radio" name="Favorite" value="4"> Cirqus Voltaire<br> 9 L% t3 {9 D+ p. N
<input type="radio" name="Favorite" value="0"> I don't see it here<br> 8 @. e3 a- a% I" i
<br>
$ V1 {) i9 N$ T  @, \1 i; o. R<input type="submit" value="Show Me!"> ( ?2 j: r8 V5 Z2 I
</form> & f' t. l. u' f: N% U1 V4 J# ]
" U( n' J9 `) t1 u# h5 a" k
当客户端选中了上面的表单中的“Attack from Mars”这一项并点击了submit按钮后,服务器端 4 Y0 H# U5 c$ u" @
最终将得到如下的URL串:
* k6 ?1 B% K! Y4 H% W! }  S; M8 V1 m1 j! P2 H
http://www.abc.com/pinball.dll?M ... mage&Favorite=1
( G6 c1 ~( K# ]: i: a" r7 I) V1 K  K* O  d/ _+ e8 ?1 \
在该URL串中,命令名是GetImage,参数Favorite的值是1,因此pinball.dll中的如下成员函数 . ^% l! j6 v) Z3 O* R/ ~
将被调用以处理该请求,其中参数dwChoice对应URL中的参数Favorite: 8 e5 Q0 P& a9 [0 g
" H2 f7 j" u) Z/ p$ c* u0 Q
void CPinballExtension::GetImage(CHttpServerContext* pCtxt, long dwChoice);
$ [" U* a/ @  {# J/ b7 T' Q0 z. b2 Y  t
而parse map需要按照下面的形式定义: + n$ |0 M# q- y4 S$ Y9 A( u

, G5 B3 G( @" R  Y2 A% w+ `//CPinballExtension从CHttpServer派生而来
$ r+ b/ R1 I- d" R/ z7 tBEGIN_PARSE_MAP(CPinballExtension, CHttpServer)
4 M4 F6 y( X5 Y% k  \( h) [
3 N/ p7 H! d+ s7 o! H//GetImage是CPinballExtension的成员函数,且有一个long型的参数即dwChoice
& q0 Q  Z% T! B# b  a7 v* Q1 l/ \& tON_PARSE_COMMAND(GetImage, CPinballExtension, ITS_I4) . a3 X2 f, t0 m7 j* U  a

6 n9 A: g& c) H) l4 w//该参数在URL中的名字为Favorite
6 J, ?7 P- w( g# R/ RON_PARSE_COMMAND_PARAMS("Favorite")
9 t  ]4 c; V6 P# m2 L0 B7 d4 [) M& g9 B' z$ u( H/ e
END_PARSE_MAP(CPinballExtension) $ Z" J" p1 {( E9 I

& B0 u: K  u5 H; K而对于ISAPI Filter,在VC中可以通过重载CHttpFilter(或其子类)的不同的成员函数来实现对不同事件的处理。可重载的函数如下,每一个成员函数均对应一个或多个事件:
* ^' d( s( F6 q. c. d  H
7 v' y% ~4 {: N2 ^OnPreprocHeaders
1 b2 ~6 ^3 j1 z+ X9 bOnAuthentication
, W/ u- R, c$ r4 hOnUrlMap * ~+ `$ R8 S+ i" d# I; D4 b& F
OnSendRawData
1 }: R+ m* |, ]. s" b& QOnReadRawData
5 z% D# y! @% p6 [, BOnLog
0 _7 D9 Y: s2 _0 ~5 x; jOnEndOfNetSession 9 o' A( e$ ~/ o

9 _" g3 j( m5 B5 ~+ ZMSDN提供了4个关于ISAPI的编程实例:counter、MFCUCASE、pinball、wwwquote,有兴趣的可看看,本文主要不是介绍编程,所以不再赘述。 ; X, e' {4 m/ ^# k
% z% ]# g: w5 x
参考资料:
5 W7 _# _4 p) ]0 y" d% O* I0 k, ]* F( E6 f5 b5 h+ Y; Y
1、MSDN * y: E$ q+ X" K& ~  L
2、《精通CGI编程》,丁一强等,清华大学出版社
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2025-11-14 16:23 , Processed in 0.017723 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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