找回密码
 注册
搜索
查看: 4118|回复: 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中的实现。 * s& \( Y: G" _$ x8 m
. B8 ~5 g5 ]2 e2 @
一、ISAPI接口和CGI接口的不同。
+ j1 v7 P- D( f8 `: V2 O
" G9 f- s) y2 Y  |" y( X4 I2 v# u5 ]ISAPI程序和CGI程序完成类似的功能,但是实现方法不同。
& w9 N  ~7 w! t
$ w; i9 D5 d5 Y2 E0 _3 K( j1、ISAPI程序以DLL形式被Web服务器加载到自己的进程空间中,因此和服务器共用同一个地址空间,且在没有客户请求时可以将其从内存中卸载;而对客户端发来的每个对CGI程序的请求则需要服务器为它单独启动一个进程,这需要耗费大量的时间和内存。当并发的请求数目很大时,使用CGI在效率上不如ISAPI。 " g8 N- s) v4 W( h

* f# _. {3 [. d: b3 y# J& X. L2、CGI程序通过环境块和标准输入输出与Web服务器进行通信,而ISAPI程序与服务器结合得更为紧密,与服务器共享同一个进程上下文,主要通过一个参数块与服务器进行交互,可以从服务器那里获得关于当前HTTP连接的大量信息。 8 c: L* p8 v- J6 N
2 t" `' P  U: x& x: H" d
ISAPI主要分为ISA和ISAPI Filter两部分。ISA方法相对而言要传统一些,利用一些特殊的链接,指向服务器的作业,供程序开发人员设计一些扩展功能;而ISAPI过滤器则倾向于构造服务器直接调用的模块,提供一种无缝链接部件用于监测直接来自于服务器的HTTP请求。
; w# _% [# U( G
, L  i& _8 b3 a, A
1 k, o; l9 d4 D3 ^$ K二、ISA ; ?3 f; x! k7 G! i% s  ~
- v0 V" W# C* _% j
ISA(Internet Server Application)也可称为ISAPI DLL,其功能和CGI程序的功能直接相对应,使用方法和CGI也类似,由客户端在URL中指定其名称而激活。例如下面的请求将调用服务器的虚拟可执行目录Scripts下的function.dll(ISAPI DLL必须放在服务器的虚拟可执行目录下): # c+ g# h6 i" J$ R# A
http://www.abc.com/Scripts/function.dll?
6 R. ]( k& `$ _
5 O0 p; `9 G  O. J3 xISA和服务器之间的接口主要有两个:GetExtentionVersion( )和HttpExtentionProc( )。任何ISA都必须在其PE文件头的引出表中定义这两个引出函数,以供Web服务器在适当的时候调用。
2 X. A* [' ~# f$ k0 ?% Y) a: j
) W( F' q/ ?! ?, P" b; ?0 S$ B# n1、当服务器刚加载ISA时,它会调用ISA提供的GetExtentionVersion( )来获得该ISA所需要的服务器版本,并与自己的版本相比较,以保证版本兼容。函数原型如下: 0 w0 U6 o' W, T  y
4 T! z5 S& Q8 C* Y: y; a8 s( z5 [8 k
BOOL WINAPI GetExtentionVersion(HSE_VERSION_INFO *version); . f- j# v8 `  {- p6 g4 V' X
typedef struct _HSE_VERSION_INFO $ W0 w- P9 @' {6 T' S; e. F3 k
{ 5 E. s6 {) h0 p8 g" ]
DWORD dwExtensionVersion; //版本号 * ]9 z9 g) j9 G, \) N, r
CHAR lpszExtensionDesc[HSE_MAX_EXT_DLL_NAME_LEN]; //关于ISA的描述字符串
- r! r4 [5 K: I0 ]6 _2 _} HSE_VERSION_INFO, *LPHSE_VERSION_INFO;
* g8 t$ s3 A4 U- Q( L  R8 H1 |4 H- H  D$ e' s  v) x+ X
2、ISA的真正入口是HttpExtentionProc( ),它相当于普通C程序的main( )函数,在这个函数中根据不同的客户请求作不同的处理。服务器和HttpExtentionProc( )之间是通过扩展控制块(Extention Control Block)来进行通信的,即ECB中存放入口参数和出口参数,包括服务器提供的几个回调函数的入口地址。函数原型如下:
8 U( d: Z5 V! H& K* h; A# Z, R: U* k/ R
DWORD HttpExtensionProc( EXTENSION_CONTROL_BLOCK *pECB );
! V) c; c# l1 f7 d. I$ ?) X
% D' d6 M2 P; cECB的结构定义如下(IN表示入口参数,OUT表示出口参数):
" ]- D1 F, d$ V# j# Z! O
: o. }( ]9 B' m4 [9 L! [1 B8 w0 Gtypedef struct _EXTENSION_CONTROL_BLOCK
- N' m6 O& Z; N; ]: g) i{ # X! f, S3 r; }8 h  j
DWORD cbSize; //IN,本结构的大小,只读
) f3 F. W' p$ }6 CDWORD dwVersion //IN,版本号,高16位为主版本号,低16位为次版本号
9 t3 {+ d9 {3 EHCONN ConnID; //IN,连接句柄,由服务器分配,ISA只能读取该值
9 }+ s) z1 l2 K* b2 rDWORD dwHttpStatusCode; //OUT,当前完成的事务状态
8 Q* }' v: J' ^% \5 CCHAR lpszLogData[HSE_LOG_BUFFER_LEN]; //OUT,需要写入到日志文件中的内容
0 r( o; R: z* w8 [, tLPSTR lpszMethod; //IN,等价于CGI的环境变量REQUEST_METHOD . U; @; J8 ^8 m8 z
LPSTR lpszQueryString; //IN,等价于环境变量QUERY_STRING 7 |7 k% v: e$ P3 U3 m: B+ [
LPSTR lpszPathInfo; //IN,等价于环境变量PATH_INFO
5 V8 E! d# D) R, i+ |# M  O3 CLPSTR lpszPathTranslated; //IN,等价于环境变量PATH_TRANSLATED
0 L3 c; J, L2 z  WDWORD cbTotalBytes; //IN,等价于环境变量CONTENT_LENGTH
5 n' m, N6 ?- ]: M# `0 jDWORD cbAvailable; //IN,缓冲区中的可用字节数
. f( C8 [: q% M. E3 uLPBYTE lpbData; //IN,缓冲区指针,指向客户端发来的数据
/ b0 {" \4 u* G9 d* }# fLPSTR lpszContentType; //IN,等价于环境变量CONTENT_TYPE $ {% a1 m9 R0 l+ k: B& N0 k7 {: q
* F, S. I7 p: u  }, z4 ~8 ^
//回调函数,用于返回服务器的连接信息或特定的服务器详细情况 , {( r6 U; z; s3 S
BOOL ( WINAPI * GetServerVariable ) 0 C8 R8 _8 K( T( B% o5 C
( HCONN hConn, - K& Z* e4 e# r
LPSTR lpszVariableName,
: V6 p" }" F0 T. l; VLPVOID lpvBuffer,   Z0 `) b7 @- b
LPDWORD lpdwSize ); ! J  k* V' m) M# [, J# K

, t3 A  n, G- p7 f( h: XBOOL ( WINAPI * WriteClient ) //回调函数,从客户端的HTTP请求中读取数据   g' {+ _% \" x# v& n3 i
( HCONN ConnID,
; K: ], @7 f3 ^. W( P0 ELPVOID Buffer, ' x% A; G3 G3 F
LPDWORD lpdwBytes,
& T. F4 s0 ~9 `% E: _' o. f7 eDWORD dwReserved );
" h2 R- @  E; y8 {
6 ^5 j; c7 Q  G. ^2 [( s) pBOOL ( WINAPI * ReadClient ) //回调函数,向客户端发送数据 8 [( E) A$ Z: Z* {& E: G6 j- h
( HCONN ConnID, ' f2 m: Y% {2 A
LPVOID lpvBuffer,
# _5 N% W4 K2 S4 s+ k( DLPDWORD lpdwSize ); # W4 U9 D. T1 R4 N) a
5 Y* M$ U* ^+ P/ i8 o6 ^; T
BOOL ( WINAPI * ServerSupportFunction ) //回调函数,访问服务器的一般和特定功能
+ K" ?8 h- H3 U2 K* {% I( HCONN hConn, : r9 K8 [& ~  y3 T  \1 h$ F
DWORD dwHSERRequest,
0 B1 j8 X2 X+ f0 H* v  ULPVOID lpvBuffer,
( a) G5 g& ~# U$ jLPDWORD lpdwSize,
7 c$ Z: N0 s. Y/ E0 P7 A1 l7 }) ]LPDWORD lpdwDataType );
: O8 g' N( r/ k& V' y0 N+ n% A
$ c" h6 _, K$ i0 g' M+ b+ r} EXTENSION_CONTROL_BLOCK, *LPEXTENSION_CONTROL_BLOCK; ! U: v6 ^) r# ^) g5 R3 ]! F
. a* W2 c- c3 _. X4 N
在上述ECB中,服务器不但提供了当前HTTP连接的句柄和一些变量,而且提供了4个回调函数给ISA调用,从而使ISA可以获得更详尽的信息。
3 w3 T$ \# J/ |3 _! s( v+ Q
* ~) `& Q0 C1 g2 |0 \7 o6 _0 D3 H: \) P三、ISAPI Filter
0 [2 N6 W9 b" S
) j; T" i. A  h* c. L+ w; EISAPI Filter位于服务器和客户端之间,能够对服务器和客户端之间的通信进行预处理和后处理,比如对通信进行加密/解密、提供对客户进行身份验证的新方法、提供自定义的日志记录等,在CGI中没有与ISAPI Filter直接相对应的部分。 ' u& L4 W5 t; w' W4 Q7 u; W

  X0 ~/ |3 x" z6 d  n/ UISAPI Filter与服务器之间的接口有两个:GetFilterVersion( )和HttpFilterProc( )。任何
/ {7 \0 B2 D$ b7 G4 O, K: D2 ZISAPI Filter都必须引出这两个函数以供服务器调用。 : F  P+ P. h# R

2 b3 c" G5 m5 H7 N* w* p1、在注册表的如下键值中存放着所有ISAPI Filter的文件名,IIS服务器启动时从该键值中获得
$ {- r- N+ F( d6 eFilter的文件名并加载它们。
2 r) c$ n9 I4 |$ \4 D* d" r
' e, q$ J; @5 V9 YHKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/W3SVC/Parameters/FilterDLL
! \+ c. l: G; e8 S
# r1 o9 J- y7 F5 j5 P  ^2、然后服务器调用每个Filter提供的GetFilterVersion( )函数,获得版本号以及该Filter希望处理的事件,即ISAPI Filter通过引出GetFilterVersion( )函数来告知服务器自己希望处理什么类型的事件,因为ISAPI Filter是通过事件来激活的,当满足条件的事件到达时,服务器就会调用Filter引出的主函数HttpFilterProc( )对该事件进行处理。GetFilterVersion( )的原型如下:
( d7 S! z, D" v  `! J# l* r
( q+ J6 }& t$ U% W: A% F4 yBOOL WINAPI GetFilterVersion( ' R# M( o+ z! u% g2 ?: m( K
DWORD dwServerFilterVersion; //IN,服务器使用的版本规范
- `( y5 X! T, l" S! ~! Y$ @DWORD dwFilterVersion; //OUT,过滤器使用的版本规范
' h8 n2 o* r0 A* [! m. DCHAR lpszFilterDesc[SF_MAX_FILTER_DESC_LEN+1]; //OUT,对该过滤器的描述字符串
! N0 t! f4 ~4 V5 UDWORD dwFlags //OUT,事件和优先级标志
9 g' [$ `+ G) E);
; Y( E% U: ]2 \" y: P5 a$ }$ o3 R' c" I
事件和优先级标志dwFlasg的取值在MSDN中有详细解释,其中包括该Filter被调用的优先级,一般应使用默认的低优先级,否则可能会对系统的性能造成很大影响。
3 n' C7 v! X2 d/ x0 J* }) n! }
7 V+ s( z: ?% t+ ~3、HttpFilterProc( )是ISAPI Filter主要的入口函数,它根据当前的事件的不同作出不同的处理。服务器通过如下的参数块和Filter进行交互,这个参数块的作用和ISA中的ECB类似。
$ |/ M: B. D6 @4 j/ k# F* D, }  ?- n2 E9 H! @) Q5 Q( F8 `
typedef struct _HTTP_FILTER_CONTEXT - ]0 n0 ~8 S* ^6 V1 e" z- P
{
) J/ z1 l1 F/ S5 k  L5 M' b
6 A* d% T0 ?& Y- X3 M5 Y+ HDWORD cbSize; //IN,本参数块的大小 - m$ z; G5 ~' ?2 Z; E- Z; x
DWORD Revision; //IN
4 U( T7 v$ V- T1 x7 ]PVOID ServerContext; //IN,由server使用本参数 : S' Z5 f+ K/ `0 Q. @6 F
DWORD ulReserved; //IN,由server使用本参数 7 z, \7 T- G) f* ]/ ]1 n
BOOL fIsSecurePort; //IN,事件是否发生在安全端口上
9 [# s* E1 T4 O6 X+ o' VPVOID pFilterContext; //IN/OUT,与本次请求相关的上下文 ! D' |; ?- c- I2 B0 X
' u& b8 o, \' V
//回调函数,取得关于服务器和本次连接的信息
! _' K& z7 `5 \BOOL (WINAPI * GetServerVariable) (
8 q9 C& e: H+ }2 C$ ]struct _HTTP_FILTER_CONTEXT * pfc, & A; w  Y5 @0 h( R( x  v6 \$ n: \9 x
LPSTR lpszVariableName,
8 m( c& P! @/ M# e: E% n3 K4 c  A9 VLPVOID lpvBuffer, . k' e; B+ Q2 m
LPDWORD lpdwSize
4 E9 K; t6 d4 L1 k% F& Z);
; S2 f) B. ?8 G" U9 ~" h& ^
% \/ q- p  B2 T" \BOOL (WINAPI * AddResponseHeaders) ( //回调函数,给HTTP响应添加一个标头 : P% g8 m8 \& E$ k
struct _HTTP_FILTER_CONTEXT * pfc,
6 r3 }: h2 \' l- pLPSTR lpszHeaders,
$ Y2 X  _6 p: A( K3 C5 mDWORD dwReserved
/ f+ K# H# Y; D5 m); 3 U8 o. x: K; t  q& _& d- h4 y6 t
" i+ Y+ e# a& L' R3 x/ \$ i8 C
BOOL (WINAPI * WriteClient) ( //回调函数,将原始数据发送给客户端
5 z9 [0 a5 \9 @7 }struct _HTTP_FILTER_CONTEXT * pfc, 6 l9 ^7 j. T# w4 F: g
LPVOID Buffer, & B5 }( x3 ]) t0 l
LPDWORD lpdwBytes, $ a" K" K# a# H4 j2 B$ p
DWORD dwReserved 7 I' C2 x( b+ I. T
);
, J( \$ I% M% [6 ^% @4 J7 }( J4 ]  C" N( ?% K2 l( g7 I6 v
VOID * (WINAPI * AllocMem) ( //回调函数,分配内存。
$ x) {% v" e. S* l+ Xstruct _HTTP_FILTER_CONTEXT * pfc, & K# R) L( Z5 L9 [
DWORD cbSize, + }8 g/ j6 o# ~. f, j
DWORD dwReserved # F+ n6 q1 W9 A& C& ?4 {
);
/ ~4 z8 i0 Q2 f
, K7 b7 Z- Q9 Q! Q7 g8 ?. JBOOL (WINAPI * ServerSupportFunction) ( //回调函数,访问服务器的一般和特定功能   d- Q4 ?# Z# c; U$ ^! Q
struct _HTTP_FILTER_CONTEXT * pfc, . P0 f) @+ k9 w: ~
enum SF_REQ_TYPE sfReq,
  a- Z5 t' |  s* s, PPVOID pData,
- u' O' S# |! K8 X, s7 jDWORD ul1,
0 s1 e+ |$ [( i0 }# I1 YDWORD ul2 & U. c+ t2 A# u; I- N0 i
);
* n2 G: U% ], o! P' \& e8 H; P: G( ], b- z' b2 g
} HTTP_FILTER_CONTEXT, *PHTTP_FILTER_CONTEXT; # c8 U; I  n1 ?" B* q2 Z

/ p$ V8 S. l4 e  V' K$ |- M四、VC++ 6.0中对ISAPI的支持
7 g) b& `7 J* C) @) q# J* d6 I9 Q8 S; V1 @( U( Q+ V
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实例,每个 1 m  @6 |8 p1 i) h( [3 @
CHttpServerContext处理一个客户请求,这样可以处理并发的HTTP请求;CttpFilter和CHttpFilterContext之间的关系与此类似,在每个ISAPI Filter中只能有一个CHttpFilter实例,但是可以有多个CHttpFilterContext来处理并发的事件。CHttpServer和CHttpFilter是独立的类,它们可以共存于一个DLL中,也可以分别在不同的DLL中。 3 W: _6 x- a- O# f( N6 i
9 P) N# ?( G3 H5 {) {
一个ISA可以提供多个命令,每个命令对应于CHttpServer(或其子类)的一个成员函数,客户端可以在URL中指定命令名及其参数。在VC++ 6.0中是通过parse map来实现这种对应的。 2 B; a* t, k* P8 H
4 `" Z3 }* c$ ?1 t
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 _* H8 Z/ e7 L" Q
$ |4 ?1 r7 [% F# Y<form method=get action="pinball.dll?">
, J0 f7 U4 B0 D/ F+ m- \<input type="hidden" name="MfcISAPICommand" VALUE="GetImage">
6 V( U; c( h0 i% ]* z  f4 E6 d3 q' E2 q- }<input type="radio" name="Favorite" value="1" checked> Attack from Mars<br> ; I6 i: z" q; Q7 x0 P  \
<input type="radio" name="Favorite" value="2"> Twilight Zone<br> ) ?0 X! l1 ]8 S. j+ R
<input type="radio" name="Favorite" value="3"> The Addams Family<br>
) \0 \) ~4 r4 F- u& u7 P8 E- f0 `<input type="radio" name="Favorite" value="4"> Cirqus Voltaire<br> : `! }$ Z* O+ O# I$ A
<input type="radio" name="Favorite" value="0"> I don't see it here<br> , k) t# i% W9 M6 @$ `  c; q3 k
<br> 8 R: o6 d2 E0 U# A- l' J
<input type="submit" value="Show Me!"> ! H8 U4 @9 h3 c  y+ F
</form>
/ r# P+ C: p9 ~. G2 j0 B* z  R1 V+ ]5 C6 R. N3 Z  l, z
当客户端选中了上面的表单中的“Attack from Mars”这一项并点击了submit按钮后,服务器端 , P0 _4 G/ y6 c# ?7 i; h
最终将得到如下的URL串:
3 y- J" N7 v/ G" d% \, g* n
  D1 ?' j' ]1 d( f8 w4 Ahttp://www.abc.com/pinball.dll?M ... mage&Favorite=1 : `4 ~0 i+ V8 Z0 s& q( I/ r% Y
( K2 ], k1 A" \1 v+ u
在该URL串中,命令名是GetImage,参数Favorite的值是1,因此pinball.dll中的如下成员函数
8 g6 {. t/ [+ e  e将被调用以处理该请求,其中参数dwChoice对应URL中的参数Favorite:
' T$ D* ?+ h& u, K' Y2 D6 H8 @7 s$ l* O4 \" S4 v. J
void CPinballExtension::GetImage(CHttpServerContext* pCtxt, long dwChoice); 4 S7 x# g3 U* K
* Z0 d1 i4 `' N  u7 ~. N# K
而parse map需要按照下面的形式定义:
8 u) a' t; V4 G* O+ z- S
# }6 B; [% i7 V2 @- Z- m- R* N8 {//CPinballExtension从CHttpServer派生而来 / j4 B' N8 q9 m) X+ D
BEGIN_PARSE_MAP(CPinballExtension, CHttpServer)   h7 W  n! m8 R1 T) Q1 v

7 f$ a4 m# e8 k. U$ O5 d, L: k//GetImage是CPinballExtension的成员函数,且有一个long型的参数即dwChoice ! z/ }2 l7 o0 B5 p1 I
ON_PARSE_COMMAND(GetImage, CPinballExtension, ITS_I4)
( C) }% j0 l2 Q, h* x4 f
7 F) e5 m- H4 C: m. r$ |, f8 k//该参数在URL中的名字为Favorite
8 H( n0 y, H& eON_PARSE_COMMAND_PARAMS("Favorite")
8 o, K& q  r) ?3 a/ j$ p, Y
2 O+ m& z5 l; y  [3 D5 G; pEND_PARSE_MAP(CPinballExtension) / v  ^0 x: o  z  u& S) A

* j6 F/ {2 m, S$ H) U而对于ISAPI Filter,在VC中可以通过重载CHttpFilter(或其子类)的不同的成员函数来实现对不同事件的处理。可重载的函数如下,每一个成员函数均对应一个或多个事件:
# x# [1 W; i" v4 b
! I& S, `* a3 S/ qOnPreprocHeaders
' }4 N+ b8 c& zOnAuthentication ) ?+ s5 K+ w6 c& K6 B
OnUrlMap ; j4 K) A/ i. ~5 ], L8 B
OnSendRawData
) Q: q4 \4 |1 P/ ?) b3 N' }. |) A+ {  sOnReadRawData
- B6 {4 t- {/ P( hOnLog
1 C$ k/ O! ^$ g" a. x" F. y1 HOnEndOfNetSession ; B" x9 T! N. C+ W  V" D  F

; f2 G6 V) F% LMSDN提供了4个关于ISAPI的编程实例:counter、MFCUCASE、pinball、wwwquote,有兴趣的可看看,本文主要不是介绍编程,所以不再赘述。 4 O. n+ E- F0 Q) R  N

) X: O4 U. N( ~6 ^( e参考资料: + |: Y$ `) M' y. o

% Q1 V9 ^3 n( b; t1、MSDN
) y. F7 W& M3 j2、《精通CGI编程》,丁一强等,清华大学出版社
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2025-9-30 13:15 , Processed in 0.037338 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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