用delphi編寫ISAPI過濾器
發(fā)表時間:2024-02-20 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]實 現(xiàn)WEB 站 點 同 時 對GB 碼 和BIG 碼 的 支 持 ---- 目 前 由 于 漢 字 內(nèi) 碼 的 不 統(tǒng) 一, 互 聯(lián) 網(wǎng) 上 的 中 文 站 點 為 了 實 現(xiàn) 對 于 不 同 用 戶 的 支 持, 一 般 采 取 建 立 兩 套 主 頁, 分 別 用GB 和BIG 碼 來 編 ...
實 現(xiàn)WEB 站 點 同 時 對GB 碼 和BIG 碼 的 支 持
---- 目 前 由 于 漢 字 內(nèi) 碼 的 不 統(tǒng) 一, 互 聯(lián) 網(wǎng) 上 的 中 文 站 點 為 了 實 現(xiàn) 對 于 不 同 用 戶 的 支 持, 一 般 采 取 建 立 兩 套 主 頁, 分 別 用GB 和BIG 碼 來 編 寫。 這 樣 做 顯 然 要 增 加 站 點 的 維 護 工 作, 更 新 主 頁 時 要 同 時 更 新 兩 部 分。 而 且 如 果 主 頁 內(nèi) 容 是 實 時 更 新 的, 采 用 手 工 維 護 兩 套 主 頁 的 方 法 顯 然 不 行 了。 本 文 介 紹 了 用ISAPI 過 濾 器 來 動 態(tài) 產(chǎn) 生 另 外 一 套 內(nèi) 碼 主 頁 的 方 法, 這 樣 就 可 以 只 制 作 一 套 主 頁 就 同 時 支 持GB 碼 和BIG5 碼。
---- 基 本 的 思 路, 編 寫 一 個ISAPI 過 濾 器, 對 于 所 有 最 終 返 回 給 用 戶 的HTML 文 本, 實 行 內(nèi) 碼 轉(zhuǎn) 換。 這 樣 用 戶 看 到 的 將 是 他 期 望 的 編 碼 方 式。 ISAPI 過 濾 器 可 以 作 為WEB Server 橫 向 功 能 擴 展。 當 某 個 預 先 定 義 好
---- 的 服 務 器 端 的 事 件 發(fā) 生 時,IIS 就 調(diào) 用 用 戶 定 義 好 的 過 程, 此 時 就 可 以 通 過 修 改IIS 傳 來 的 數(shù) 據(jù) 來 改 變IIS 的 行 為。IIS 預 定 義 的 事 件 如 下:
SF_NOTIFY_READ_RAW_DATA
---- 當IIS 要 從 用 戶 讀 入 數(shù) 據(jù) 時 發(fā) 生。 過 濾 器 可 以 在IIS 處 理 他 們 之 前 檢 查 甚 至 修 改 用 戶 輸 入 的 原 始 數(shù) 據(jù)。
---- SF_NOTIFY_PREPROC_HEADERS
---- IIS 預 處 理HTTP 請 求 包 頭 后 發(fā) 生。 過 濾 器 可 以 檢 查 修 改 增 加 包 頭。
---- SF_NOTIFY_AUTHENTICATION
---- IIS 試 圖 驗 證 用 戶 身 份 時 發(fā) 生。 過 濾 器 可 以 實 現(xiàn) 自 己 的 驗 證 方 案。
---- SF_NOTIFY_URL_MAP
---- IIS 試 圖 將URL 解 釋 為 物 理 文 件 時。 過 濾 器 可 以 將 請 求 重 定 向 到 其 他 的 文 件。
---- SF_NOTIFY_ACCESS_DENIED
---- 當 身 份 驗 證 失 敗 時 發(fā) 生。
---- SF_NOTIFY_SEND_RAW_DATA
---- 當 其 他 程 序 處 理 完,IIS 準 備 將 數(shù) 據(jù) 發(fā) 回 給 用 戶 時 發(fā) 生。 我 們 的 過 濾 器 就 通 過 此 事 件, 轉(zhuǎn) 換 內(nèi) 碼。
---- SF_NOTIFY_LOG
---- 當IIS 寫 記 錄 到LOG 文 件 時。 過 濾 器 可 以 搜 集 更 多 的 信 息 寫 入 記 錄 文 件 中。
---- SF_NOTIFY_END_OF_REQUEST
---- 當 一 個HTTP 請 求 結(jié) 束 時 發(fā) 生。 過 濾 器 可 以 實 現(xiàn) 基 于 請 求 的 處 理。 由 于 這 是 在IIS 3.0 中 新 增 的,Delphi 中 的ISAPI2.pass 單 元 中 沒 有 相 應 的 定 義 可 以 手 工 加 入SF_NOTIFY_END_OF_REQUEST=$80
---- SF_NOTIFY_END_OF_NET_SESSION
---- 連 接 結(jié) 束 時。 注 意 如 果 瀏 覽 器 支 持"keep-alive", 一 次 連 接 可 能 包 含 幾 個HTTP 請 求。 過 濾 器 可 以 用 他 來 釋 放 一 些 用 戶 的 資 源。
---- 我 們 要 實 現(xiàn) 動 態(tài) 的 內(nèi) 碼 轉(zhuǎn) 換, 只 要 過 濾 器 處 理SF_NOTIFY_SEND_RAW_DATA 事 件, 將IIS 處 理 好 的 數(shù) 據(jù) 轉(zhuǎn) 換 成 需 要 的 內(nèi) 碼 就 可 以 實 現(xiàn) 內(nèi) 碼 的 動 態(tài) 轉(zhuǎn) 換。 具 體 程 序 有 兩 個 問 題 需 要 注 意:
過 濾 器 只 能 處 理 返 回 是HTML 格 式 的, 其 他 圖 片 等 二 進 制 請 求 無 須 也 不 允 許 轉(zhuǎn) 換。
對 于 返 回 的HTML, 只 處 理 實 際 數(shù) 據(jù), 其 他HTTP 協(xié) 議 的 包 不 應 該 處 理。
編 程
---- 每 個ISAPI 過 濾 器DLL 必 須 輸 出 兩 個 供IIS 使 用 的 函 數(shù):
---- 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
//處理發(fā)送數(shù)據(jù)事件
or $80 // SF_NOTIFY_END_OF_REQUEST
處理請求結(jié)束事件
or SF_NOTIFY_ORDER_DEFAULT
//過濾器使用缺省優(yōu)先級
);
//過濾器使用的版本HTTP_FILTER_REVISION
返回當前版本
pVer.dwFilterVersion := HTTP_FILTER_REVISION;
//過濾器的描述
pVer.lpszFilterDesc[0]:='A'; pVer.lpszFilterDesc[1]:=#0;
result:=true; //初始化成功
end;
HttpFilterProc()
---- 由IIS 回 調(diào), 是 過 濾 器 的 實 際 處 理 部 分。
---- 其 中 參 數(shù)Notificationtype 是 該 調(diào) 用 的 事 件 類 型, 如 果 過 濾 器 處 理 多 個 事 件, 可 以 檢 查 該 值 來 區(qū) 分 事 件。
---- PvNotification 是 一 個 根 據(jù) 事 件 類 型 可 變 結(jié) 構(gòu) 的 參 數(shù)。 對 于SF_NOTIFY_SEND_RAW_DATA, 他 的 結(jié) 構(gòu) 如 下:
THTTP_FILTER_RAW_DATA = record
pvInData: Pointer; //指向數(shù)據(jù)區(qū)
cbInData: DWORD; //數(shù)據(jù)大小
cbInBuffer: DWORD; //緩沖的大小
dwReserved: DWORD; //保留
end;
---- 其 中 的pvInData 就 是 要 發(fā) 送 的 數(shù) 據(jù) 指 針。 其 他 的 結(jié) 構(gòu) 請 參 看 有 關(guān) 資 料 這 里 不 再 詳 述。
---- 第 一 個 參 數(shù)var pfc: THTTP_FILTER_CONTEXT 是 過 濾 器 的 環(huán) 境 指 針, 其 中 的pFilterContext 是 一 個 用 戶 使 用 的 指 針, 用 來 保 存 和 一 個HTTP 連 接 相 關(guān) 的 信 息, 這 樣 過 濾 器 可 以 區(qū) 分 出 正 在 處 理 的 是 否 是 以 前 曾 處 理 過 的 連 接。 因 為 一 個 請 求 將 會 產(chǎn) 生 多 個SF_NOTIFY_SEND_RAW_DATA 事 件, 過 濾 器 必 須 能 夠 區(qū) 分 他 們。
---- 程 序 的 流 程 是: 當 連 接 建 立 后,pFilterContext 被IIS 初 始 化 為NIL(0), 第 一 次SF_NOTIFY_SEND_RAW_DATA 調(diào) 用 時, 過 濾 器 要 檢 查 返 回 的MIME, 如 果 不 是HTML 則 將pFilterContext 置 為pointer(2)( 將 指 針 當 作 變 量 用, 因 為 我 們 只 要 一 個 標 志), 隨 后 的 發(fā) 送 事 件 調(diào) 用 將 直 接 返 回。 請 求 結(jié) 束 后, 發(fā) 生SF_NOTIFY_END_OF_REQUEST 事 件, 過 濾 程 序 將pFilterContext 復 位 為nil。
---- 如 果 是HTML, 則 將pFilterContext 置 為pointer(1), 隨 后 的 調(diào) 用 就 將 對 數(shù) 據(jù) 進 行 內(nèi) 碼 的 轉(zhuǎn) 換, 然 后 將pFilterContext 置 為pointer(3)。 如 果 還 有 后 續(xù) 的 調(diào) 用, 則 再 將pFilterContext 置 為pointer(1), 直 到 全 部 數(shù) 據(jù) 發(fā) 送 完 成。
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: //第一次調(diào)用,要檢查MIME
begin
pfc.pFilterContext:=pointer(2);
i:=0;
while i
----
下 面 是 完 整 的 程 序 文 件(gb2bigfiler.dpr), 其 中 的u_gb2big_tab 單 元 完 成GB 碼 到BIG5, 碼 的 轉(zhuǎn) 換, 這 里 不 再 細 述, 有 興 趣 的 讀 者 可 以 到 后 文 提 到 的 筆 者 的 站 點 去 下 載 源 碼。
library gb2bigfiler;
uses
SysUtils,math, Classes, windows,
isapi2, //delphi中ISAPI過濾器單元
u_gb2big_tab; //包含將GB碼轉(zhuǎn)換成BIG5碼的過程gb2big
//下面兩個函數(shù)的定義見上文
function HttpFilterProc(...); begin ... end;
function GetFilterVersion(...); begin ... end;
exports
HttpFilterProc index 1, GetFilterVersion index 2;
Begin end.
---- 讀 者 一 定 注 意 到 了, 這 個 過 濾 器 將 所 有 返 回 的HTML 都 轉(zhuǎn) 換 成 了BIG5 碼, 那 么GB 碼 又 如 何 看 到 呢 ? 當 然 可 以 在 過 濾 器 中 檢 查 一 些 環(huán) 境 變 量 來 決 定 用 戶 所 要 求 的 是GB 還 是BIG5, 可 是 這 樣 做 除 了 比 較 麻 煩 外, 還 存 在 效 率 問 題, 因 為 每 個 請 求 都 要 被 過 濾 器 處 理。
---- 筆 者 采 用 的 方 法 是 利 用IIS4.0 中 可 以 設(shè) 置 多 個 站 點 的 功 能, 設(shè) 置 兩 個 站 點。 一 個 不 含 過 濾 器, 所 以GB 內(nèi) 容 高 效 直 接 的 返 回 給GB 用 戶; 而 另 一 個 站 點 使 用 另 外 一 個 端 口 比 如81, 所 有 虛 擬 目 錄 和 前 一 個 站 點 一 樣, 將 過 濾 器 加 載 在 該 站 點 上, 這 樣 所 有 向81 端 口 的 請 求, 都 將 被 過 濾 器 轉(zhuǎn) 換 成BIG5 碼 返 回 給 用 戶。
---- 下 面 簡 述 一 下 具 體 配 置 過 程。 首 先 在delphi 中 選 擇 新 建 一 個DLL, 輸 入 程 序 源 碼, 編 譯 后 生 成gb2bigfiler.DLL 文 件。 在IIS4.0 的 管 理 控 制 臺 中, 選" 新 建 站 點", 主 目 錄 和 缺 省 站 點 一 樣, 端 口 設(shè) 為81, 在ISAPI 過 濾 器 中 選 擇" 添 加", 將gb2bigfiler.DLL 加 入。
---- 設(shè) 置 好 后, 可 以 瀏 覽81 端 口( 例 如:http://www.yoursite.com:81/your.html), 這 時 原 來GB 碼 的 內(nèi) 容 就 變 成 了BIG5 碼 了。
---- 有 興 趣 的 讀 者 可 以 訪 問http://202.96.122.45/qq, 起 始 頁 可 以 選 擇 內(nèi) 碼, 隨 后 瀏 覽 的 內(nèi) 容 包 括 靜 態(tài) 的HTML 文 本, 放 在 數(shù) 據(jù) 庫 中 的《 紅 樓 夢》、《 唐 詩》、 完 全 動 態(tài) 更 新 的BBS, 都 可 以 做 到 用 兩 種 內(nèi) 碼 顯 示。