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

IIS的ISAPI接口简介

[复制链接]
发表于 2003-10-13 13:22:17 | 显示全部楼层 |阅读模式
作者:netguy (mailto:netguy@nsfocus.com) $ i( D5 H5 T' ?( W. s9 V

$ b3 T1 i8 v- k) L/ {9 I6 IISAPI(Internet Server Application Programming Interface)作为一种可用来替代CGI的方法,是由微软和Process软件公司联合提出的Web服务器上的API标准。ISAPI与Web服务器结合紧密,功能强大,能够获得大量的信息,因此利用ISAPI可以开发出灵活高效的Web服务器增强程序。由于ISAPI程序与Web服务器的关系,使得ISAPI接口在安全方面有一定的研究价值。本文主要讨论ISAPI在IIS和VC++ 6.0中的实现。
3 U. d) C$ Z; d3 S8 \1 G  a2 N3 R- ]- X" q6 S9 n
一、ISAPI接口和CGI接口的不同。. F" x* H6 M5 I" V. N" J5 ?

2 j& a; J& C' L  F7 f0 kISAPI程序和CGI程序完成类似的功能,但是实现方法不同。
' _6 A9 I9 G, ]: z' b- h) J% N  u2 \* a; S0 K6 H7 b6 D7 M
1、ISAPI程序以DLL形式被Web服务器加载到自己的进程空间中,因此和服务器共用同一个地址空间,且在没有客户请求时可以将其从内存中卸载;而对客户端发来的每个对CGI程序的请求则需要服务器为它单独启动一个进程,这需要耗费大量的时间和内存。当并发的请求数目很大时,使用CGI在效率上不如ISAPI。
  b% W8 L+ }* A  m4 d8 L
5 |4 x6 P& h4 o2、CGI程序通过环境块和标准输入输出与Web服务器进行通信,而ISAPI程序与服务器结合得更为紧密,与服务器共享同一个进程上下文,主要通过一个参数块与服务器进行交互,可以从服务器那里获得关于当前HTTP连接的大量信息。. F. U, `" e+ _

1 J3 A4 J  `5 A$ f0 q7 K4 iISAPI主要分为ISA和ISAPI Filter两部分。ISA方法相对而言要传统一些,利用一些特殊的链接,指向服务器的作业,供程序开发人员设计一些扩展功能;而ISAPI过滤器则倾向于构造服务器直接调用的模块,提供一种无缝链接部件用于监测直接来自于服务器的HTTP请求。
0 D" e( l+ w1 T" o( j  [2 ?/ h4 o
/ W5 V2 r$ \  M: ~( F; ~6 e: S7 P" ?# D; }5 L
二、ISA
  i+ v& m# s5 V1 A; }/ y! `! F3 |8 |0 ~) _# }! D
ISA(Internet Server Application)也可称为ISAPI DLL,其功能和CGI程序的功能直接相对应,使用方法和CGI也类似,由客户端在URL中指定其名称而激活。例如下面的请求将调用服务器的虚拟可执行目录Scripts下的function.dll(ISAPI DLL必须放在服务器的虚拟可执行目录下):
: T' P$ L! g4 ihttp://www.abc.com/Scripts/function.dll?
! Y* Q0 q8 {8 g; S1 B/ a" k7 j, _0 n, w5 O9 t- T
ISA和服务器之间的接口主要有两个:GetExtentionVersion( )和HttpExtentionProc( )。任何ISA都必须在其PE文件头的引出表中定义这两个引出函数,以供Web服务器在适当的时候调用。7 h2 T; u  o% v; N
- v4 w( x( ^, p& a" U; l9 V- I/ b  _
1、当服务器刚加载ISA时,它会调用ISA提供的GetExtentionVersion( )来获得该ISA所需要的服务器版本,并与自己的版本相比较,以保证版本兼容。函数原型如下:
2 D/ L3 s' X$ Z0 L7 y/ ?
' h# _" w+ m6 H2 OBOOL WINAPI GetExtentionVersion(HSE_VERSION_INFO *version);3 D. Y5 s" }% p- y8 ?: ]
typedef struct _HSE_VERSION_INFO' l  P- H8 q" }* B5 N6 x1 o
{" s$ y/ f. P; b: r$ b
DWORD dwExtensionVersion; //版本号
9 ^( U! o# j' CCHAR lpszExtensionDesc[HSE_MAX_EXT_DLL_NAME_LEN]; //关于ISA的描述字符串
" u) M: R3 a9 C- k4 k' o# X2 k2 N} HSE_VERSION_INFO, *LPHSE_VERSION_INFO;1 v1 A( b  E, q1 P. g0 G
# l) @0 x0 U$ ~! z- J- B# k
2、ISA的真正入口是HttpExtentionProc( ),它相当于普通C程序的main( )函数,在这个函数中根据不同的客户请求作不同的处理。服务器和HttpExtentionProc( )之间是通过扩展控制块(Extention Control Block)来进行通信的,即ECB中存放入口参数和出口参数,包括服务器提供的几个回调函数的入口地址。函数原型如下:3 w+ L5 |& q6 d

; P& U0 G! F$ y% QDWORD HttpExtensionProc( EXTENSION_CONTROL_BLOCK *pECB );) d- T! ~' C5 m' |, I

+ `1 s2 ^; |% @) UECB的结构定义如下(IN表示入口参数,OUT表示出口参数):
7 |$ M) r1 a* f- p/ n' C. A8 v) V/ B7 X+ O- [  v4 M& c  t
typedef struct _EXTENSION_CONTROL_BLOCK
7 j& G7 O$ P$ ~) s. L+ Y# s) d{
2 m5 `2 U0 z0 ]! uDWORD cbSize; //IN,本结构的大小,只读
+ \# d! d% J/ _7 YDWORD dwVersion //IN,版本号,高16位为主版本号,低16位为次版本号
. b2 b$ S9 h0 Y! u3 c- C4 d5 q! UHCONN ConnID; //IN,连接句柄,由服务器分配,ISA只能读取该值
4 |0 |$ l0 L. j0 o' ^DWORD dwHttpStatusCode; //OUT,当前完成的事务状态3 n* A$ U( y' E5 R% ]
CHAR lpszLogData[HSE_LOG_BUFFER_LEN]; //OUT,需要写入到日志文件中的内容- \, @8 r. _4 M  _: N
LPSTR lpszMethod; //IN,等价于CGI的环境变量REQUEST_METHOD1 A3 Z  A! a1 l' ^
LPSTR lpszQueryString; //IN,等价于环境变量QUERY_STRING
( I7 |7 A% W8 {LPSTR lpszPathInfo; //IN,等价于环境变量PATH_INFO
/ P3 K' ]+ Q2 P/ S* i0 LLPSTR lpszPathTranslated; //IN,等价于环境变量PATH_TRANSLATED
( f' C" b" x# v) @DWORD cbTotalBytes; //IN,等价于环境变量CONTENT_LENGTH$ o9 i) `. ?1 E) e+ A2 }
DWORD cbAvailable; //IN,缓冲区中的可用字节数0 c; `7 G) w. E- l% m
LPBYTE lpbData; //IN,缓冲区指针,指向客户端发来的数据
* `% P/ u. M7 e+ ~LPSTR lpszContentType; //IN,等价于环境变量CONTENT_TYPE" E* H! v# X+ U
( t3 W( B7 N& k
//回调函数,用于返回服务器的连接信息或特定的服务器详细情况  @; H  g3 `- Q& {) p$ Q
BOOL ( WINAPI * GetServerVariable ) 8 a$ {4 t/ c1 H" L7 l9 a
( HCONN hConn,
8 K) [$ ]; T) N/ k& O9 i! e/ GLPSTR lpszVariableName,
1 E. I" Q, y# b8 oLPVOID lpvBuffer,
8 o( @" q( `$ e3 y9 P: _& s' lLPDWORD lpdwSize );' y7 ~* X( T8 d8 @7 }8 a" E  b9 i

+ W- \& C4 t9 dBOOL ( WINAPI * WriteClient ) //回调函数,从客户端的HTTP请求中读取数据
6 u3 {$ Q: W, |! w: }( HCONN ConnID,) D3 w. W: Z  f( `4 J
LPVOID Buffer,: }) Q; l: k5 o  ~
LPDWORD lpdwBytes,
! k" F( s; y9 P! \3 O& F- WDWORD dwReserved );
$ [( O1 M* }, m1 _( F( e' a& L3 `/ h4 I& R
BOOL ( WINAPI * ReadClient ) //回调函数,向客户端发送数据$ I3 B& L1 n2 N- k9 y; G, x: T
( HCONN ConnID,' Q( }# e* X: i4 C1 K0 }
LPVOID lpvBuffer,
; x6 C1 }) N9 }LPDWORD lpdwSize );
( R1 K8 L# ~2 W" c! p& g4 }. Y5 W+ u; X# }; r. z6 I& S
BOOL ( WINAPI * ServerSupportFunction ) //回调函数,访问服务器的一般和特定功能* U2 e6 D5 _2 L' R, o. K
( HCONN hConn,# E% p2 N+ |: D" N1 ]
DWORD dwHSERRequest,
! V3 {2 W/ b! E/ g- ^LPVOID lpvBuffer,
* \& q' b( U2 q- @' KLPDWORD lpdwSize,
; H, {2 u" }3 ^3 J6 FLPDWORD lpdwDataType );
# e) J6 k( Q$ e. e1 _9 i/ {/ E! Z" Q; |4 W
} EXTENSION_CONTROL_BLOCK, *LPEXTENSION_CONTROL_BLOCK;
* Y8 M( X: E% t
' P- H5 v2 j5 ^) Y在上述ECB中,服务器不但提供了当前HTTP连接的句柄和一些变量,而且提供了4个回调函数给ISA调用,从而使ISA可以获得更详尽的信息。- P$ p, {' X& J6 H4 |
3 J- H' Y- Y7 k4 @" t
三、ISAPI Filter
7 K, M- ~  a8 z4 u- S+ V. q( i# D# p/ @
ISAPI Filter位于服务器和客户端之间,能够对服务器和客户端之间的通信进行预处理和后处理,比如对通信进行加密/解密、提供对客户进行身份验证的新方法、提供自定义的日志记录等,在CGI中没有与ISAPI Filter直接相对应的部分。
2 R* O* _; A! b1 N0 V& C
. F  l) w" e8 E* RISAPI Filter与服务器之间的接口有两个:GetFilterVersion( )和HttpFilterProc( )。任何/ m4 i. N9 W# G& P' A
ISAPI Filter都必须引出这两个函数以供服务器调用。
4 i  G2 X$ G( w( K7 T
. O: w* T+ ?2 U1 z3 b1、在注册表的如下键值中存放着所有ISAPI Filter的文件名,IIS服务器启动时从该键值中获得
9 ]7 P7 Y9 f7 M9 z5 J! N+ tFilter的文件名并加载它们。) J, U* Z# A; A& F
+ C7 m1 _* c) {/ _% D1 E2 ?
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/W3SVC/Parameters/FilterDLL. s+ q# [4 a, R; L( k
. e6 K8 N' [8 n9 k+ B5 w' D
2、然后服务器调用每个Filter提供的GetFilterVersion( )函数,获得版本号以及该Filter希望处理的事件,即ISAPI Filter通过引出GetFilterVersion( )函数来告知服务器自己希望处理什么类型的事件,因为ISAPI Filter是通过事件来激活的,当满足条件的事件到达时,服务器就会调用Filter引出的主函数HttpFilterProc( )对该事件进行处理。GetFilterVersion( )的原型如下:/ S! g0 h+ S+ \8 \' C& {. y
0 ^3 l7 Y: E% O+ U
BOOL WINAPI GetFilterVersion( 1 p" `) U% X1 c8 }# V! y3 Z7 e
DWORD dwServerFilterVersion; //IN,服务器使用的版本规范
1 T8 ^, v& l# Q% P* \DWORD dwFilterVersion; //OUT,过滤器使用的版本规范
5 _8 T+ a* \6 e. U; `CHAR lpszFilterDesc[SF_MAX_FILTER_DESC_LEN+1]; //OUT,对该过滤器的描述字符串- g4 m2 e) w6 S# k
DWORD dwFlags //OUT,事件和优先级标志
1 X& N+ `1 M0 D1 H6 Y1 b2 O8 d);
3 e% I0 T& n7 B2 x9 f
+ Q* j$ `7 ~7 r( h事件和优先级标志dwFlasg的取值在MSDN中有详细解释,其中包括该Filter被调用的优先级,一般应使用默认的低优先级,否则可能会对系统的性能造成很大影响。
9 u+ |4 Y' Z: t! I! d0 E' @) M* l+ m) w" M; J/ ]9 N
3、HttpFilterProc( )是ISAPI Filter主要的入口函数,它根据当前的事件的不同作出不同的处理。服务器通过如下的参数块和Filter进行交互,这个参数块的作用和ISA中的ECB类似。
; M, V! [' h# x/ b! j" p& r5 D
% @$ J3 o) ^9 M4 B  A4 Ntypedef struct _HTTP_FILTER_CONTEXT
/ F! _2 t5 e' X9 B: }{2 V% s) r; L/ w8 h( d& A: H3 @" x

% E% f" i5 s. M& i& O4 v% |DWORD cbSize; //IN,本参数块的大小+ u* r/ V  y3 e" V& a8 h4 Y$ _! f
DWORD Revision; //IN
7 W; ?& S. [: i3 Y, G! S: a6 S) WPVOID ServerContext; //IN,由server使用本参数3 V1 y+ k' O* D6 ~
DWORD ulReserved; //IN,由server使用本参数5 a. M# P3 L% u5 v1 ?+ O
BOOL fIsSecurePort; //IN,事件是否发生在安全端口上8 c" l$ X) c& h+ w4 v
PVOID pFilterContext; //IN/OUT,与本次请求相关的上下文
3 E. B2 F  s4 ]7 ^
, a2 i4 w1 ?$ s; M# V4 {1 n2 ^3 o//回调函数,取得关于服务器和本次连接的信息
: W, w% d" Y  D7 D& p$ X' ^BOOL (WINAPI * GetServerVariable) (
  Q$ O$ m% X( Z4 A! B: k- gstruct _HTTP_FILTER_CONTEXT * pfc,
$ C7 X( P: n$ d+ ]8 lLPSTR lpszVariableName,
* l! _. h: Z( M- k3 c, |" y+ MLPVOID lpvBuffer,
! M* x1 O5 @+ g5 U6 z1 V" ~LPDWORD lpdwSize
0 N0 B+ j2 }3 p);
* K* ], W1 L" E1 ?1 K9 f7 o7 N6 z% B; B
BOOL (WINAPI * AddResponseHeaders) ( //回调函数,给HTTP响应添加一个标头. I7 g' o+ Q5 Q( `+ F; h
struct _HTTP_FILTER_CONTEXT * pfc,
4 O- [1 u- O( O2 _LPSTR lpszHeaders,
8 v0 D- l5 d0 @+ I1 G5 D: KDWORD dwReserved! }) b& L$ O9 C1 B" a
);
4 Z' a! I  ^  X1 b" [7 X3 h# O" J& O2 _7 c( B- o+ d0 q; ^$ E
BOOL (WINAPI * WriteClient) ( //回调函数,将原始数据发送给客户端+ O7 `3 u% {8 {7 ^$ l" r
struct _HTTP_FILTER_CONTEXT * pfc,
" A3 Q7 d. T7 L% rLPVOID Buffer,
  Z) }8 v' i- ], dLPDWORD lpdwBytes,
  M1 o2 o( E1 ODWORD dwReserved
' R6 H! e0 d8 E# d); 9 g( O3 X% s8 n$ B% _2 |* ]- C

3 \: q# W, E8 y0 QVOID * (WINAPI * AllocMem) ( //回调函数,分配内存。% B4 b( R! R) t& e
struct _HTTP_FILTER_CONTEXT * pfc,
# E5 a& u1 k. m0 ]" q% @DWORD cbSize,
' U& w6 x0 |$ f; f/ T7 XDWORD dwReserved4 N) z( U- b8 ^' ?
);
# j( u: _8 _% _5 k; f2 k: x$ ]5 y1 F; m% ?. v3 g5 F
BOOL (WINAPI * ServerSupportFunction) ( //回调函数,访问服务器的一般和特定功能
$ H5 Z( \' m4 l$ a  m0 rstruct _HTTP_FILTER_CONTEXT * pfc,' K" c3 w  P+ ^0 F# o' n2 w
enum SF_REQ_TYPE sfReq,
* b  t+ j) [8 K) I) WPVOID pData,
  N3 c7 }) y% n2 O+ w/ i* o5 tDWORD ul1,! a4 Y( |! G  n: J) ^
DWORD ul2
8 D- f) X7 R8 U) ~/ B); 7 V1 C: w) {" \
2 \5 S3 c" j7 _" [* n& [
} HTTP_FILTER_CONTEXT, *PHTTP_FILTER_CONTEXT;
- V$ z# w# X3 n. ?& Y
/ k3 a, G; `, J9 K$ U" i7 r9 }四、VC++ 6.0中对ISAPI的支持4 A" o- `" B/ |
0 A& k) B  h- g2 |1 V& [# N* E
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实例,每个0 Q0 m2 k6 x( A) ]& W; L$ [
CHttpServerContext处理一个客户请求,这样可以处理并发的HTTP请求;CttpFilter和CHttpFilterContext之间的关系与此类似,在每个ISAPI Filter中只能有一个CHttpFilter实例,但是可以有多个CHttpFilterContext来处理并发的事件。CHttpServer和CHttpFilter是独立的类,它们可以共存于一个DLL中,也可以分别在不同的DLL中。
7 a) b8 b) v5 o
4 y8 L3 i$ H$ `* L; Q# W一个ISA可以提供多个命令,每个命令对应于CHttpServer(或其子类)的一个成员函数,客户端可以在URL中指定命令名及其参数。在VC++ 6.0中是通过parse map来实现这种对应的。' n" x' ~, W0 v$ \

( c7 z9 W) f% `5 HParse 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 f- h$ P9 J& K, r

3 ^' s4 N# I' d: |; Y/ g<form method=get action="pinball.dll?">
6 v4 \% c% K' f4 e; L% w# Q% `<input type="hidden" name="MfcISAPICommand" VALUE="GetImage">
" }  Q2 H! g1 T- O$ I. z# f3 e<input type="radio" name="Favorite" value="1" checked> Attack from Mars<br>
. @4 ^9 ?' ^0 A9 V7 r<input type="radio" name="Favorite" value="2"> Twilight Zone<br>1 ]5 Q" E, Z5 W' Q9 O
<input type="radio" name="Favorite" value="3"> The Addams Family<br>8 K- g2 n& a5 [& ^8 T
<input type="radio" name="Favorite" value="4"> Cirqus Voltaire<br>" Q) J: N& l8 U$ m2 v, u& `
<input type="radio" name="Favorite" value="0"> I don't see it here<br>: k, r* d. B6 \% T2 x# X6 t+ K  Q9 G/ K3 Y
<br>; w* {+ B% q; M! U: r  ^
<input type="submit" value="Show Me!">
! t9 \" H% O7 \; {7 K5 }</form>
7 J0 |5 h: k- }3 z& V9 `3 ?
5 B! b- A2 S& j7 o( ?+ J, [3 V当客户端选中了上面的表单中的“Attack from Mars”这一项并点击了submit按钮后,服务器端
6 v- s; q( x4 W0 j3 Q0 ?: y: T% Q$ e6 j最终将得到如下的URL串:3 B0 W# O' u* s, P9 c" H& y5 s8 x% j- g

, K: \$ }/ a3 g5 [; x, }http://www.abc.com/pinball.dll?M ... GetImage&Favorite=1$ M. B- t( ?/ M4 Z3 n

4 _* Y) s$ w/ N* ~& H0 o在该URL串中,命令名是GetImage,参数Favorite的值是1,因此pinball.dll中的如下成员函数6 G- b5 i+ k/ G/ |) |
将被调用以处理该请求,其中参数dwChoice对应URL中的参数Favorite:% t9 m3 `& t( h" \+ G

  v4 k% q, p2 ]( b, vvoid CPinballExtension::GetImage(CHttpServerContext* pCtxt, long dwChoice);0 @, y- G" x7 }$ F
( M1 q+ V9 P( l
而parse map需要按照下面的形式定义:
8 ~# \( C# p6 ?4 k3 A; T- L4 z2 L" t; K
//CPinballExtension从CHttpServer派生而来! _2 [/ d6 o% w! O% h& ]
BEGIN_PARSE_MAP(CPinballExtension, CHttpServer) , }* @! C5 Z8 j" q
+ @( o, N1 i) |5 }' b/ {
//GetImage是CPinballExtension的成员函数,且有一个long型的参数即dwChoice
" i- g. V& L$ Q' A0 w% HON_PARSE_COMMAND(GetImage, CPinballExtension, ITS_I4)   H0 W6 A/ F7 F* ?: P
4 l: J( p3 T! t# O
//该参数在URL中的名字为Favorite
- ]2 F: ]2 N7 f  u' H# k: Y  zON_PARSE_COMMAND_PARAMS("Favorite")
. d, k& Q3 ]% M4 G, Z/ D1 [
- V! p; j( R' c4 k  M* {( S4 z) GEND_PARSE_MAP(CPinballExtension)4 g1 T  h2 p0 s. u& w
- m+ K5 E2 I% C' z
而对于ISAPI Filter,在VC中可以通过重载CHttpFilter(或其子类)的不同的成员函数来实现对不同事件的处理。可重载的函数如下,每一个成员函数均对应一个或多个事件:
! Q# s' v+ t" j( W3 E. r( C$ h* S6 W- ]& U6 |, x
OnPreprocHeaders) i6 t) Q/ \( H" R' i
OnAuthentication4 A1 a! ^. z8 N/ a
OnUrlMap
7 b" |8 H: r9 s, zOnSendRawData
; V9 h7 ]7 q$ x' v* nOnReadRawData% y/ @2 s3 j  g* h  c& N8 Y1 Z
OnLog
: k9 d& f8 o+ r0 Z+ nOnEndOfNetSession' [6 k% N8 w: `; e- D! H
. h. |9 _: {  a+ M9 x2 \1 @, Z
MSDN提供了4个关于ISAPI的编程实例:counter、MFCUCASE、pinball、wwwquote,有兴趣的可看看,本文主要不是介绍编程,所以不再赘述。4 C* b  Y& v0 w* w" [
' V" J& B3 d! W( ^4 D% S
参考资料:
- |9 U4 Q3 L& `* _: f! c. m& b
( j' v, I% g0 \# u2 p! `& R; g; X1、MSDN' S" O2 V& X5 S4 q2 s7 e
2、《精通CGI编程》,丁一强等,清华大学出版社
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2025-6-19 10:15 , Processed in 0.016263 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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