设为首页 收藏本站
查看: 881|回复: 0

[经验分享] [转]delphi IIS ISAPI

[复制链接]

尚未签到

发表于 2015-8-14 10:37:47 | 显示全部楼层 |阅读模式
  原文地址:http://siyebocai.blog.163.com/blog/static/103316426200810297512408/
  目前由于汉字内码的不统一,互联网上的中文站点为了实现对于不同用户的支持,一般采取建立两套主页,分别用GB和BIG码来编写。这样做显然要增加站点的维护工作,更新主页时要同时更新两部分。而且如果主页内容是实时更新的,采用手工维护两套主页的方法显然不行了。本文介绍了用ISAPI过滤器来动态产生另外一套内码主页的方法,这样就可以只制作一套主页就同时支持GB码和 BIG5码。
  基本的思路,编写一个ISAPI过滤器,对于所有最终返回给用户的HTML文本,实行内码转换。这样用户看到的将是他期望的编码方式。ISAPI过滤器可以作为WEBServer横向功能扩展。当某个预先定义好
  的服务器端的事件发生时,IIS就调用用户定义好的过程,此时就可以通过修改IIS传来的数据来改变IIS的行为。IIS预定义的事件如下:
    SF_NOTIFY_READ_RAW_DATA
    当IIS要从用户读入数据时发生。过滤器可以在IIS处理他们之前检查甚至修改用户输入的原始数据。
  SF_NOTIFY_PREPROC_HEADERS
    IIS预处理HTTP请求包头后发生。过滤器可以检查修改增加包头。
  SF_NOTIFY_AUTHENTICATION
    IIS试图验证用户身份时发生。过滤器可以实现自己的验证方案。
  SF_NOTIFY_URL_MAP
    IIS试图将URL解释为物理文件时。过滤器可以将请求重定向到其他的文件。
  SF_NOTIFY_ACCESS_DENIED
  当身份验证失败时发生。
  
    SF_NOTIFY_SEND_RAW_DATA
  当其他程序处理完,IIS准备将数据发回给用户时发生。我们的过滤器就通过此事件,转换内码。
  SF_NOTIFY_LOG
    当IIS写记录到LOG文件时。过滤器可以搜集更多的信息写入记录文件中。
  SF_NOTIFY_END_OF_REQUEST
    当一个HTTP请求结束时发生。过滤器可以实现基于请求的处理。由于这是在IIS3.0中新增的,Delphi中的ISAPI2.pass单元中没有相应的定义可以手工加入SF_NOTIFY_END_OF_REQUEST=$80
  SF_NOTIFY_END_OF_NET_SESSION
    连接结束时。注意如果浏览器支持"keep-alive",一次连接可能包含几个HTTP请求。过滤器可以用他来释放一些用户的资源。
  我们要实现动态的内码转换,只要过滤器处理SF_NOTIFY_SEND_RAW_DATA事件,将IIS处理好的数据转换成需要的内码就可以实现内码的动态转换。具体程序有两个问题需要注意:
1.过滤器只能处理返回是HTML格式的,其他图片等二进制请求无须也不允许转换。
2.对于返回的HTML,只处理实际数据,其他HTTP协议的包不应该处理。

编程
  每个ISAPI过滤器DLL必须输出两个供IIS使用的函数:
  GetFilterVersion()和HttpFilterProc()。下面分别讲述。
  GetFilterVersion()
  用于初始化和处理事件的登记。例程没有初始工作要做,只是简单的登记了要处理的两个事件和其他一些标志。
function GetFilterVersion(var pVer:THTTP_FILTER_VERSION):BOOL;stdcall;
begin
//过滤器要处理的事件和其他一些标志
pVer.dwFlags:=(SF_NOTIFY_NONSECURE_PORT
//过滤器只在一般端口上使用
or SF_NOTIFY_SEND_RAW_DATA
//处理发送数据事件
or $80//SF_NOTIFY_END_OF_REQUEST
//处理请求结束事件
or SF_NOTIFY_ORDER_DEFAULT
//过滤器使用缺省优先级
);
//过滤器使用的版本HTTP_FILTER_REVISION
返回当前版本
pVer.dwFilterVersion:=HTTP_FILTER_REVISION;
//过滤器的描述
pVer.lpszFilterDesc[0]:=’A’;pVer.lpszFilterDesc[1]:=#0;
result:=true;//初始化成功
end;
HttpFilterProc()
  由IIS回调,是过滤器的实际处理部分。
  其中参数Notificationtype是该调用的事件类型,如果过滤器处理多个事件,可以检查该值来区分事件。
  PvNotification是一个根据事件类型可变结构的参数。对于SF_NOTIFY_SEND_RAW_DATA,他的结构如下:

THTTP_FILTER_RAW_DATA=record
pvInData:Pointer;//指向数据区
cbInData:DWORD;//数据大小
cbInBuffer:DWORD;//缓冲的大小
dwReserved:DWORD;//保留
end;
  其中的pvInData就是要发送的数据指针。其他的结构请参看有关资料这里不再详述。
  第一个参数var pfc:THTTP_FILTER_CONTEXT是过滤器的环境指针,其中的pFilterContext是一个用户使用的指针,用来保存和一个 HTTP连接相关的信息,这样过滤器可以区分出正在处理的是否是以前曾处理过的连接。因为一个请求将会产生多个 SF_NOTIFY_SEND_RAW_DATA事件,过滤器必须能够区分他们。
  程序的流程是:当连接建立后,pFilterContext被IIS初始化为NIL(0),第一次SF_NOTIFY_SEND_RAW_DATA调用时,过滤器要检查返回的MIME,如果不是HTML则将pFilterContext置为pointer(2)(将指针当作变量用,因为我们只要一个标志),随后的发送事件调用将直接返回。请求结束后,发生SF_NOTIFY_END_OF_REQUEST事件,过滤程序将pFilterContext 复位为nil。
  如果是HTML,则将pFilterContext置为pointer(1),随后的调用就将对数据进行内码的转换,然后将 pFilterContext置为pointer(3)。如果还有后续的调用,则再将pFilterContext置为pointer(1),直到全部数据发送完成。

function HttpFilterProc(var pfc:THTTP_FILTER_CONTEXT;Notificationtype:DWORD;pvNotification:Pointer):DWORD;stdcall;
var
p:PHTTP_FILTER_RAW_DATA;
i:integer;
pc:pchar;
begin
if Notificationtype=$80 then
  //是SF_NOTIFY_END_OF_REQUEST将pFilterContext复位
  begin
   pfc.pFilterContext:=nil;
  end
else
  begin
   p:=PHTTP_FILTER_RAW_DATA(pvNotification);
   pc:=p^.pvInData;
   case integer(pfc.pFilterContext) of
    0://第一次调用,要检查MIME
     begin
      pfc.pFilterContext:=pointer(2);
      i:=0;
      while i<p^.cbInBuffer-4-1do
       begin
        if (pc^=’/’)and(pc^[i+1]=’h’)and(pc^[i+2]=’t’)and(pc^[i+3]=’m’)
then begin
pfc.pFilterContext:=pointer(1);
//是HTML
break;
end;
inc(i);
end;//endofwhile
end;
1://HTML数据
begin
pfc.pFilterContext:=pointer(3);
//将pc转换内码
gb2big(pc,p^.cbInBuffer);
end;
3://http1.1100contimue
begin
pfc.pFilterContext:=pointer(1);
end;
end;//endofcase
end;
//总是返回成功,并且如果有其他过滤器的话,还将继续调用
result:=SF_STATUS_REQ_NEXT_NOTIFICATION;
end;
  下面是完整的程序文件(gb2bigfiler.dpr),其中的u_gb2big_tab单元完成GB码到BIG5,码的转换,这里不再细述,有兴趣的读者可以到后文提到的笔者的站点去下载源码。
library gb2bigfiler;
uses
SysUtils,math,Classes,windows,
isapi2,//delphi中ISAPI过滤器单元
u_gb2big_tab;//包含将GB码转换成BIG5码的过程gb2big

//下面两个函数的定义见上文
function HttpFilterProc(...);begin...end;
function GetFilterVersion(...);begin...end;
exports
HttpFilterProc index 1,GetFilterVersion index 2;
Begin end.
  读者一定注意到了,这个过滤器将所有返回的HTML都转换成了BIG5码,那么GB码又如何看到呢?当然可以在过滤器中检查一些环境变量来决定用户所要求的是GB还是BIG5,可是这样做除了比较麻烦外,还存在效率问题,因为每个请求都要被过滤器处理。
  笔者采用的方法是利用IIS4.0中可以设置多个站点的功能,设置两个站点。一个不含过滤器,所以GB内容高效直接的返回给GB用户;而另一个站点使用另外一个端口比如81,所有虚拟目录和前一个站点一样,将过滤器加载在该站点上,这样所有向81端口的请求,都将被过滤器转换成BIG5码返回给用户。
  下面简述一下具体配置过程。首先在delphi中选择新建一个DLL,输入程序源码,编译后生成gb2bigfiler.DLL文件。在 IIS4.0 的管理控制台中,选"新建站点",主目录和缺省站点一样,端口设为81,在ISAPI过滤器中选择"添加",将gb2bigfiler.DLL加入。
  设置好后,可以浏览81端口(例如:http://www.yoursite.com:81/your.html),这时原来GB码的内容就变成了BIG5码了。
  有兴趣的读者可以访问http://202.96.122.45/qq,起始页可以选择内码,随后浏览的内容包括静态的HTML文本,放在数据库中的《红楼梦》、《唐诗》、完全动态更新的BBS,都可以做到用两种内码显示。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-98862-1-1.html 上篇帖子: 网站定时任务IIS配置 下篇帖子: 64位操作系统/64位IIS下解决32位DLL不能运行的问题(原创)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

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