2007. június 23., szombat
ISAPI FILTER* LOADER - On the fly updating of your ISAPI filter without restarting web services
Problem/Question/Abstract:
Writing Filters and updating them on the server is even more a pain in the butt than ISAPI extensions. If you are using Personal web server then that means you have to reboot your machine for every update. For IIS you have to go into config (on win2K) and restart web services. Doing so will first take too much of your time, and also will stop web traffic and visitors get an error trying to connect to your site. The main deal though is the pain of updating an ISAPI filter during development.
Answer:
My solution is identical in concept as my ISAPI extension loader. This isapi filter loader is an isapi filter that loads and calls your isapi filer. When you have an update, your isapi filter will be unloaded and the new one will be loaded... all on the fly without interupting your users.
How to use:
Compile or use the already compiled version of this DLL and rename it to the same name as your existing filter.
Now - rename your existing filter with a .run extension. The loader will look for this file and will load it.
Thats all, but now for the update part. When you have an update, you change the extension of your new filter to .update. The loader will look for this file and if it is found, then will unload the .run file, rename it to .backup then rename the .update to .run then load the new .run filter.
If you already had a .backup then it will be overwritten.
If you need to revert back for some reason then simply rename the .backup to .update.
The performance hit of this loader is very small I think.
One thing this loader does do, it registers most all events with the server then calls your filter only with the events you specified.
Source Listing
3 units.
FilterLoader.dpr - Main project file
EggFilterLoader.pas - The update engine.
Fn_GetModuleName.pas - Utility to return the name of the module.
FilterLoader.dpr
library FilterLoader;
{
Author
William Egge
egge@eggcentric.com
Version 1.0
Original FileName FilterLoader.dpr
Date: Sep 9, 2001
Website http://www.eggcentric.com/ISAPIFilterLoader.htm
This source code is free to distribute and modify.
This is the Filter Loader DLL main project file. The applications
intention is to be a loader for ISAPI filters to reduce development time
and headache of updating your isapi filters.
Check my website at http://www.eggcentric.com/ISAPIFilterLoader.htm
for updates or further explaining.
}
uses
ISAPI2,
Windows,
EggFilterLoader;
{$R *.RES}
var
GEggFilterLoader: IEggFilterLoader = nil;
function GetFilterVersion(var pVer: HTTP_FILTER_VERSION): BOOL; export; stdcall;
begin
try
GEggFilterLoader := nil; // Free prev if any
GEggFilterLoader := CoCreateEggFilterLoader;
Result := GEggFilterLoader.GetFilterVersion(pVer);
except // Dont crash IIS
Result := False;
end;
end;
function HttpFilterProc(var pfc: THTTP_FILTER_CONTEXT;
NotificationType: DWORD;
pvNotification: Pointer): DWORD; export; stdcall;
begin
try
Result := GEggFilterLoader.HttpFilterProc(pfc, NotificationType, pvNotification);
except // Dont crash IIS
Result := SF_STATUS_REQ_NEXT_NOTIFICATION;
end;
end;
exports
GetFilterVersion,
HttpFilterProc;
begin
end.
EggFilterLoader.pas
unit EggFilterLoader;
{
Author
William Egge
egge@eggcentric.com
Version 1.0
Original FileName EggFilterLoader.pas
Date: Sep 9, 2001
Website http://www.eggcentric.com/ISAPIFilterLoader.htm
This source code is free to distribute and modify.
This is the core updating part of my isapi filter loader. Its purpose
is to check for updates of a new isapi filter then unload the current
one and load the new one. To use, simply Create it by calling the
function CoCreateEggFilterLoader then your main isapi filter
application should forward all extension calls to the object.
}
interface
uses
ISAPI2,
Fn_GetModuleName,
SysUtils,
Windows;
type
IEggFilterLoader = interface
function GetFilterVersion(var pVer: HTTP_FILTER_VERSION): BOOL;
function HttpFilterProc(var pfc: THTTP_FILTER_CONTEXT;
NotificationType: DWORD;
pvNotification: Pointer): DWORD;
end;
function CoCreateEggFilterLoader: IEggFilterLoader;
implementation
const
// This is the time that must pass between update checks
WAIT_BEFORE_CHECK = 10000; // 10 seconds
SF_NOTIFY_SEND_RESPONSE = $00000040;
SF_NOTIFY_END_OF_REQUEST = $00000080;
ALL_FLAGS =
// SF_NOTIFY_READ_RAW_DATA
{or} SF_NOTIFY_PREPROC_HEADERS
or SF_NOTIFY_URL_MAP
or SF_NOTIFY_AUTHENTICATION
or SF_NOTIFY_ACCESS_DENIED
or SF_NOTIFY_SEND_RESPONSE
// or SF_NOTIFY_SEND_RAW_DATA
or SF_NOTIFY_END_OF_REQUEST
or SF_NOTIFY_LOG
or SF_NOTIFY_END_OF_NET_SESSION
or SF_NOTIFY_ORDER_DEFAULT
or SF_NOTIFY_SECURE_PORT
or SF_NOTIFY_NONSECURE_PORT;
type
TEggFilterLoader = class(TInterfacedObject, IEggFilterLoader)
private
FLastTimeCheck: LongWord;
FCheckSync: TMultiReadExclusiveWriteSynchronizer;
FDLLSync: TMultiReadExclusiveWriteSynchronizer;
FDLL: HModule;
FCallbackVersion: TGetFilterVersion;
FCallbackProc: THttpFilterProc;
FBackupDLLName, FRunDLLName, FUpdateDLLName: string;
FFilterFlags: DWord;
procedure ReloadDLL;
procedure DoUpdateIfNeeded;
public
constructor Create;
destructor Destroy; override;
function GetFilterVersion(var pVer: HTTP_FILTER_VERSION): BOOL;
function HttpFilterProc(var pfc: THTTP_FILTER_CONTEXT;
NotificationType: DWORD;
pvNotification: Pointer): DWORD;
end;
function CoCreateEggFilterLoader: IEggFilterLoader;
begin
Result := TEggFilterLoader.Create;
end;
{ TEggFilterLoader }
constructor TEggFilterLoader.Create;
var
ThisModule: string;
begin
inherited Create;
FDLLSync := TMultiReadExclusiveWriteSynchronizer.Create;
FCheckSync := TMultiReadExclusiveWriteSynchronizer.Create;
ThisModule := GetModuleName;
FBackupDLLName := ChangeFileExt(ThisModule, '.backup');
FRunDLLName := ChangeFileExt(ThisModule, '.run');
FUpdateDLLName := ChangeFileExt(ThisModule, '.update');
end;
destructor TEggFilterLoader.Destroy;
begin
// unload DLL
if FDLL <> 0 then
FreeLibrary(FDLL);
FDLLSync.Free;
FCheckSync.Free;
inherited;
end;
procedure TEggFilterLoader.DoUpdateIfNeeded;
var
NeedCheck, NeedLoad: Boolean;
begin
// Quick Check
FCheckSync.BeginRead;
try
NeedCheck := (GetTickCount - FLastTimeCheck) >= WAIT_BEFORE_CHECK;
finally
FCheckSync.EndRead;
end;
if NeedCheck then
begin
FCheckSync.BeginWrite;
try
// Recheck in case another thread has updated
FDLLSync.BeginRead;
try
NeedCheck := (FDLL = 0) or ((GetTickCount - FLastTimeCheck) >=
WAIT_BEFORE_CHECK);
finally
FDLLSync.EndRead;
end;
if NeedCheck then
begin
FLastTimeCheck := GetTickCount;
FDLLSync.BeginRead;
try
NeedLoad := (FDLL = 0) or FileExists(FUpdateDLLName);
finally
FDLLSync.EndRead;
end;
if NeedLoad then
ReloadDLL;
end;
finally
FCheckSync.EndWrite;
end;
end;
end;
function TEggFilterLoader.GetFilterVersion(
var pVer: HTTP_FILTER_VERSION): BOOL;
begin
DoUpdateIfNeeded;
FDLLSync.BeginRead;
try
pVer.dwFilterVersion := MakeLong(0, 1);
pVer.lpszFilterDesc := 'Eggcentric Filter Loader.';
pVer.dwFlags := ALL_FLAGS;
Result := True;
finally
FDLLSync.EndRead;
end;
end;
function TEggFilterLoader.HttpFilterProc(var pfc: THTTP_FILTER_CONTEXT;
NotificationType: DWORD; pvNotification: Pointer): DWORD;
begin
DoUpdateIfNeeded;
FDLLSync.BeginRead;
try
// Check Notification bit to make sure the DLL should be called
if Assigned(FCallbackProc) and ((NotificationType and FFilterFlags) <> 0) then
Result := FCallbackProc(pfc, NotificationType, pvNotification)
else
Result := SF_STATUS_REQ_NEXT_NOTIFICATION;
finally
FDLLSync.EndRead;
end;
end;
procedure TEggFilterLoader.ReloadDLL;
var
ShouldReload: Boolean;
pVer: THTTP_FILTER_VERSION;
begin
FDLLSync.BeginWrite;
try
// First Determine if we really should
ShouldReload := (FDLL = 0) or FileExists(FUpdateDLLName);
if ShouldReload then
begin
// First unload the DLL
if FDLL <> 0 then
begin
FreeLibrary(FDLL);
FDLL := 0;
FCallbackVersion := nil;
FCallbackProc := nil;
end;
// check for update file, if exists then rename things;
if FileExists(FUpdateDLLName) then
begin
SysUtils.DeleteFile(FBackupDLLName);
RenameFile(FRunDLLName, FBackupDLLName);
RenameFile(FUpdateDLLName, FRunDLLName);
end;
// Now load the .run file if it exists
if FileExists(FRunDLLName) then
begin
FDLL := LoadLibrary(PChar(FRunDLLName));
if FDLL <> 0 then
begin
FCallbackVersion := GetProcAddress(FDLL, 'GetFilterVersion');
FCallbackProc := GetProcAddress(FDLL, 'HttpFilterProc');
if Assigned(FCallbackVersion) then
begin
FCallbackVersion(pVer);
FFilterFlags := pVer.dwFlags;
end
else
FFilterFlags := 0;
end;
end;
end;
finally
FDLLSync.EndWrite;
end;
end;
end.
Fn_GetModuleName.pas
unit Fn_GetModuleName;
{
Author
William Egge
egge@eggcentric.com
Version 1.0
Original FileName Fn_GetModuleName.pas
Date: Sep 9, 2001
Website http://www.eggcentric.com
This source code is free to distribute and modify.
Very simple function, it returns the full path and file name of the module
it is running in.
}
interface
uses
Windows;
function GetModuleName: string;
implementation
function GetModuleName: string;
var
FileName: array[0..MAX_PATH] of char;
begin
FillChar(FileName, SizeOf(FileName), #0);
GetModuleFileName(HInstance, FileName, SizeOf(FileName));
Result := FileName;
end;
end.
Component Download: http://www.eggcentric.com/Download/ISAPIFilterLoaderSource.zip
Feliratkozás:
Megjegyzések küldése (Atom)
Nincsenek megjegyzések:
Megjegyzés küldése