Mega Code Archive

 
Categories / Delphi / .NET
 

ISAPI FILTER LOADER On the fly updating of your ISAPI filter without restarting web services

Title: ISAPI FILTER* LOADER - On the fly updating of your ISAPI filter without restarting web services Question: 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. 1. FilterLoader.dpr - Main project file 2. EggFilterLoader.pas - The update engine. 3. 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.