I was recently involved with a project in which we were able to create a custom ISAPI filter DLL to get a number of incompatible third-party products to work together peacefully.
The project involved writing new portions of a web site using JavaServer Pages (JSP) and Java servlets, while maintaining existing portions of the site in ColdFusion. The site also had to continue to function under our existing site security policy, and had to continue to work with existing development tools. I'm going to tell you about three specific problems that were encountered, and how a custom ISAPI filter was able to solve them all.
The first problem was that our servlet engine, Allaire's JRun, was not compatible with one piece of our web site security software Secure Computing's SafeWord Web Access Agent for IIS (Microsoft's Internet Information Services web server). Both JRun and SafeWord use ISAPI filters as part of their implementation. The SafeWord software screens requests to ensure that requests are coming from authorized and properly authenticated users, while JRun is an IIS add-on that allows IIS to dynamically generate web content using the Java programming language.
A problem arises because SafeWord's ISAPI filter has a low priority and JRun's ISAPI filter has a high priority. These priorities are hard-coded into the filter DLLs and are not configurable in any way. Looking at Figure 1, you can see that the JRun filter DLL will carry out its processing before the SafeWord DLL can examine the request and determine if it is from a valid, authorized user. This is obviously not what we want. It is possible to move filter DLLs up and down the list within their own priority level, but it is not possible to place a low-priority filter above a high-priority filter. We need something to allow SafeWord to screen the requests before JRun processes them.
What we want is something like what is shown in Figure 2. Here the SafeWord filter will screen the request and ensure that it is safe to proceed before it is processed by JRun. In order to do this, we will create a JRun Proxy DLL that will take the place of the JRun ISAPI filter DLL. It will have a low priority, allowing us to place the SafeWord filter above it in the list. It will allow JRun to process only requests that we determine are meant for JRun, and it will allow IIS to do what it would otherwise do if JRun was not present on our system. The JRun ISAPI filter will still be present on our system, and its exported functions will be called from the JRun Proxy filter.
The second problem was that JRun wants to process all HTTP requests. This is fine in an all-Java environment, but not good in a mixed environment such as Java and ColdFusion. Fox example, if a request is made for a ColdFusion page (.cfm extension), JRun will simply return the .cfm file's contents, server-side scripting code and all, and not HTML dynamically generated with ColdFusion. Using the JRun Proxy filter to bypass JRun for requests that are not meant for JRun solves this problem.
The third and final problem was that development tools such as Microsoft FrontPage and Microsoft Visual InterDev, which rely on FrontPage Server Extensions, are incompatible with JRun. This is because these tools POST to various FrontPage Server Extensions DLLs and JRun tries to process these requests, too. These DLLs are located in IIS virtual directories, which JRun has no knowledge of. As with the mixed-environment problem mentioned, using the JRun Proxy filter to bypass the JRun filter for requests that are not meant to be handled by JRun solves this problem (keeping in mind, of course, that it is not recommended practice to install FrontPage Server Extensions on production web servers).
JRun Proxy Filter
The JRun Proxy filter will do two things: allow control over the priority of the servlet engine filter DLL; and allow a mixed environment and the use of FrontPage Server Extensions by sending all requests that should be processed by JRun to the JRun ISAPI filter, and bypassing JRun for all other requests.
The source code for the filter is presented in Listing 1. The exported functions are typedef(ed) for easier reading. Variables are declared for the addresses of the JRun ISAPI filter's exported functions, as well as variables to hold a handle to the JRun ISAPI filter module. Operation of the proxy filter is fairly simple:
- The proxy filter exports the three standard ISAPI filter functions GetFilterVersion, HttpFilterProc, and TerminateFilter (see Listing 2).
- GetFilterVersion uses LoadLibrary to load the JRun DLL. It then uses GetProcAddress to obtain the addresses of JRun's GetFilterVersion, HttpFilterProc, and TerminateFilter. It calls JRun's GetFilterVersion and then adjusts the HTTP_FILTER_VERSION structure to allow the filter to run at a low priority while retaining the JRun filter's other settings.
- HttpFilterProc calls JRun's HttpFilterProc for requests that are to be processed by JRun. The JRun filter is otherwise bypassed.
- TerminateFilter calls JRun's TerminateFilter (if it is exported by JRun, which it currently is not) and then unloads the JRun filter DLL with FreeLibrary.
Investigation into what the JRun ISAPI filter does in its version of GetFilterVersion shows that JRun is interested in receiving notification for two events: First, IIS has preprocessed the HTTP header; and second, IIS is writing to its server log.
Therefore, we must handle notification types of SF_NOTIFY_PREPROC_HEADERS and SF_NOTIFY_LOG in the HttpFilterProc function to determine if these should be handled by IIS or JRun.
HttpFilterProc calls RequestIsForJRun for notification type SF_NOTIFY_PREPROC_HEADERS to determine if the request should be processed by JRun or IIS. RequestIsForJRun makes some simple assumptions about whether a request is to be processed by JRun. This function returns TRUE if the HTTP request is for the site's default page, a JavaServer Page, or a Java servlet.
HttpFilterProc also determines whether JRun or IIS should process events of notification type SF_NOTIFY_LOG based on the target of the HTTP command. If the target is not JRun's ISAPI filter DLL, then these events are processed by IIS. Otherwise, these events are processed by JRun.
That's all there is to it. In the interest of keeping the size of the source code for this article to a minimum, a simple version of JRunProxy filter DLL is presented in Listing 1. This filter does work, but many improvements could be made, including making it more modular, making it object oriented, removing hard-coded file names and paths, and removing the hard-coded rules to determine if JRun or IIS should process each request. w::d
Mike Monagle is a senior software Engineer at Intrado, specializing in C++ and Internet development. Mike has a Bachelor of Science in Civil Engineering from Cal Poly Pomona, a Master of Computer Information Systems from the University of Denver, and was a Nuclear Propulsion Officer in the U.S. Navy before embarking on a career in software engineering. He can be reached at [email protected].