ISAPI on Microsoft IIS » History » Revision 7
Revision 6 (Wim Dumon, 08/06/2010 12:47 PM) → Revision 7/17 (Wim Dumon, 08/06/2010 02:34 PM)
h1. ISAPI on Microsoft IIS This page is work in progress. The ISAPI interface is not yet fully released - it will be in the upcoming Wt 3.1.4. From Wt 3.1.4 onward, Wt is delivered with an ISAPI connector for easy integration with Microsoft IIS. ISAPI was preferred above FCGI on Windows because existing FCGI implementations (client libraries and servers) turned out to result in suboptimal solutions. Note that Wt does not support FCGI on Windows. Wt beautifully integrates with IIS's asynchronous architecture, and even specialized features such as server push, the internal path API, ... work perfectly scalable with IIS. The Wt ISAPI connector is fully asynchronous, and therefore causes no scalability problems for IIS's internal architecture. h2. Using the connector h3. Building the connector When configuring Wt with CMake, set @CONNECTOR_ISAPI@ to true. If you want to compile all examples with the ISAPI connector, set @EXAMPLES_CONNECTOR@ to @wtisapi@. h3. Building the application - General To use the connector, the following steps are required: * Configure to build a DLL * Link to @wtisapi.lib@, in addition to @wt.lib@ and other Wt libraries such as dbo. * Export the ISAPI symbols (HttpExtensionProc, GetExtensionVersion, TerminateExtension) through a .def file or linker options * Write a @main()@ function just like you would in a normal Wt application. The connector will invoke @main()@ and @argv[0]@ contains the path of the DLL. * Optionally, write a @DllMain()@ function The source code needs no changes to use the ISAPI connector. h3. Using CMake to build your application We suggest the use of the following CMake function to quickly switch between the httpd and the isapi connector. <pre> FUNCTION(WT_ADD_APPLICATION name) IF(${WT_CONNECTOR} STREQUAL "wtisapi") LIST(INSERT ARGV 1 "SHARED") ADD_LIBRARY(${ARGV}) SET_TARGET_PROPERTIES(${name} PROPERTIES LINK_FLAGS "/EXPORT:HttpExtensionProc /EXPORT:GetExtensionVersion /EXPORT:TerminateExtension" ) ELSE(${WT_CONNECTOR} STREQUAL "wtisapi") ADD_EXECUTABLE(${ARGV}) ENDIF(${WT_CONNECTOR} STREQUAL "wtisapi") TARGET_LINK_LIBRARIES(${name} wt ${WT_CONNECTOR}) ENDFUNCTION(WT_ADD_APPLICATION) </pre> Wt applications can then be defined as follows: <pre> SET(WT_CONNECTOR # "wthttp" # uncomment if you want to use the standalone connector instead "wtisapi" ) WT_ADD_APPLICATION(MyApp src1.C src2.C src3.C) </pre> An alternative approach is to always build both the http and isapi versions of your application: <pre> ADD_LIBRARY(MyAppAsLib STATIC src1.C src2.C src3.C) ADD_LIBRARY(MyAppIsapi SHARED main.C MyApp.def) # Using a .def file for exported symbols instead of linker options TARGET_LINK_LIBRARIES(MyAppIsapi wt wtisapi MyAppAsLib) ADD_EXECUTABLE(MyAppHttp main.C) # Could we build apps without a single .C file? TARGET_LINK_LIBRARIES(MyAppHttp wt wthttp MyAppAsLib) </pre> h3. Debugging your WtIsapi program Generally, it is recommended to debug your Wt application using the http connector. This is very natural (just run it in MSVC) and easy to interact with. Your development cycle will be shorter than when you take IIS in the picture. Remember, it is easy to switch between the http and isapi connector: just link to a different library. For some problems, you may need to debug your application in the same way as it is deployed in IIS. A google search will yield many options to debug an ISAPI application. In essence, the easiest way to debug an ISAPI extension is to attach your debugger to a running process (MSVC: Debug->Attach to process. Note that this feature is available in MSVC Express Editions version 2005 and 2008, but was removed from the 2010 version). The big question is of course: what process should I attach to? This depends on your IIS version and deployment configuration. You can look this up in the manual of your IIS, and still gamble between processes with identical names, or you could let your application tell you what the correct process ID is. I particularly liked the following code snippet for this purpose: <pre> std::stringstream ss; ss << "Please attach a debugger to the process " << GetCurrentProcessId() << " and click OK" << std::endl; MessageBox(NULL, ss.str().c_str(), "Wt/ISAPI Debug Time!", MB_OK|MB_SERVICE_NOTIFICATION); </pre> You can add this to your @main()@ function, but if it is added to @GetExtensionVersion()@ in @src/isapi/Isapi.C@ (you'll have to recompile Wt then), this even waits for you to connect at the moment that your DLL is first loaded. On Vista and Windows 7, this will send the message to the event log rather than a dialog on the screen. You may have more success using @WTSSendMessage()@ on those systems. h2. Architecture overview IIS expects a DLL to run the web application, whereas Wt's other connectors (httpd, fcgi) result in an executable. This means that there are a few important points to take into account when using this connector (e.g. build a DLL instead of an EXE). On the other hand, we made sure that the connectors can still be interchanged, meaning that you're not required to make any changes to your Wt application in order to use the new connector. In Wt's architecture, the connector is a library that is separate from the rest of the system. The same is true for ISAPI: the connector library is @wtisapi.lib@. This is a static library which provides the entry functions for ISAPI (@GetExtensionVersion()@, @TerminateExtension()@, @HttpExtensionProc()@). IIS will search for these functions in your DLL so you must make sure that your DLL exports them. That is also one of the reasons why wtisapi is only available as a static library: the symbols must be embedded in your DLL. Upon initialization, IIS calls @GetExtensionVersion()@. The connector starts a thread, which invokes the @main()@ function of your Wt application. During this invocation, argc will be 1 and argv[0] contains the path of the ISAPI DLL. This thread keeps running until the server is terminated and main() returns. When IIS is shut down, IIS calls @TerminateExtention()@ and the Wt server will be terminated. This means that all applications will be properly destroyed when IIS shuts down in a normal way. The Wt ISAPI application runs inside a server process. Whereas it is unimportant whether the extension runs inside the servers process or in a separate process, it is important to ensure that all HTTP requests for a single application will be served by the same process. It is therefore mandatory to properly configure IIS6/7 to avoid the use of a so-called Web Garden for the Wt application. If you do use a Web Garden, requests will end up in processes that did not initiate the session, causing failing (restarting) web applications. This is not a problem, as a stable Wt application has no benefit from a web garden. If you want a Wt application to benefit from multi-core servers, modify the number of worker threads, which is by default 10 (FIXME: configuration file not yet implemented). IIS does not foresee a method to unload an ISAPI extension: once loaded, it is loaded until the server shuts down. h2. Configuring Microsoft Internet Information Services General remark: As an ISAPI extension is loaded inside the IIS process, it is impossible to specify the CWD (current work directory) of your application. Make sure that all files you access from within your application use absolute paths if you want to deploy them as ISAPI applications. h3. IIS 7.5 (Windows 7) h4. Configuring the Application Pool While the default configuration of application pools on IIS 7.5 is pretty decent for Wt, we recommend to make a few changes (select your application pool, right-click on it, and select 'Advanced Settings...'). Verify the following settings (in order of importance): * Set 'Maximum Worker Processes' (maxProcesses) to 1 (=default). This is mandatory for Wt. * Set 'Regular Time Interval (minutes)' (time) to 0. By default, this is not set to 0, so your Wt application will be killed and restarted every so often. * Also set 'Private Memory Limit (KB)' (privateMemory), 'Request Limit' (requests) and 'Virtual Memory Limit (KB)' (memory) to 0 (the default). If not, you risk that your Wt application will be killed from time to time. * Consider to set 'Disable Overlapped Recycle' (disallowOverlappingRotation) to true (default is false). This depends on your application: if it does not mind being started before an older instance exits, you can keep this to false. * Set all 'Generate Recycle Event Log Entry' to true to ease debugging. h4. Running the examples Wt examples are best executed from within their source directory. Therefore, we recommend to copy the .dll files to the source example folders after building. Using cygwin, you can do this with this command, executed from within the build directory: <pre> for i in `find . -name \*dll`; do cp $i ../wt/`echo $i | sed s/Debug//`; done </pre> Most examples require files from the 'resources' directory to run correctly. Simply copy the @wt-x.y.z/resources@ folder to every example directory. You can do this with many mouse clicks, or with a single line in a cygwin shell: <pre> for i in `find examples -type d`; do cp -r resources $i; done </pre> Some examples need extra dependencies: ExtJS (widgetgallery, extkitchen) and tinyMCE (widgetgallery). These are not delivered with Wt; download them from the appropriate place and have a look in the FAQ on this wiki. Next, add a virtual directory to your server that points to all the examples. In the Internet Information Services Manager, right-click on your web site (e.g. Default Web Site), select 'Add Virtual Directory', choose an alias (e.g. examples), and point the 'Physical Path' to the wt examples source directory (wt-x.y.z/examples). It is easier to browse through the examples if you enable directory browsing: click on your virtual folder ('examples'), double-click the 'Directory Browsing' icon, and click 'Enable' in the right column. Now you can browse the examples folder (surf to http://localhost/examples), but you will get a 404.2 error when you click a DLL. Every ISAPI DLL must be explicitly granted execute permissions in IIS. Click on your IIS machine (toplevel icon in the tree on the left), double-click the 'ISAPI and CGI Restrictions' icon. A cautious system administrator adds every single DLL to this list, but a brave (or lazy) one clicks 'Edit Feature Settings...' in the right column and selects 'Allow Unspecified ISAPI Modules'. h3. IIS 7 (Vista) h3. IIS 5.1 (Windows XP) h2. Compatibility with other web servers The connector was not designed to work with other web servers. @wtisapi.lib@ will not work with Apache due to how Apache handles processes and threads.