Project

General

Profile

ISAPI on Microsoft IIS » History » Version 10

Wim Dumon, 08/13/2010 02:33 PM

1 1 Wim Dumon
h1. ISAPI on Microsoft IIS
2
3
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.
4
5 10 Wim Dumon
Wt beautifully integrates with IIS's asynchronous architecture, and even specialized features such as server push, the internal path API, static resources, multiple deploy paths, ... work perfectly with IIS. The Wt ISAPI connector is fully asynchronous, and therefore causes no performance or scalability problems for IIS's internal architecture.
6 1 Wim Dumon
7
h2. Using the connector
8
9
h3. Building the connector
10 3 Wim Dumon
11 10 Wim Dumon
Follow the general build instructions for [[Installing Wt on MS Windows]]. When configuring Wt with CMake, ensure that @CONNECTOR_ISAPI@ is set to true (the default). If you want to compile all examples with the ISAPI connector, set @EXAMPLES_CONNECTOR@ to @wtisapi@. By default, the examples will use the wthttpd connector, which turns every example in a standalone web server.
12 1 Wim Dumon
13 10 Wim Dumon
h3. Building your Wt application - General
14 1 Wim Dumon
15 10 Wim Dumon
To use the ISAPI connector in your own projects, the following steps are required:
16
* An ISAPI extension is a DLL, so you need to configure your project to build a DLL.
17 1 Wim Dumon
* Link to @wtisapi.lib@, in addition to @wt.lib@ and other Wt libraries such as dbo.
18 10 Wim Dumon
* Export the ISAPI symbols (@HttpExtensionProc@, @GetExtensionVersion@, @TerminateExtension@) through a @.def@ file or linker options
19
* Write a @main()@ function just like you would in a normal Wt application. The connector will invoke @main()@ when IIS loads and initializes the ISAPI extension, and @argv[0]@ contains the path of the extension DLL.
20
* Optionally, write a @DllMain()@ function.
21 1 Wim Dumon
22 10 Wim Dumon
The source code of your Wt application needs no changes to use the ISAPI connector.
23 1 Wim Dumon
24 10 Wim Dumon
h3. Important remark regarding the Current Working Directory
25
26
The CWD of your Wt application will be whatever CWD that IIS decides to assign it. You cannot control the CWD on a per-application basis. Therefore, you should avoid the use of any relative path names in your Wt application. We have implemented an 'approot' mechanism that helps you to achieve this.
27
28
'approot' is a configuration property (see @WApplication::readConfigurationProperty()@). Instead of accessing it through the @readConfigurationProperty()@ method, use the @WApplication::appRoot()@ function, or @WServer::appRoot()@ if an application object does not yet exist, e.g. in your main() function. The result of @appRoot()@ is guaranteed to end on a '/', and it will be empty if the approot property has not been set. Therefore, you can now use absolute paths anywhere you access a local file:
29
<pre>
30
messageResourceBundle().use(appRoot() + "text");
31
Wt::Dbo::backend::Sqlite3 sqlite3_(approot() + "planner.db");
32
std::ifstream file(approot() + "myfile.csv");
33
</pre>
34
35
The approot property can be set in serveral ways:
36
* Like any other property, you can set the @approot@ property in @wt_config.xml@. Add an @application-settings@ block for the path where your application is installed (this must match exactly with @argv[0]@), where a @properties@ section specifies a @property@ with name @approot@
37
* in an ini file that is placed in the same directory as your .dll. Give it the same name as your dll, and append .ini (if your app is @hello.wt.dll@, the ini file is @hello.wt.dll.ini@). The ini file contains an @isapi@ chapter with an @approot@ property. Example of a .ini file:
38
<pre>
39
[isapi]
40
approot=c:/wt/examples/blog
41
</pre>
42
The @approot@ must be set for every Wt example that uses local files (resources, database, csv files, ...) in order to run correctly. Some simple examples (like hello world) do not use local files and can be served without setting @approot@.
43
44 1 Wim Dumon
h3. Using CMake to build your application
45
46 10 Wim Dumon
We suggest the use creative use of CMake functions to quickly switch between the httpd and the ISAPI connector. For example, Wt uses a function similar to this one to define its examples:
47 1 Wim Dumon
<pre>
48
FUNCTION(WT_ADD_APPLICATION name)
49
  IF(${WT_CONNECTOR} STREQUAL "wtisapi")
50
    LIST(INSERT ARGV 1 "SHARED")
51
    ADD_LIBRARY(${ARGV})
52
    SET_TARGET_PROPERTIES(${name}
53
      PROPERTIES
54
        LINK_FLAGS
55
         "/EXPORT:HttpExtensionProc /EXPORT:GetExtensionVersion /EXPORT:TerminateExtension"
56
    )
57
  ELSE(${WT_CONNECTOR} STREQUAL "wtisapi")
58
    ADD_EXECUTABLE(${ARGV})
59
  ENDIF(${WT_CONNECTOR} STREQUAL "wtisapi")
60
  TARGET_LINK_LIBRARIES(${name} wt ${WT_CONNECTOR})
61
ENDFUNCTION(WT_ADD_APPLICATION)
62
</pre>
63
64
Wt applications can then be defined as follows:
65
<pre>
66
SET(WT_CONNECTOR
67
#  "wthttp" # uncomment if you want to use the standalone connector instead
68
  "wtisapi"
69 4 Wim Dumon
)
70 10 Wim Dumon
# now use WT_ADD_APPLICATION instead of ADD_EXECUTABLE
71 4 Wim Dumon
WT_ADD_APPLICATION(MyApp src1.C src2.C src3.C)
72
</pre>
73
74 1 Wim Dumon
An alternative approach is to always build both the http and isapi versions of your application:
75
<pre>
76
ADD_LIBRARY(MyAppAsLib STATIC src1.C src2.C src3.C)
77 4 Wim Dumon
78 1 Wim Dumon
ADD_LIBRARY(MyAppIsapi SHARED main.C MyApp.def) # Using a .def file for exported symbols instead of linker options
79
TARGET_LINK_LIBRARIES(MyAppIsapi wt wtisapi MyAppAsLib)
80
81
ADD_EXECUTABLE(MyAppHttp main.C) # Could we build apps without a single .C file?
82 4 Wim Dumon
TARGET_LINK_LIBRARIES(MyAppHttp wt wthttp MyAppAsLib)
83
</pre>
84 1 Wim Dumon
85
h3. Debugging your WtIsapi program
86
87 10 Wim Dumon
It is recommended to debug your Wt application using the http connector. This is very natural and easy to interact with(just run it from within the MSVC IDE). 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.
88 1 Wim Dumon
89 10 Wim Dumon
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 Express Edition).
90 1 Wim Dumon
91 10 Wim Dumon
The big question is of course: what process should I attach to? You can find this in the logfile of Wt. This example shows a Wt process running within the context of the process with ID 6844:
92 1 Wim Dumon
<pre>
93 10 Wim Dumon
[2010-Jul-14 15:17:43.187500] 6844 - [notice] "Reading Wt config file: c:/witty/wt_config.xml (location = '\\?\C:\wt-build\examples\hello\hello.wt.dll')"
94
</pre>
95
It is hard to find the process by name. The process name 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:
96
<pre>
97 1 Wim Dumon
  std::stringstream ss;
98
  ss << "Please attach a debugger to the process " << GetCurrentProcessId() << " and click OK" << std::endl;
99
  MessageBox(NULL, ss.str().c_str(), "Wt/ISAPI Debug Time!", MB_OK|MB_SERVICE_NOTIFICATION);
100
</pre>
101
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.
102
103 10 Wim Dumon
On Vista and Windows 7, @MessageBox()@ 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.
104 1 Wim Dumon
105 10 Wim Dumon
h2. Background information and Architecture overview
106 1 Wim Dumon
107
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.
108
109
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.
110
111 10 Wim Dumon
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.
112
When IIS is shut down, IIS calls @TerminateExtension()@ and the Wt server will be terminated. This means that all WApplication objects will be properly destroyed when IIS shuts down in a normal way.
113 5 Wim Dumon
114 10 Wim Dumon
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 Wt 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 eventually end up in a process that did not initiate the session, causing failing (restarting) web applications. Wt applications do not suffer from not running in 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. This is the @num-threads@ option in the @connector-isapi@ section of wt_config.xml.
115 1 Wim Dumon
116
IIS does not foresee a method to unload an ISAPI extension: once loaded, it is loaded until the server shuts down.
117 2 Wim Dumon
118 1 Wim Dumon
h2. Configuring Microsoft Internet Information Services
119 9 Wim Dumon
120
General remarks:
121
* 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 (e.g. by using WApplication::approot() consistently throughout your application) if you want to deploy them as ISAPI applications.
122
* The IIS runs with other privileges than normal user. This means that it may be impossible for IIS to access certain files on your system. Especially those examples requiring access to SQLite databases in Wt will not be able to create/modify a database unless you modify the access rights to the example's directory.
123 7 Wim Dumon
124 1 Wim Dumon
125 7 Wim Dumon
h3. IIS 7.5 (Windows 7)
126
127 1 Wim Dumon
h4. Configuring the Application Pool
128 7 Wim Dumon
129
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):
130
* Set 'Maximum Worker Processes' (maxProcesses) to 1 (=default). This is mandatory for Wt.
131
* 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.
132
* 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.
133
* 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.
134
* Set all 'Generate Recycle Event Log Entry' to true to ease debugging.
135 1 Wim Dumon
136
h4. Running the examples
137 7 Wim Dumon
138
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:
139
<pre>
140
for i in `find . -name \*dll`; do cp $i ../wt/`echo $i | sed s/Debug//`; done
141
</pre>
142
143
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:
144
<pre>
145
for i in `find examples -type d`; do cp -r resources $i; done
146
</pre>
147
148 10 Wim Dumon
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 [[Frequently Asked Questions]] on this wiki.
149 7 Wim Dumon
150
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).
151 1 Wim Dumon
152 7 Wim Dumon
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.
153
154
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'.
155
156 10 Wim Dumon
Now there's one more issue left: access of local files. Many examples require access to resource bundles (.xml files), csv files, sqlite3 .db files, ... As described in the 'Important remark regarding the Current Working Directory' section above, the examples can't just use CWD. Wt examples use the @appRoot()@ function to transform every relative local path to an absolute path, but you must set the approot variable correctly. The easiest method is to add a .ini file in every directory with the appropriate approot setting. Additionally, some examples use dbo to create sqlite3 databases. The IIS process can normally not write to the directories where these databases are located, so you must give the user of the IIS process write rights to those example directories. Examples that use dbo databases are blog, planner and wt-homepage (this list may be outdated at any time). Fix the permissions of the approot for those examples, so that the IIS process is granted sufficient rights to create and write to the databases.
157
158 1 Wim Dumon
h3. IIS 7 (Vista)
159
160
h3. IIS 5.1 (Windows XP)
161
162 8 Wim Dumon
h4. Configuring the Application Pool
163
164
IIS 5.1 does not have application pools like the newer versions. But you can define an 'Application Protection' level, which can be set to one of three choices:
165
* Low - IIS Process: Select this if you want your Wt application to run in the same address space (application) as IIS. This is the fastest choice, but if your application crashes, it will take the IIS server down with it. IIS will restart automatically when this happens.
166
* Medium - Pooled: Select this if you want all ISAPI extensions (including your Wt applications) to run in a child process of IIS. Any crashing ISAPI extension will cause all other ISAPI extensions to terminate as well. The cost of this setting is that the HTTP traffic needs to be transfered to the child application by means of an IPC mechanism.
167
* High - Isolated: With this option, every ISAPI extension runs in its own process. A crash in one ISAPI extension does not affect the other extensions. The extra overhead compared to the 'Pooled' setting is that you need slightly more memory.
168
169
A Wt ISAPI extension will run correctly with any of the three settings. We recommend to use the 'High' setting until you have thoroughly tested your application.
170
171
h4. Running the examples
172
173 10 Wim Dumon
This is similar to the description above for IIS 7.5: copy the DLLs from the build directory to the examples src directory, create a virtual directory that points to the examples directory, and allow execution of ISAPI. Make sure to enable the 'Execute' rights on the Virtual Directory when you create it (Right-click on 'Default Web Site', New -> Virtual Directory). As described for IIS 7.5, some examples require extra dependencies (ext, tinymce), which should be copied to the src directory or the examples will not work. Also give the IIS user write rights to the dbo examples directories in order to create and update the database files.
174 8 Wim Dumon
175
Allow 'Directory Browsing' in the virtual directory (right-click the virtual directory -> Properties -> Virtual Directory -> check Directory Browsing) to allow to browse through the examples.
176
177 1 Wim Dumon
h2. Compatibility with other web servers
178
179 10 Wim Dumon
The connector was not designed to work with other web servers. More specifically, @wtisapi.lib@ will not work with Apache due to how Apache handles processes and threads. When using Apache, we recommend to configure it as a reverse proxy to talk to forward requests to the built-in httpd connector.