<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>David Wang : ISAPI</title><link>http://blogs.msdn.com/david.wang/archive/tags/ISAPI/default.aspx</link><description>Tags: ISAPI</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>HOWTO: PreCondition an ISAPI Extension DLL</title><link>http://blogs.msdn.com/david.wang/archive/2008/10/30/howto-precondition-an-isapi-extension-dll.aspx</link><pubDate>Fri, 31 Oct 2008 08:00:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9025890</guid><dc:creator>David.Wang</dc:creator><slash:comments>2</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/9025890.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=9025890</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=9025890</wfw:comment><description>&lt;H3&gt;Question:&lt;/H3&gt;&lt;SPAN style="COLOR: #008000; FONT-FAMILY: Courier New"&gt;
&lt;P&gt;Hello&lt;/P&gt;
&lt;P&gt;We have a legacy x64 ISAPI extension that runs without problems in Win 2003 64bit - IIS 6.0.&lt;/P&gt;
&lt;P&gt;But in Win2008 EBS Security Manager setup, IIS 7.0 Default Application Pool's WOW64 / Enable32BitApp property is enabled by default, and this makes x64 ISAPI extension DLL to fail loading.&lt;/P&gt;
&lt;P&gt;By setting the Enable32BitApp option as false in application pool settings, the problem gets resolved.&lt;/P&gt;
&lt;P&gt;However we want to speicify ISAPI extension explicitly to be loaded as x64 module using "precondition='bitness64' " settings like ISAPI filters, so that ISAPI extension works no matter what application pool is configured for (x64 only, or both x64 and x86)&lt;/P&gt;
&lt;P&gt;Could anyone please suggest how we can do this or the app cmd that would allow us to do it.&lt;/P&gt;&lt;/SPAN&gt;
&lt;H3&gt;Answer:&lt;/H3&gt;
&lt;P&gt;Technically, preConditions only apply on a per-GlobalModule, per-Module, per-Handler, or per-ISAPI Filter basis. This is because globalModules, modules, handlers, and ISAPI Filters have to be explicitly configured within IIS to function. ISAPI Extensions simply need to be dropped into a web-accessible folder and then "allowed to execute" via Executable execution permission and IsapiCgiRestriction. There is no per-ISAPI Extension configuration to stick preConditions.&lt;/P&gt;
&lt;P&gt;However, as I will illustrate shortly, it is STILL possible to&amp;nbsp;apply preConditions on a per-ISAPI Extension basis in a fairly clean and clear manner.&lt;/P&gt;
&lt;P&gt;Some people would propose that it would be nice to stick&amp;nbsp;preCondition onto IsapiCgiRestriction since it is a sort of per-ISAPI Extension configuration, but IsapiCgiRestriction is really a security configuration which we hardly want to preCondition. Besides, what happens if notListedIsapisAllowed="true" and we still want to preCondition an ISAPI Extension - now we have to add a bogus entry for preCondition? Anyways, this is getting complicated very quickly, a sure sign that the proposed design has some fundamental logical flaw.&lt;/P&gt;
&lt;H4&gt;Common Misconceptions about Bitness and preConditions&amp;nbsp;&lt;/H4&gt;
&lt;P&gt;Now, before I get into how to setup per-ISAPI Extension preCondition, I want to clarify some of the misconceptions in your question about Bitness and preCondition. Using preCondition="bitness64" does NOT:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Explicitly load an ISAPI Extension as x64 module&lt;/LI&gt;
&lt;LI&gt;Make an ISAPI work no matter what the application pool is configured for&lt;/LI&gt;
&lt;LI&gt;Allow "both x64 and x86" of ANYTHING&lt;/LI&gt;&lt;/OL&gt;
&lt;P mce_keep="true"&gt;A preCondition is a simple filtering mechanism on the affected IIS configuration. What preCondition="bitness64" means is that the affected configuration is ONLY applicable in a 64bit Application Pool.&amp;nbsp;A 64bit Application Pool will only see globalModule, module, handler, and isapiFilter with either bitness64 or no bitness preCondition. It will not see any globalModule, module, handler, and isapiFilter configuration with the bitness32 preCondition.&lt;/P&gt;
&lt;P mce_keep="true"&gt;Thus, a preCondition does NOT cause an ISAPI to be explicitly loaded as any bitness. LoadLibrary() will just load the resolved DLL name into the process's address space, and if the bitness do not match, fail. Likewise, it cannot make the ISAPI work no matter what, because if the bitness does not match, it fails. Finally, Windows does not allow both 32bit and 64bit code to co-exist in the same process, and IIS does not allow an Application Pool to be both 32bit and 64bit.&lt;/P&gt;
&lt;P mce_keep="true"&gt;What is a correct way to use the bitness preConditions? Check out the various pre-defined "-64" handlers when ASP.Net Feature support is installed. For example, the svc-ISAPI-2.0 and svc-ISAPI-2.0-64 handlers. Notice that the bitness32 preCondition applies to a 32bit ISAPI, and bitness64 preCondition applies to a separate 64bit ISAPI. This dual configuration allows the same feature to work with both 32bit and 64bit Application Pools with no additional configurations changes in-between. Remember how you had to run aspnet_regiis.exe from the correct Framework bitness directory every time you changed Application Pool bitness with .Net Framework 2.0 on IIS6 in 64bit Windows, or else you would see 503 Service Unavailable failure? No longer necessary with preConditions on IIS7 and things just work!&lt;/P&gt;
&lt;H4&gt;PreConditioning an ISAPI Extension DLL&lt;/H4&gt;
&lt;P&gt;As I mentioned earlier, per-ISAPI Extension preCondition can be accomplished by leveraging preCondition support of handlers. The following configuration shows how to request an ISAPI Extension name "MyISAPI.dll" and have it work no matter the bitness of the Application Pool. It requires a 32bit version and 64bit version of the same DLL, named MyISAPI32.dll and MyISAPI64.dll, respectively.&lt;/P&gt;&lt;PRE&gt;&amp;lt;handlers&amp;gt;
  ...
  &amp;lt;add name="MyISAPI-64" path="MyISAPI.dll" verb="*" modules="IsapiModule" scriptProcessor="C:\inetpub\wwwroot\bin\MyISAPI64.dll" resourceType="File" requireAccess="Execute" preCondition="bitness64" /&amp;gt;
  &amp;lt;add name="MyISAPI-32" path="MyISAPI.dll" verb="*" modules="IsapiModule" scriptProcessor="C:\inetpub\wwwroot\bin\MyISAPI32.dll" resourceType="File" requireAccess="Execute" preCondition="bitness32" /&amp;gt;
  ...
&amp;lt;/handlers&amp;gt;&lt;/PRE&gt;
&lt;P&gt;Looking at the key configuration details:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Having two handlers, one with bitness32 preCondition on the 32bit MyISAPI32.dll, the other with bitness64 preCondition on the 64bit MyISAPI64.dll, assures that only one of these handlers with the correct bitness apply to any given Application Pool&lt;/LI&gt;
&lt;LI&gt;path="MyISAPI.dll" means that end consumers only request /MyISAPI.dll while the dynamic 32/64bit wiring happens underneath the covers via the bitness preCondition&lt;/LI&gt;
&lt;LI&gt;requireAccess="Execute" makes Execute permissions necessary to execute an ISAPI Extension DLL in a given URL namespace&lt;/LI&gt;&lt;/UL&gt;
&lt;P mce_keep="true"&gt;The combination of preConditions, path-remapping, and requireAccess gives the illusion of a&amp;nbsp;single named ISAPI DLL which transparently works with similar requirements as ISAPI Extension&amp;nbsp;regardless of Application Pool bitness.&amp;nbsp;Yes, don't forget to set IsapiCgiRestriction on both MyISAPI64.dll and MyISAPI32.dll as well as enable "Execute" permission on your virtual directory... :-)&lt;/P&gt;
&lt;P mce_keep="true"&gt;Cheers.&lt;/P&gt;
&lt;P mce_keep="true"&gt;//David&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9025890" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/ISAPI/default.aspx">ISAPI</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Tips/default.aspx">Tips</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/HOWTO_2E002E002E00_/default.aspx">HOWTO...</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS7/default.aspx">IIS7</category></item><item><title>Managed Modules and ISAPI Filters</title><link>http://blogs.msdn.com/david.wang/archive/2008/09/08/managed-modules-and-isapi-filters.aspx</link><pubDate>Tue, 09 Sep 2008 07:36:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8935722</guid><dc:creator>David.Wang</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/8935722.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=8935722</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=8935722</wfw:comment><description>&lt;H3&gt;Question:&lt;/H3&gt;&lt;SPAN style="COLOR: #008000; FONT-FAMILY: courier new"&gt;
&lt;P&gt;Hello,&lt;/P&gt;
&lt;P&gt;I have an isapi filter and a managed module. I need to put both in the same website and I need that the manage module run before the isapi filter. The sequence are: Module---&amp;gt;ISAPI filter. How can I do this? &lt;/P&gt;&lt;/SPAN&gt;
&lt;H3&gt;Answer:&lt;/H3&gt;
&lt;P&gt;The answer really depends on the filter events that the ISAPI Filter subscribes to.&lt;/P&gt;
&lt;P&gt;Unfortunately, no built-in IIS UI or tool displays this information since it is rarely of interest to the user.&lt;/P&gt;
&lt;P&gt;However, you can use my tool from &lt;A class="" href="http://blogs.msdn.com/david.wang/archive/2006/03/02/HOWTO_Add_and_Remove_an_ISAPI_Filter_using_JScript.aspx" target=_blank mce_href="http://blogs.msdn.com/david.wang/archive/2006/03/02/HOWTO_Add_and_Remove_an_ISAPI_Filter_using_JScript.aspx"&gt;here&lt;/A&gt; to view the events that an ISAPI Filter subscribes for. To the astute reader - this filter status&amp;nbsp;information is only available AFTER IIS successfully loads an ISAPI Filter (i.e. IIS successfully LoadLibrary(), GetProcAddress() the Filter DLL's GetFilterVersion() exported function, executes it&amp;nbsp;for registered events, and the function returns TRUE to IIS), and depending on IIS version/mode and the type of ISAPI Filter, IIS ends up loading an ISAPI Filter at different times. The history and rationale behind the differences is an entire blog entry all to itself, but the following table&amp;nbsp;is a sufficient summary for now:&lt;/P&gt;
&lt;TABLE class="" border=1&gt;
&lt;TBODY&gt;
&lt;TR bgColor=#000000&gt;
&lt;TD class=""&gt;&lt;FONT color=#ffffff&gt;IIS Version (Mode)&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=""&gt;&lt;FONT color=#ffffff&gt;Global Filter Loads...&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=""&gt;&lt;FONT color=#ffffff&gt;Site Filter Loads...&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class=""&gt;IIS4 / IIS5 / IIS5.1&lt;/TD&gt;
&lt;TD class=""&gt;On Server Startup&lt;/TD&gt;
&lt;TD class=""&gt;When Configured for a Website&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class=""&gt;IIS6 (IIS5 Compatibility Mode)&lt;/TD&gt;
&lt;TD class=""&gt;On Server Startup&lt;/TD&gt;
&lt;TD class=""&gt;When Configured, on first request to that Website&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class=""&gt;IIS6 (Worker Process Isolation Mode)&lt;/TD&gt;
&lt;TD class=""&gt;On w3wp.exe Startup&lt;/TD&gt;
&lt;TD class=""&gt;When Configured, on first request to that Website&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class=""&gt;IIS7 (Classic and Integrated Modes)&lt;/TD&gt;
&lt;TD class=""&gt;Same as IIS6 Worker Process Isolation&amp;nbsp;Mode&lt;/TD&gt;
&lt;TD class=""&gt;Same as IIS6 Worker Process Isolation Mode&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;H4&gt;It's all about Timing&lt;/H4&gt;
&lt;P mce_keep="true"&gt;Now, you may wonder WHY knowing the subscribed filter events affect the answer. As in life and most things in our four dimensional world, it's all about timing, and this situation is no exception.&lt;/P&gt;
&lt;P mce_keep="true"&gt;ISAPI Filter triggers on various events fired by IIS throughout a request's processing, while Managed Modules trigger after only one of those events (and in IIS7 in Integrated Pipeline Mode, Managed Modules trigger on ALMOST all of the events). Thus, if you want the Managed Module to run before the ISAPI Filter, the ISAPI Filter's subscribed events must be limited to those that happen AFTER&amp;nbsp;the Module triggers.&lt;/P&gt;
&lt;P&gt;Since Modules trigger pretty late in the request processing, right before response generation and logging, and ISAPI Filters typically trigger early in the request process, to perform either custom authentication, URL rewriting, etc, it is highly likely that what you want to do is impossible&amp;nbsp;on any IIS version&amp;nbsp;- without knowing the exact filter events involved, I cannot be definitive.&lt;/P&gt;
&lt;P&gt;The following is a condensed outline of how ISAPI Filter and Managed Modules triggering are ordered:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;On IIS4, IIS5, IIS5.1, and IIS6, Managed Modules trigger between the SF_NOTIFY_AUTH_COMPLETE and SF_NOTIFY_SEND_RESPONSE filter events.&lt;/LI&gt;
&lt;LI&gt;On IIS7 in Classic Pipeline Mode, things behave as in IIS6.&lt;/LI&gt;
&lt;LI&gt;On IIS7 in Integrated Pipeline Mode, Managed Modules trigger in-line with all ISAPI Filter events in all Pipeline Modes (Classic and Integrated) because ISAPI Filters are actually just DLLs loaded by the "ISAPI Filter" Module, which acts as a shim to trigger appropriate ISAPI Filter events for the corresponding&amp;nbsp;Module events of the Integrated Pipeline. For example, the "ISAPI Filter Module" subscribes for the OnPreBeginRequest event, which when triggered by IIS will cause it to fire the SF_NOTIFY_PREPROC_HEADERS filter event to all applicable ISAPI Filters for that URL scope.&lt;/LI&gt;&lt;/UL&gt;
&lt;H4&gt;Conclusion&lt;/H4&gt;
&lt;P&gt;In general, if your ISAPI filter does NOT subscribe to events earlier than SF_NOTIFY_SEND_RESPONSE, it would be possible for a Managed Module to execute before the ISAPI filter triggers.&lt;/P&gt;
&lt;P&gt;You should notice some direct correlations between the Module events of the IIS7 Integrated Pipeline and a merging of the ISAPI Filter events and classic ASP.Net HttpModule events. This is intentional - that is what we meant with the name "Integrated" Pipeline! :-)&lt;/P&gt;
&lt;P&gt;The astute reader should note that Managed Modules on IIS7 do not have access to the OnPreBeginRequest module event. Since that event is used by the "ISAPI Filter" Module to trigger the SF_NOTIFY_PREPROC_HEADERS event, this means that even in Integrated Pipeline mode, where Managed Modules trigger in-line with any other module&amp;nbsp;such as&amp;nbsp;the "ISAPI Filter" Module shim, a Managed Module will NOT be able to execute before an ISAPI Filter that subscribes to the SF_NOTIFY_PREPROC_HEADERS event.&lt;/P&gt;
&lt;P&gt;Yes, there is a huge story behind why OnPreBeginRequest even exists and why Managed Modules do not have access to that event (and other such global notification events). The blurbs on MSDN simply does not do it justice...&lt;/P&gt;
&lt;P&gt;But at long last, here is the long-winded response to it all.&lt;/P&gt;
&lt;P&gt;Cheers!&lt;/P&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8935722" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/ISAPI/default.aspx">ISAPI</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS7/default.aspx">IIS7</category></item><item><title>HOWTO: Change IIS User Impersonation Token</title><link>http://blogs.msdn.com/david.wang/archive/2008/06/02/howto-change-iis-user-impersonation-token.aspx</link><pubDate>Mon, 02 Jun 2008 10:30:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8568348</guid><dc:creator>David.Wang</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/8568348.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=8568348</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=8568348</wfw:comment><description>&lt;H3&gt;Question:&lt;/H3&gt;&lt;SPAN style="COLOR: #008000; FONT-FAMILY: Courier new"&gt;
&lt;P&gt;Hi,&lt;/P&gt;
&lt;P&gt;We are using our third party component for doing authentication and authorization with IIS6 web server on win2k3 X64 EE. Here we are using IMPERSONATION concept for this integration.&lt;/P&gt;
&lt;P&gt;Can anybody describe the required configuration which are needed at IIS 6 for successfully impersonation of users with third party component?&lt;/P&gt;
&lt;P&gt;Should we need to set Anonymous authentication explicitely for this kind of configuration?&lt;/P&gt;&lt;/SPAN&gt;
&lt;H3&gt;Answer:&lt;/H3&gt;
&lt;P&gt;Third party code will not be able to directly impersonate and have IIS use that user token. IIS will:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Strip off the impersonation token after ISAPI Extension returns control to it&lt;/LI&gt;
&lt;LI&gt;Ignore and re-apply IIS's desired impersonation token if ISAPI Filter changed it&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Thus, the only way for you to impersonate users with IIS is to change IIS's desired impersonation token. The following are the methods, in no particular order, that ISAPI can change IIS's desired impersonation token and hence control impersonated user. The method you choose depends on your authentication protocol's requirements.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;ISAPI Filter - SF_NOTIFY_PREPROC_HEADERS&lt;/STRONG&gt; event - change the "Authorization" request header value to be: Basic [base64 encoding of username:password] . Requires Basic Authentication enabled in IIS.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;ISAPI Filter - SF_NOTIFY_AUTHENTICATION&lt;/STRONG&gt; event - change pszUser and pszPassword fields on HTTP_FILTER_AUTHENT. Remember to SET your values&amp;nbsp;into the provided buffers (and change the cbUserBuff/cbPasswordBuf values) because those are the actual buffers IIS will use. Requires Anonymous or Basic Authentication enabled in IIS.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;ISAPI Extension - HSE_REQ_EXEC_URL&lt;/STRONG&gt; configured as Wildcard Application Mapping - change hImpersonationToken field on HSE_EXEC_URL_USER_INFO to be the actual NT User Token used by IIS for request impersonation. Requires Anonymous Authentication enabled in IIS.&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8568348" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/ISAPI/default.aspx">ISAPI</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/HOWTO_2E002E002E00_/default.aspx">HOWTO...</category></item><item><title>Internet Explorer is not an HTTP Validator</title><link>http://blogs.msdn.com/david.wang/archive/2006/08/17/Internet-Explorer-is-not-an-HTTP-Validator.aspx</link><pubDate>Fri, 18 Aug 2006 09:45:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:704859</guid><dc:creator>Anonymous</dc:creator><slash:comments>4</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/704859.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=704859</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=704859</wfw:comment><description>&lt;H3&gt;Question:&lt;/H3&gt;&lt;FONT face="courier new" color=#008000&gt;
&lt;P&gt;I tried a 2nd approach in porting client code from WinInet, and that was to utilize managed C++, as opposed to WinHttp.&lt;/P&gt;
&lt;P&gt;After implementing the .NET managed client code...&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; HttpWebRequest^ myReq = dynamic_cast&amp;lt;HttpWebRequest^&amp;gt;(WebRequest::Create( strTargetURL ));&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; myReq-&amp;gt;Method = "POST";&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ...&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; HttpWebResponse^ HttpWResp = dynamic_cast&amp;lt;HttpWebResponse^&amp;gt;(myReq-&amp;gt;GetResponse());&lt;/P&gt;
&lt;P&gt;Within the code, GetResponse() throws...&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; An unhandled exception of type 'System.Net.WebException' occurred in System.dll&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Additional information: The server committed a protocol violation. Section=ResponseStatusLine&lt;/P&gt;
&lt;P&gt;&lt;BR&gt;On the Windows 2003 R2 SP1 server, the ISAPI extension has been simplified to the following...&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DWORD dwPageSize, dwBytes;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; char szPage[] = "We're good to go";&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; dwPageSize = (DWORD) strlen(szPage);&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return ( pECB-&amp;gt;WriteClient(pECB-&amp;gt;ConnID, (LPVOID) szPage, &amp;amp;dwPageSize, 0) ) ? HSE_STATUS_SUCCESS : HSE_STATUS_ERROR;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/P&gt;
&lt;P&gt;Any help would be apreciated enabling more clients whether they are WinHttp or .NET clients, the ability to POST to the ISAPI extension.&lt;/P&gt;
&lt;P&gt;Thanks,&lt;/P&gt;&lt;/FONT&gt;
&lt;H3&gt;Answer:&lt;/H3&gt;
&lt;P&gt;Actually, the problem you observe comes from your ISAPI Extension DLL. It is actually sending an invalid HTTP response without an entity body, and the APIs correctly complain. Let me explain.&lt;/P&gt;
&lt;H4&gt;Problem Restated&lt;/H4&gt;
&lt;P&gt;My understanding of your facts:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;You have an ActiveX DLL using WinInet which successfully POSTs to an ISAPI Extension 
&lt;LI&gt;You have a Windows Service which fails to POST to the exact same ISAPI Extension using either WinHttp or .NET Client&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;Based on that information, I hypothesize either:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;The ISAPI Extension or other Server Side ISAPI Filter is doing something wrong, but WinInet ignores or covers it up while WinHttp/.NET Client correctly fail 
&lt;LI&gt;The ISAPI Extension or other Server Side ISAPI Filter is doing something wrong, but IIS/ISAPI/Windows Networking stack&amp;nbsp;does a special hack for WinInet but not others like WinHttp or .NET Client&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;Personally, I am more inclined to believe that the former is happening. The latter is simply way too convoluted, difficult, and fragile. I am not a Conspiracy Theorist - I believe in straight-forward explanations for computer issues. :-)&lt;/P&gt;
&lt;H4&gt;Problem Confirmed&lt;/H4&gt;
&lt;P&gt;In this case, I am certain that #1 applies for several reasons:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;The simplified ISAPI Extension does not send a proper HTTP Response 
&lt;LI&gt;"WinInet accepts the response" does NOT mean "it is a proper HTTP response"&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;In other words, the ISAPI Extension is doing something bad, but WinInet (and IE, which uses WinInet) tries very hard to succeed and hence overlooks such errors. &lt;/P&gt;
&lt;P&gt;&amp;lt;rant&amp;gt;&lt;/P&gt;
&lt;P&gt;Yes, it gives a great user experience to see IE render all sorts of improper HTTP responses and HTML pages, but it also gives false user confidence in the correctness of such HTTP/HTML. &lt;/P&gt;
&lt;P&gt;But, I do not think the problem is solely with Microsoft/IE but rather shared with the authors of such bad HTTP and HTML. Computer users expect things to magically "work", so someone has to make the broken things be "less broken" and "work". This expectation results in a viscious cycle:&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P&gt;If IE refused to render the broken HTTP/HTML but some other browser did, then users think that IE is broken instead of blaming the incorrect web page. Since IE renders more broken HTTP/HTML, web page developers have less motivation to author&amp;nbsp;correctly... and IE will be punished for refusing to render that future broken web page.&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;How whacky is that!?! Of course, Users have no idea that this is going on - they only see rendered pages and think everything is alright - when in fact the browser and web developers are slowly diverging from published specifications, increasing their maintenance costs, and causing headaches on the development side of things. And all because we are trying to shield the&amp;nbsp;End User...&lt;/P&gt;
&lt;P&gt;This downside is what hits you right now. In this case, you see a response from the ISAPI Extension when browsed with Internet Explorer or WinInet, so you probably think the ISAPI is perfectly simple and correct. Hence, you think that Microsoft has a bug somewhere that either causes WinHttp or .NET Client to not work with the ISAPI, or that there is some devious hack somewhere to favor WinInet. Bad Microsoft.&lt;/P&gt;
&lt;P&gt;But in reality, it is the ISAPI Extension that is broken, and you were fooled by the tainted validations with Internet Explorer and WinInet.&lt;/P&gt;
&lt;P&gt;&amp;lt;/rant&amp;gt;&lt;/P&gt;
&lt;H4&gt;Troubleshoot with Trusted Tools&lt;/H4&gt;
&lt;P&gt;This is why I only use the following basic but trusted tools to debug HTTP Client/Server issues...&amp;nbsp;because they have no alternative agenda to mislead anyone:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;A href="http://www.microsoft.com/downloads/details.aspx?familyid=9bfa49bc-376b-4a54-95aa-73c9156706e7&amp;amp;displaylang=en"&gt;WFetch&lt;/A&gt; to make raw requests and observe raw responses 
&lt;LI&gt;Network Monitor to tap the network and observe raw requests and responses 
&lt;LI&gt;&lt;A href="http://www.microsoft.com/whdc/devtools/debugging/default.mspx"&gt;Native Code Debuggers&lt;/A&gt; to observe programmatic state inside a process&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;I simply do not trust debugging/troubleshooting with anything else. If I have to use something like IE/FireFox, I always treat its answer with a heavy grain of salt and not as Gospel.&lt;/P&gt;
&lt;H4&gt;Resolution&lt;/H4&gt;
&lt;P&gt;If you change the ISAPI code to send the following response, then I believe it should work for WinHttp and .NET Client as well as WinInet: &lt;FONT face="Courier new" color=#008000&gt;&lt;PRE&gt;char szPage[] = "HTTP/1.1 200 OK\r\n"
                "Content-Type: text/html\r\n"
                "Content-Length: 16\r\n"
                "\r\n"
                "We're good to go";&lt;/PRE&gt;&lt;/FONT&gt;
&lt;P&gt;Basically, your ISAPI only sent back "We're good to go" as an HTTP response, and that is improperly formatted. The fix makes the ISAPI send back a proper HTTP response, so the client APIs like WinHttp or .NET Client should just work.&lt;/P&gt;
&lt;H4&gt;Conclusion&lt;/H4&gt;
&lt;P&gt;Powerful APIs, like ISAPI Extension and ISAPI Filter, directly control the data stream to/from IIS. Thus, they can either positively augment IIS behavior or negatively manipulate IIS to misbehave.&lt;/P&gt;
&lt;P&gt;In particular, they differ from programming environments like ASP, ASP.Net, or PHP, which removes some of the power to protect the user from generating common HTTP mistakes. One just has to be aware of the guard-rails and training wheels.&lt;/P&gt;
&lt;P&gt;Like many things, it is just a tradeoff that one needs to be aware of; nothing right or wrong.&lt;/P&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=704859" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/ISAPI/default.aspx">ISAPI</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category></item><item><title>Can ISAPI Filter change the Request's domain name?</title><link>http://blogs.msdn.com/david.wang/archive/2006/07/06/Can-ISAPI-Filter-change-the-Requests-domain-name.aspx</link><pubDate>Fri, 07 Jul 2006 08:50:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:657038</guid><dc:creator>Anonymous</dc:creator><slash:comments>6</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/657038.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=657038</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=657038</wfw:comment><description>&lt;H3&gt;Question:&lt;/H3&gt;&lt;FONT face="courier new" color=#008000&gt;
&lt;P&gt;Hi,&lt;/P&gt;
&lt;P&gt;I would like to know if we can get the entire URL that is sent through the browser into the ISAPI filter DLL. &lt;/P&gt;
&lt;P&gt;E.g. If I have a URL like - http://www.mysite.com/test&lt;/P&gt;
&lt;P&gt;what I get into the DLL (using GetHeader) is "/test". But I need the entire URL as I want to make changes even to the domain name http://www.mysite.com).&lt;/P&gt;
&lt;P&gt;Can we do this? I am sorry if I am asking the same questions that have already been asked, but I need to know this ugently.&lt;/P&gt;
&lt;P&gt;Thanks!&lt;/P&gt;&lt;/FONT&gt;
&lt;H3&gt;Answer:&lt;/H3&gt;
&lt;P&gt;ISAPI Filter can access the entire request sent by the Browser, which includes the information you named (URL and Host [i.e. "domain name"]).&amp;nbsp;However, it may not be in the form you expect because:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;The web browser does not necessarily send the exact typed text to the web server 
&lt;LI&gt;GetHeader("url") does not retrieve what is typed into the web browser&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;The following is an abridged version of the BNF definition of a HTTP Request:&lt;/P&gt;&lt;PRE&gt;       HTTP-message   = Request | Response     ; HTTP/1.1 messages

        Request       = Request-Line              ; Section 5.1
                        *(( general-header        ; Section 4.5
                         | request-header         ; Section 5.3
                         | entity-header ) CRLF)  ; Section 7.1
                        CRLF
                        [ message-body ]          ; Section 4.3

       Request-Line   = Method SP Request-URI SP HTTP-Version CRLF

       Request-URI    = "*" | absoluteURI | abs_path | authority

       request-header = ...
                      | Host                     ; Section 14.23&lt;/PRE&gt;
&lt;P&gt;Given your example URL of &lt;CODE&gt;http://www.mysite.com/test&lt;/CODE&gt; , the web browser can send a couple of different valid requests. I color code what retrieves what:&lt;/P&gt;
&lt;P&gt;Example 1:&lt;/P&gt;&lt;PRE&gt;GET &lt;FONT color=#ff0000&gt;http://www.mysite.com/test&lt;/FONT&gt; HTTP/1.1\r\n
\r\n&lt;/PRE&gt;
&lt;P&gt;Example 2:&lt;/P&gt;&lt;PRE&gt;GET &lt;FONT color=#ff0000&gt;/test&lt;/FONT&gt; HTTP/1.1\r\n
Host: &lt;FONT color=#008000&gt;www.mysite.com&lt;/FONT&gt;\r\n
\r\n&lt;/PRE&gt;
&lt;P&gt;In particular, GetHeader("url") only retrieves the Request-URI (colored in red) and GetHeader("host:") only retrieves the Host header (colored in green). In other words, GetHeader() does not retrieving the "logical URL"&amp;nbsp;typed into the web browser; it retrieves specific parts of the request as identified by BNF.&lt;/P&gt;
&lt;P&gt;Since you want to know the logical "domain" and "URL" of the request, you will have to retrieve the data via ISAPI and parse it yourself according to HTTP specifications. I suggest you read the HTTP 1.1 RFC for all of the proper details since ISAPI just gives you access to the data - you have to make logical sense of it yourself.&lt;/P&gt;
&lt;P&gt;Now, as to your question about using ISAPI Filter to "change the domain name"... an ISAPI Filter can only change the domain name as it appears in the Host: header. It cannot alter IIS request processing nor server variables based on the website.&lt;/P&gt;
&lt;P&gt;This means that if the original request came in for domain "original.com", it will be processed by the metadata associated with the "original.com" website. Even if the ISAPI Filter uses SetHeader() changes the Host: header to "new.com" or sets the URL to &lt;CODE&gt;http://www.new.com/test&lt;/CODE&gt;,&amp;nbsp;the SERVER_NAME and request is still processed by "original.com" - you only see the Host header change in the ALL_RAW and HTTP_HOST server variables.&lt;/P&gt;
&lt;P&gt;In other words, it is not possible to transparently and easily change/re-route requests between different websites... unless you use a SF_NOTIFY_READ_RAW_DATA filter. But, you really do not want to do that for many reasons - on IIS6, you lose Worker Process Isolation Mode, and you also have to know HTTP backwards and forwards to write it correctly for all possible cases without crashing.&lt;/P&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=657038" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/ISAPI/default.aspx">ISAPI</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category></item><item><title>HOWTO: Install and Run PHP on IIS7, Part 3</title><link>http://blogs.msdn.com/david.wang/archive/2006/06/24/HOWTO-Install-and-Run-PHP-on-IIS7-Part-3.aspx</link><pubDate>Sat, 24 Jun 2006 13:15:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:644001</guid><dc:creator>Anonymous</dc:creator><slash:comments>5</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/644001.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=644001</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=644001</wfw:comment><description>&lt;P&gt;A short while ago, I came out with a &lt;A href="http://blogs.msdn.com/david.wang/archive/2006/06/21/HOWTO_Install_and_Run_PHP_on_IIS7_Part_2.aspx"&gt;small script&lt;/A&gt; to properly configure PHP for IIS7. Sure enough, I got asked to make a similar one for IIS6. So, I figure that while I am at it, I might as well cover&amp;nbsp;IIS4, IIS5, and IIS5.1 as well since it is not very different, and put it all into one script so that you can see one classic way to maintain a single automation script which runs version-specific logic.&lt;/P&gt;
&lt;P&gt;For the astute reader - no, this script is not minimal, optimal, nor foolproof... it is illustrative. I am deliberately showing several possibilities at the expense of conciseness... but I hope you agree that the information is worth more than the result here...&lt;/P&gt;
&lt;P&gt;To correctly configure the PHP Application Mapping prior to IIS7, you MUST provide a tool that can modify the IIS LIST data type.&lt;/P&gt;
&lt;P&gt;I have provided such a tool in &lt;A href="http://blogs.msdn.com/david.wang/archive/2004/12/02/273681.aspx"&gt;this blog entry&lt;/A&gt;, so you need to copy that script tool into the same directory as you copy this script and name it "ChgList.vbs". If you want to put the tool in a different directory or with a different name, you must modify the FILE_CHGLIST variable in this script appropriately to give the complete pathname.&lt;/P&gt;
&lt;P&gt;In addition, I made several little illustrative enhancements:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Debug mode - if you want to merely SEE what is going to execute but NOT execute anything, set the _DEBUG environment variable to 1. Default executes. 
&lt;LI&gt;Functions vs. Labels - the label :VerifyScripts is treated as a FUNCTION in batch (with ERRORLEVEL as the return value), while the labels :Menu and :Start are treated like GOTO labels 
&lt;LI&gt;File Existence Validations - depending on the OS/IIS Version, validate the existence of necessary files and scripts 
&lt;LI&gt;OS BuildNumber detection&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Yes, when you write scripts/tools meant to run on multiple platforms and versions, you get constrained into the most reliable least-common-denominator and never get to use the new-fangled stuff. But that's the difference between getting stuff done with compatible software vs experimenting with the bleeding edge... ;-)&lt;/P&gt;
&lt;P&gt;In this case, I am using OS BuildNumber, parsed from 'ver', to determine IIS version. If I can constrain this script to IIS5.1 and above (or W2K with REG.EXE from the Resource Kit), I can use REG.EXE to read the installed IIS version from the Registry... but I am not making those assumptions&amp;nbsp;and hence use the OS BuildNumber as a compatible mechanism.&lt;/P&gt;
&lt;P&gt;And to be complete - all of these actions require you to run with administrative privileges since you cannot modify the IIS Configuration file(s) without them. Prior to Vista, this means the user must be in the local Administrators group. On Vista with UAC (default), it means that you either run as the built-in Administrator (disabled by default) or run the script with elevated permissions (by saving the script and right-click running as Administrator).&lt;/P&gt;
&lt;P&gt;One final disclaimer:&lt;/P&gt;
&lt;P&gt;*** Please realize that the script tool simply makes PHP work in one configuration (default). It is not meant to fix or make your arbitrary configuration work ***&lt;/P&gt;
&lt;P&gt;In particular, if you run this script more than once, it may not work correctly or configure duplicate settings. For example, APPCMD will fail to configure duplicate handlers&amp;nbsp;and WebServiceExtension entries, iisext.vbs will fail to configure duplicate WebServiceExtension entries, and ChgList.vbs will keep adding duplicate .php ScriptMappings. It is a slippery slope, so I draw the line early.&lt;/P&gt;
&lt;P&gt;Also, the script does not go through your ScriptMaps to change the right ones; you can do that yourself. It also does not verify file ACLs, user identities and permissions, etc - it assumes everything is working perfectly and you just need to make the minimal IIS-related configuration to make PHP work.&lt;/P&gt;
&lt;P&gt;Sorry... but please understand that I am not in the business of writing and supporting installation programs for other products nor troubleshooting why it does not work on IIS. I am just trying to show how things work, together.&lt;/P&gt;
&lt;P&gt;Enjoy.&lt;/P&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;FONT color=#008000&gt;&lt;PRE&gt;@IF ?%_ECHO%?==?? ECHO OFF

SETLOCAL
IF ?%_DEBUG%? EQU ?1? (SET DEBUG=ECHO) ELSE (SET DEBUG=)
SET FILE_CHGLIST=chglist.vbs
SET FILE_IISEXT=%SYSTEMROOT%\System32\iisext.vbs
SET CMD_CHGLIST=CSCRIPT //NoLogo %FILE_CHGLIST%
SET CMD_IISEXT=CSCRIPT //NoLogo %FILE_IISEXT%
SET CMD_APPCMD=%SYSTEMROOT%\System32\inetsrv\APPCMD.EXE
SET DIR_PHP_FROM=%SYSTEMDRIVE%\Inetpub\PHP
SET PHP_TYPE=ISAPI
SET PHP_MODULE=IsapiModule
SET PHP_BINARY=php5isapi.dll

REM
REM Determine OS BuildNumber
REM 1381    NT4     IIS4
REM 2195    W2K     IIS5
REM 2600    WXP     IIS5.1
REM 3790    WS03    IIS6
REM Other   Vista   IIS7
REM
FOR /f "tokens=3 delims=.]" %%i IN ('ver') DO SET OS_BUILDNUMBER=%%i
IF "%OS_BUILDNUMBER%"=="" FOR /f "tokens=4" %%i IN ('ver') DO IF "%%i"=="4.0" SET OS_BUILDNUMBER=1381

:Menu
ECHO.
ECHO David.Wang's Sample PHP/IIS Configurator
ECHO Version: June 2006
ECHO OS BuildNumber: %OS_BUILDNUMBER%
ECHO.
ECHO ------------------------------ Summary ------------------------------
ECHO PHP Binaries Dir : %DIR_PHP_FROM%
ECHO PHP Binary Type  : %PHP_TYPE%
ECHO PHP Binary Name  : %PHP_BINARY%
ECHO ---------------------------------------------------------------------

REM
REM Do some basic validations
REM
ECHO.
ECHO Validating inputs...
IF /I ?%PHP_TYPE%? NEQ ?CGI? IF /I ?%PHP_TYPE%? NEQ ?ISAPI? ECHO.&amp;amp;ECHO ERROR: Binary Type MUST be either CGI or ISAPI
FOR %%I IN ( %PHP_BINARY% ) DO (
    IF /I ?%PHP_TYPE%? EQU ?CGI? IF /I ?%%~xI? NEQ ?.exe? ECHO.&amp;amp;ECHO WARNING: Binary Type %PHP_TYPE% requires a CGI EXE binary
    IF /I ?%PHP_TYPE%? EQU ?ISAPI? IF /I ?%%~xI? NEQ ?.dll? ECHO.&amp;amp;ECHO WARNING: Binary Type %PHP_TYPE% requires an ISAPI DLL binary
)
IF /I ?%PHP_TYPE%? EQU ?CGI? SET PHP_MODULE=CgiModule
IF /I ?%PHP_TYPE%? EQU ?CGI? ECHO.&amp;amp;ECHO ERROR: PHP CGI requires modifying cgi.force_redirect to 0 in "%DIR_PHP_FROM%\PHP.INI"
IF /I ?%PHP_BINARY%? NEQ ?php5isapi.dll? IF /I ?%PHP_BINARY%? NEQ ?php-cgi.exe? ECHO.&amp;amp;ECHO WARNING: Unrecognized PHP binary %PHP_BINARY%
Call :VerifyScripts
ECHO.
ECHO Remember to tweak PHP.INI for security and functionality per php.net
ECHO Finished input validation.
ECHO.

SET GO=
SET /P GO=Press 1 to EDIT choices, or ENTER to start IIS modifications:
IF ?%GO%? EQU ?? GOTO :Start

ECHO.
ECHO Press ENTER to accept [%DIR_PHP_FROM%], or provide new value (folder path)
SET /P DIR_PHP_FROM=PHP Binaries Dir:
ECHO Press ENTER to accept [%PHP_TYPE%], or provide new value (CGI or ISAPI)
SET /P PHP_TYPE=PHP Binary Type:
ECHO Press ENTER to accept [%PHP_BINARY%], or provide new value (filename)
SET /P PHP_BINARY=PHP Binary Name:

GOTO :Menu

:Start
REM
REM Start Configuration
REM
ECHO.
ECHO Starting IIS Configuration...
ECHO.
ECHO Copying "%DIR_PHP_FROM%\PHP.INI-Recommended" to PHP.INI...
%DEBUG% COPY /Y "%DIR_PHP_FROM%\PHP.INI-Recommended" "%DIR_PHP_FROM%\PHP.INI"

CALL :VerifyScripts
IF %ERRORLEVEL% EQU 2 GOTO :EOF

REM
REM Use OS Version to distinguish between IIS Versions
REM
REM 1381    NT4     IIS4
REM 2195    W2K     IIS5
REM 2600    WXP     IIS5.1
REM 3790    WS03    IIS6
REM Other   Vista   IIS7
REM
IF %OS_BUILDNUMBER% GTR 3790 (
    ECHO Setting PHP Handler...
    %DEBUG% %CMD_APPCMD% SET CONFIG -section:handlers "-+[name='PHP-%PHP_TYPE%',path='*.php',verb='GET,HEAD,POST',modules='%PHP_MODULE%',scriptProcessor='%DIR_PHP_FROM%\%PHP_BINARY%',resourceType='File']"

    ECHO Adding and Enabling PHP in ISAPI/CGI Restriction List...
    %DEBUG% %CMD_APPCMD% SET CONFIG -section:isapiCgiRestriction "-+[path='%DIR_PHP_FROM%\%PHP_BINARY%',allowed='true',groupId='PHP',description='PHP']"
) ELSE IF %OS_BUILDNUMBER% EQU 3790 (
    ECHO Setting PHP Handler...
    %DEBUG% %CMD_CHGLIST% W3SVC/ScriptMaps "" ".php,%DIR_PHP_FROM%\%PHP_BINARY%,0" /INSERT /COMMIT
    ECHO Adding and Enabling PHP in ISAPI/CGI Restriction List...
    %DEBUG% %CMD_IISEXT% /AddFile "%DIR_PHP_FROM%\%PHP_BINARY%" 1 PHP 1 PHP
) ELSE IF %OS_BUILDNUMBER% LSS 3790 (
    ECHO Setting PHP Handler...
    %DEBUG% %CMD_CHGLIST% W3SVC/ScriptMaps "" ".php,%DIR_PHP_FROM%\%PHP_BINARY%,0" /INSERT /COMMIT
)

ECHO.
ECHO Finished IIS Configuration.
ECHO.
ECHO Test installation using PHP file content of:  ^&amp;lt;?php phpinfo();?^&amp;gt;

ENDLOCAL
GOTO :EOF

REM
REM Sub-routines and Functions
REM
:VerifyScripts
SET ERRORLEVEL=0
IF NOT EXIST "%DIR_PHP_FROM%\%PHP_BINARY%" (
    ECHO.
    ECHO ERROR: PHP Binary "%DIR_PHP_FROM%\%PHP_BINARY%" does not exist!
    ECHO Please first completely extract PHP to "%DIR_PHP_FROM%"
    SET ERRORLEVEL=2
)
IF NOT EXIST "%DIR_PHP_FROM%\PHP.INI" (
    ECHO.
    ECHO ERROR: "%DIR_PHP_FROM%\PHP.INI" does not exist!
    SET ERRORLEVEL=2
)
IF %OS_BUILDNUMBER% GTR 3790 (
    IF NOT EXIST "%CMD_APPCMD%" (
        ECHO.
        ECHO ERROR: Script requires %CMD_APPCMD% for this OS.
        SET ERRORLEVEL=2
    )
)
IF %OS_BUILDNUMBER% EQU 3790 (
    IF NOT EXIST "%FILE_IISEXT%" (
        ECHO.
        ECHO ERROR: Script requires %FILE_IISEXT% for this OS.
        SET ERRORLEVEL=2
    )
)
IF %OS_BUILDNUMBER% LEQ 3790 (
    IF NOT EXIST "%FILE_CHGLIST%" (
        ECHO.
        ECHO ERROR: Script requires %FILE_CHGLIST% for this OS.
        ECHO http://blogs.msdn.com/david.wang/archive/2004/12/02/273681.aspx
        SET ERRORLEVEL=2
    )
)
GOTO :EOF&lt;/PRE&gt;&lt;/FONT&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=644001" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/ISAPI/default.aspx">ISAPI</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Sample+Code/default.aspx">Sample Code</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Tips/default.aspx">Tips</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/HOWTO_2E002E002E00_/default.aspx">HOWTO...</category></item><item><title>HOWTO: Install and Run PHP on IIS7, Part 2</title><link>http://blogs.msdn.com/david.wang/archive/2006/06/21/HOWTO-Install-and-Run-PHP-on-IIS7-Part-2.aspx</link><pubDate>Thu, 22 Jun 2006 06:39:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:642354</guid><dc:creator>Anonymous</dc:creator><slash:comments>50</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/642354.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=642354</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=642354</wfw:comment><description>&lt;P&gt;A couple of months ago, I wrote a quick and dirty &lt;A href="http://blogs.msdn.com/david.wang/archive/2006/04/04/HOWTO_Install_and_run_PHP_on_IIS7.aspx"&gt;entry&lt;/A&gt; on how to install PHP on IIS7. The main purpose of that entry was to explain the details of what was going on as well as cookie-cutter instructions of one way to successfully install PHP on IIS7.&lt;/P&gt;
&lt;P&gt;Well, the responses that I have received from that blog entry made me realize that I need to provide something a little more shrink-wapped which does a few more things other than just run the bare minimum... because I was seeing way too many broken custom modifications coming from mistaken assumptions about&amp;nbsp;PHP or IIS7.&lt;/P&gt;
&lt;P&gt;Here it is, V2. Just copy/paste the following into a .bat file, right-click run it as elevated Administrator on Vista, and follow the prompted instructions. You should have PHP extracted into a directory of your choice before-hand (I favor and default to %SYSTEMDRIVE%\Inetpub\PHP for many aforementioned reasons; in my examples, I chose "C:\Program Files\P H P" to show it working with long pathnames).&lt;/P&gt;
&lt;P&gt;The batch script:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Works with PHP installed wherever, including pathnames with spaces. Just tell it where you installed PHP (sans double quotes and trailing backslash) 
&lt;LI&gt;Gives choice of whether to use the CGI or ISAPI version of PHP. You do have to give the correct binary name (php5isapi.dll or php-cgi.exe, assuming PHP5), but there are checks for that 
&lt;LI&gt;Gives warnings and errors if the directory/file does not exist, mismatched binary types, and additional steps you need to do to have a minimally functioning PHP&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Now, I am no PHP expert, so I&amp;nbsp;can only&amp;nbsp;give instructions for how to get PHP configured and running on IIS7. Questions about all other PHP-related features (like PHP extensions, integration with mySQL, etc) and how to get them working really&amp;nbsp;belong on a PHP support forum.&lt;/P&gt;
&lt;P&gt;Sample Execution Results:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Setting up PHP5 installed at "C:\Program Files\P H P", ISAPI version. Notice the defaults and verification of provided inputs.&lt;/LI&gt;&lt;/UL&gt;&lt;FONT color=#008000&gt;&lt;PRE&gt;David.Wang's Sample PHP/IIS7 Configurator
Version: June 2006

------------------------------ Summary ------------------------------
PHP Binaries Dir : C:\Inetpub\PHP
PHP Binary Type  : ISAPI
PHP Binary Name  : php5isapi.dll
---------------------------------------------------------------------

Validating inputs...

&lt;FONT color=#ff0000&gt;ERROR: PHP Binary "C:\Inetpub\PHP\php5isapi.dll" does not exist!
Please first completely extract PHP to "C:\Inetpub\PHP"
&lt;/FONT&gt;
Remember to tweak PHP.INI for security and functionality per php.net
Finished input validation.

Press 1 to EDIT choices, or ENTER to start IIS modifications:1

Press ENTER to accept [C:\Inetpub\PHP], or provide new value (folder path)
PHP Binaries Dir:&lt;FONT color=#ff0000&gt;C:\Program Files\P H P&lt;/FONT&gt;
Press ENTER to accept [ISAPI], or provide new value (CGI or ISAPI)
PHP Binary Type:
Press ENTER to accept [php5isapi.dll], or provide new value (filename)
PHP Binary Name:

David.Wang's Sample PHP/IIS7 Configurator
Version: June 2006

------------------------------ Summary ------------------------------
PHP Binaries Dir : C:\Program Files\P H P
PHP Binary Type  : ISAPI
PHP Binary Name  : php5isapi.dll
---------------------------------------------------------------------

Validating inputs...

Remember to tweak PHP.INI for security and functionality per php.net
Finished input validation.

Press 1 to EDIT choices, or ENTER to start IIS modifications:

Starting IIS7 Configuration...

Copying "C:\Program Files\P H P\PHP.INI-Recommended" to PHP.INI...
Setting PHP Handler...
CONFIG object "system.webServer/handlers" changed
Adding and Enabling PHP in ISAPI/CGI Restriction List...
CONFIG object "system.webServer/security/isapiCgiRestriction" changed

Finished IIS7 Configuration.

Test installation using PHP file content of:  &amp;lt;?php phpinfo();?&amp;gt;&lt;/PRE&gt;&lt;/FONT&gt;
&lt;UL&gt;
&lt;LI&gt;Setting up PHP5 installed at "C:\Program Files\P H P", CGI version. Notice the defaults and reminders as you change to CGI.&lt;/LI&gt;&lt;/UL&gt;&lt;FONT color=#008000&gt;&lt;PRE&gt;David.Wang's Sample PHP/IIS7 Configurator
Version: June 2006

------------------------------ Summary ------------------------------
PHP Binaries Dir : C:\Inetpub\PHP
PHP Binary Type  : ISAPI
PHP Binary Name  : php5isapi.dll
---------------------------------------------------------------------

Validating inputs...

ERROR: PHP Binary "C:\Inetpub\PHP\php5isapi.dll" does not exist!
Please first completely extract PHP to "C:\Inetpub\PHP"

Remember to tweak PHP.INI for security and functionality per php.net
Finished input validation.

Press 1 to EDIT choices, or ENTER to start IIS modifications:1

Press ENTER to accept [C:\Inetpub\PHP], or provide new value (folder path)
PHP Binaries Dir:&lt;FONT color=#ff0000&gt;C:\Program Files\P H P&lt;/FONT&gt;
Press ENTER to accept [ISAPI], or provide new value (CGI or ISAPI)
PHP Binary Type:&lt;FONT color=#ff0000&gt;cgi&lt;/FONT&gt;
Press ENTER to accept [php5isapi.dll], or provide new value (filename)
PHP Binary Name:&lt;FONT color=#ff0000&gt;php-cgi.exe&lt;/FONT&gt;

David.Wang's Sample PHP/IIS7 Configurator
Version: June 2006

------------------------------ Summary ------------------------------
PHP Binaries Dir : C:\Program Files\P H P
PHP Binary Type  : cgi
PHP Binary Name  : php-cgi.exe
---------------------------------------------------------------------

Validating inputs...

&lt;FONT color=#ff0000&gt;ERROR: PHP CGI requires modifying cgi.force_redirect to 0 in "C:\Program Files\P H P\PHP.INI"&lt;/FONT&gt;

Remember to tweak PHP.INI for security and functionality per php.net
Finished input validation.

Press 1 to EDIT choices, or ENTER to start IIS modifications:

Starting IIS7 Configuration...

Copying "C:\Program Files\P H P\PHP.INI-Recommended" to PHP.INI...
Setting PHP Handler...
CONFIG object "system.webServer/handlers" changed
Adding and Enabling PHP in ISAPI/CGI Restriction List...
CONFIG object "system.webServer/security/isapiCgiRestriction" changed

Finished IIS7 Configuration.

Test installation using PHP file content of:  &amp;lt;?php phpinfo();?&amp;gt;&lt;/PRE&gt;&lt;/FONT&gt;
&lt;P&gt;Enjoy.&lt;/P&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;FONT color=#008000&gt;&lt;PRE&gt;@IF ?%_ECHO%?==?? ECHO OFF

SETLOCAL
SET DIR_PHP_FROM=%SYSTEMDRIVE%\Inetpub\PHP
SET PHP_TYPE=ISAPI
SET PHP_MODULE=IsapiModule
SET PHP_BINARY=php5isapi.dll

:Menu
ECHO.
ECHO David.Wang's Sample PHP/IIS7 Configurator
ECHO Version: June 2006
ECHO.
ECHO ------------------------------ Summary ------------------------------
ECHO PHP Binaries Dir : %DIR_PHP_FROM%
ECHO PHP Binary Type  : %PHP_TYPE%
ECHO PHP Binary Name  : %PHP_BINARY%
ECHO ---------------------------------------------------------------------

REM
REM Do some basic validations
REM
ECHO.
ECHO Validating inputs...
IF /I ?%PHP_TYPE%? NEQ ?CGI? IF /I ?%PHP_TYPE%? NEQ ?ISAPI? ECHO.&amp;amp;ECHO ERROR: Binary Type MUST be either CGI or ISAPI
FOR %%I IN ( %PHP_BINARY% ) DO (
    IF /I ?%PHP_TYPE%? EQU ?CGI? IF /I ?%%~xI? NEQ ?.exe? ECHO.&amp;amp;ECHO WARNING: Binary Type %PHP_TYPE% requires a CGI EXE binary
    IF /I ?%PHP_TYPE%? EQU ?ISAPI? IF /I ?%%~xI? NEQ ?.dll? ECHO.&amp;amp;ECHO WARNING: Binary Type %PHP_TYPE% requires an ISAPI DLL binary
)
IF NOT EXIST "%DIR_PHP_FROM%\%PHP_BINARY%" (
    ECHO.
    ECHO ERROR: PHP Binary "%DIR_PHP_FROM%\%PHP_BINARY%" does not exist!
    ECHO Please first completely extract PHP to "%DIR_PHP_FROM%"
)
IF /I ?%PHP_TYPE%? EQU ?CGI? SET PHP_MODULE=CgiModule
IF /I ?%PHP_TYPE%? EQU ?CGI? ECHO.&amp;amp;ECHO ERROR: PHP CGI requires modifying cgi.force_redirect to 0 in "%DIR_PHP_FROM%\PHP.INI"
IF /I ?%PHP_BINARY%? NEQ ?php5isapi.dll? IF /I ?%PHP_BINARY%? NEQ ?php-cgi.exe? ECHO.&amp;amp;ECHO WARNING: Unrecognized PHP binary %PHP_BINARY%
ECHO.
ECHO Remember to tweak PHP.INI for security and functionality per php.net
ECHO Finished input validation.
ECHO.

SET GO=
SET /P GO=Press 1 to EDIT choices, or ENTER to start IIS modifications:
IF ?%GO%? EQU ?? GOTO :Start

ECHO.
ECHO Press ENTER to accept [%DIR_PHP_FROM%], or provide new value (folder path)
SET /P DIR_PHP_FROM=PHP Binaries Dir:
ECHO Press ENTER to accept [%PHP_TYPE%], or provide new value (CGI or ISAPI)
SET /P PHP_TYPE=PHP Binary Type:
ECHO Press ENTER to accept [%PHP_BINARY%], or provide new value (filename)
SET /P PHP_BINARY=PHP Binary Name:

GOTO :Menu

:Start
REM
REM Start Configuration
REM
IF NOT EXIST "%DIR_PHP_FROM%\%PHP_BINARY%" (
    ECHO.
    ECHO ERROR: PHP Binary "%DIR_PHP_FROM%\%PHP_BINARY%" does not exist!
    ECHO Please first completely extract PHP to "%DIR_PHP_FROM%"
    GOTO :EOF
)

ECHO.
ECHO Starting IIS7 Configuration...
ECHO.
ECHO Copying "%DIR_PHP_FROM%\PHP.INI-Recommended" to PHP.INI...
COPY /Y "%DIR_PHP_FROM%\PHP.INI-Recommended" "%DIR_PHP_FROM%\PHP.INI" &amp;gt;NUL

PUSHD %SYSTEMROOT%\System32\inetsrv

ECHO Setting PHP Handler...
APPCMD SET CONFIG -section:handlers "-+[name='PHP-%PHP_TYPE%',path='*.php',verb='GET,HEAD,POST',modules='%PHP_MODULE%',scriptProcessor='%DIR_PHP_FROM%\%PHP_BINARY%',resourceType='File']"

ECHO Adding and Enabling PHP in ISAPI/CGI Restriction List...
APPCMD SET CONFIG -section:isapiCgiRestriction "-+[path='%DIR_PHP_FROM%\%PHP_BINARY%',allowed='true',groupId='PHP',description='PHP']"

POPD

ECHO.
ECHO Finished IIS7 Configuration.
ECHO.
ECHO Test installation using PHP file content of:  ^&amp;lt;?php phpinfo();?^&amp;gt;

ENDLOCAL&lt;/PRE&gt;&lt;/FONT&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=642354" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/ISAPI/default.aspx">ISAPI</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Sample+Code/default.aspx">Sample Code</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS+7.0+_2800_beta_2900_/default.aspx">IIS 7.0 (beta)</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Tips/default.aspx">Tips</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/HOWTO_2E002E002E00_/default.aspx">HOWTO...</category></item><item><title>ISAPI Filter and the IIS6 Process Model</title><link>http://blogs.msdn.com/david.wang/archive/2006/06/21/ISAPI-Filter-and-the-IIS6-Process-Model.aspx</link><pubDate>Wed, 21 Jun 2006 12:34:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:641018</guid><dc:creator>Anonymous</dc:creator><slash:comments>5</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/641018.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=641018</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=641018</wfw:comment><description>&lt;H3&gt;Question:&lt;/H3&gt;&lt;FONT face="courier new" color=#008000&gt;
&lt;P&gt;In IIS6, is there a way to have a top-level filter run in a process space separate from each website's process space?&amp;nbsp; Under IIS5, our filter has been used to store a large data cache to reduce the number of round trips to our database.&amp;nbsp; Now, in IIS6, this large data cache is unfortunately duplicated in every one of our hundreds of different website processes, because the filter is now loaded into each website's w3wp process instead of into the single IIS5 inetinfo process.&amp;nbsp; To avoid having rewrite our filter to access the cached data (cross-process) from some new custom cache manager process, I wonder if there is some way to get IIS to do that work for us, like in IIS5 where the filter ran in the single inetinfo process even when requests were farmed off to websites in different processes.&lt;/P&gt;
&lt;P&gt;Initially, research into this has led me to David's blog entry, which is helpful even though it is not intended to address this particular issue: 
&lt;P&gt;&lt;A href="http://blogs.msdn.com/david.wang/archive/2006/04/07/IIS6_and_HTTP_Server_API_Part_1.aspx"&gt;http://blogs.msdn.com/david.wang/archive/2006/04/07/IIS6_and_HTTP_Server_API_Part_1.aspx&lt;/A&gt; 
&lt;P&gt;His entry mentions that in IIS6, filters run in w3wp, rather than inetinfo. Is this configurable? Is there some way to run a global filter in a single process separate from each website's process space in IIS 6 (while not using IIS5 compatibility mode)?&lt;/P&gt;
&lt;P&gt;Thanks,&lt;/P&gt;&lt;/FONT&gt;
&lt;H3&gt;Answer:&lt;/H3&gt;
&lt;P&gt;Alas. It sounds like IIS5 Compatibility mode with each website running in High Isolation protection is the closest functional equivalent to what you desire.&lt;/P&gt;
&lt;H4&gt;IIS6 Process Models in a nutshell&lt;/H4&gt;
&lt;P&gt;IIS6 supports two process model "modes": IIS5 Compatibility Mode and IIS6 Worker Process Isolation Mode.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;IIS5 Compatibility Mode runs like IIS 5.x - all ISAPI Filters load in inetinfo.exe and all request routing decisions go through inetinfo.exe and ISAPI Filters&lt;/LI&gt;
&lt;LI&gt;IIS6 Worker Process Isolation Mode is&amp;nbsp;reliable - ISAPI Filters individually load in the w3wp.exe of the Application Pool servicing that application's URL namespace. This basically means:&lt;/LI&gt;
&lt;UL&gt;
&lt;LI&gt;Global ISAPI Filter DLLs individually load in every w3wp.exe&lt;/LI&gt;
&lt;LI&gt;Site ISAPI Filter DLLs individually load in each w3wp.exe of the Application Pool(s) servicing the given website. In other words, if multiple Application Pools service a website with a site filter, every w3wp.exe belonging to those Application Pools will have its own instance of the filter DLL&lt;/LI&gt;&lt;/UL&gt;&lt;/UL&gt;
&lt;P&gt;There are some corner cases for filter configuration/loading which violate the above statements; ISAPI Filter and Application Pools are not exactly congruous concepts. But, let's leave that discussion for another day; I am more interested in the intended behavior at the moment. :-P&lt;/P&gt;
&lt;P&gt;In particular, IIS6 does not provide any built-in mechanism to load global ISAPI Filters in a single process and route all requests through the ISAPI Filters in that process prior to execution by the Application Pool's w3wp.exe.&lt;/P&gt;
&lt;H4&gt;Process Model and Global Filtering&lt;/H4&gt;
&lt;P&gt;Maybe I misunderstand what you want, but what you are asking for seems odd to me because if all websites share a single process space for global ISAPI Filters, then doesn't that defeat the whole purpose of having Worker Process Isolation? If an ISAPI Filter in that single process goes down, then&amp;nbsp;it would affect all websites... which would defeate process isolation.&lt;/P&gt;
&lt;P&gt;I realize that in a shared hosting scenario, the Hoster&amp;nbsp;may want to run his trusted code somewhere to filter/cache all requests and leave the Application Pools running untrusted&amp;nbsp;end-user&amp;nbsp;code. However, please understand that from an IIS perspective, the Hoster's code is no different than the untrusted end-user code - it is all "user code" from&amp;nbsp;our perspective - and we trust none of it with IIS6 Worker Process Isolation Mode.&lt;/P&gt;
&lt;P&gt;Sorry... we have probably all been burned too many times by bad ISAPI Filters or misbehaving applications we are "supposed to trust" running in Low/Medium Isolation. We took a stand in IIS6's core design.&lt;/P&gt;
&lt;H4&gt;Conclusion&lt;/H4&gt;
&lt;P&gt;Now,&amp;nbsp;the IIS6 mode for&amp;nbsp;running trusted code globally across all requests in a single process already exists - IIS5 Compatibility Mode. I know, I know, it gives up a lot of Application Pool benefits, but you do get some of the monitoring services of COM+.&lt;/P&gt;
&lt;P&gt;And before you ask - no, we shot down the idea of having this hybrid IIS mode where ISAPI Filters loaded in a global process like inetinfo.exe, have requests routed through it prior to reaching individual w3wp.exe, and have WAS monitor the w3wp.exe. It is basically IIS5 Compatibility Mode with COM+ replaced by WAS, and we had to choose between emulating IIS5 Compatibility Mode or creating this hybrid, along with supporting the native IIS6 Worker Process Isolation Mode... and compatibility won.&lt;/P&gt;
&lt;P&gt;IIS5 Compatibility mode with each website running in High Isolation protection isolates each website into its own dllhost.exe (and configurable process identity) and allows global ISAPI Filters all running in inetinfo.exe to examine every bit of incoming/outgoing data of every website.&lt;/P&gt;
&lt;P&gt;Yes, I realize that it makes memory sharing/efficiencies more difficult because you have to write your own data sharing manager process, but isn't that how it goes - resource utilization and resource isolation tend to be opposites - one wants to share everything for efficiency and avoid duplication, while the other wants reliable and individual copies of everything so that no one affects the other.&lt;/P&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=641018" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/ISAPI/default.aspx">ISAPI</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category></item><item><title>HOWTO: Access POST form data with ISAPI</title><link>http://blogs.msdn.com/david.wang/archive/2006/05/10/HOWTO-Access-POST-form-data-with-ISAPI.aspx</link><pubDate>Thu, 11 May 2006 09:47:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:595206</guid><dc:creator>Anonymous</dc:creator><slash:comments>3</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/595206.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=595206</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=595206</wfw:comment><description>&lt;P&gt;This is a frequently asked question about IIS6 extensibility - how to access the request entity body on the way in - as well as how to configure IIS.&lt;/P&gt;
&lt;H3&gt;Question:&lt;/H3&gt;&lt;FONT face="courier new" color=#008000&gt;
&lt;P&gt;I want to catch all incoming requests, add some header and watch it when its out. Therefore I used a filter which can't be use alone in IIS 6 cause of the post data and so I added the wildcards. I'm using the same DLL for both.&lt;/P&gt;
&lt;P&gt;Since I'm interested in all incoming requests I thought its best to define a global filter and ScriptMap.&lt;/P&gt;
&lt;P&gt;What do you think ?&lt;/P&gt;
&lt;P&gt;Thanks,&lt;/P&gt;&lt;/FONT&gt;
&lt;H3&gt;Answer:&lt;/H3&gt;
&lt;P&gt;I am just going to give the logical answers now. At the moment, I am a little short on time, so I cannot post code samples showing how to do this with ISAPI Filter and Extension (yes, I write and test my code before publishing it publicly - you guys do want functional and correct code samples, not merely code I whipped together on the side, right? ;-) ). However, if you really want the code samples, you can ask via blog entry comments for the code sample, and I will see what I can do and link it in...&lt;/P&gt;
&lt;P&gt;There are two ways to access request entity body with ISAPI on IIS6:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;ISAPI Filter subscribing to SF_NOTIFY_READ_RAW_DATA and configured as a Global ISAPI Filter, which runs on all requests. It requires:&lt;/LI&gt;
&lt;UL&gt;
&lt;LI&gt;Run IIS6 in IIS5 Compatibility Mode (you lose tremendous benefits of Application Pools and process/application isolation)&lt;/LI&gt;
&lt;LI&gt;Since SF_NOTIFY_READ_RAW_DATA is a streaming event (i.e. it triggers on every Network read), the ISAPI code must buffer and parse that data (including de-chunking) at an HTTP level to determine what is "request entity body POSTed by an HTML FORM"&lt;/LI&gt;
&lt;LI&gt;In general, this parsing is non-trivial for an ISAPI to do correctly 100% of the time and comes with severe caveats, such as you cannot do this over SSL (request buffering in SF_NOTIFY_READ_RAW_DATA is not compatible with SSL). See this &lt;A HREF="/david.wang/archive/2006/04/07/IIS6_and_HTTP_Server_API_Part_1.aspx"&gt;blog entry&lt;/A&gt; for more related details&lt;/LI&gt;&lt;/UL&gt;
&lt;LI&gt;ISAPI Extension calling HSE_REQ_EXEC_URL and configured as a wildcard application mapping. There are no difficult requirements, but since it is an application mapping which operates at a different point in request processing versus ISAPI Filters (see this &lt;A HREF="/david.wang/archive/2006/04/28/HOWTO_Run_Console_Applications_from_IIS6_on_Windows_Server_2003_Part_2.aspx"&gt;blog entry&lt;/A&gt; for an end-to-end view), you have to be aware of the resulting difference in expected behavior.&lt;/LI&gt;
&lt;UL&gt;
&lt;LI&gt;Application Mappings are invoked ONCE per request. You do not get a callback on every Network read. Thus, it does NOT offer streaming access to data (i.e. you get exactly once chance to read/manipulate the entity body prior to passing it on to the child request)&lt;BR&gt;&lt;BR&gt;This means that to manipulate large entity body, you HAVE to buffer it all in memory before manipulating it, and if you do not want to truncate the entity body, you HAVE to pass the buffer on to the child request on invoking HSE_REQ_EXEC_URL&lt;/LI&gt;
&lt;LI&gt;Since ISAPI Extension operates an an application-level, you know that you get "request entity body POSTed by an HTML FORM" when you read ECB-&amp;gt;lpbData for the pre-buffered data or call ReadClient() to get remaining data. No parsing, decrypting, or de-chunking required&lt;/LI&gt;
&lt;LI&gt;This method works fine with SSL&lt;/LI&gt;
&lt;LI&gt;The URL of the request determines the effective Application Mappings (including wildcard) of the request. Thus, it does not matter if you configure a wildcard application mapping at the global W3SVC/ScriptMaps level - if a child node has an overriding ScriptMaps setting which does NOT contain your wildcard application mapping, it will NOT execute for requests whose effective metadata comes from that overriding ScriptMaps setting.&lt;BR&gt;&lt;BR&gt;In other words, unlike Global ISAPI Filter which reliably triggers on every request, a "global" wildcard application mapping can be silenced by a child URL whose effective ScriptMaps property does NOT include that wildcard application mapping.&lt;/LI&gt;&lt;/UL&gt;&lt;/OL&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=595206" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/ISAPI/default.aspx">ISAPI</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Tips/default.aspx">Tips</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/HOWTO_2E002E002E00_/default.aspx">HOWTO...</category></item><item><title>HOWTO: Retrieve Request Headers using ISAPI, ASP, and ASP.Net</title><link>http://blogs.msdn.com/david.wang/archive/2006/04/20/HOWTO-Retrieve-Request-Headers-using-ISAPI-ASP-and-ASP-Net.aspx</link><pubDate>Thu, 20 Apr 2006 16:00:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:579743</guid><dc:creator>Anonymous</dc:creator><slash:comments>31</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/579743.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=579743</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=579743</wfw:comment><description>&lt;P&gt;Developers frequently confuse Request Headers, Response Headers, and Server Variables as well as the appropriate syntax to retrieve/manipulate each of them, depending on the API (ISAPI, ASP, and ASP.Net). I am going to clarify all of this right now. :-)&lt;/P&gt;
&lt;H3&gt;Question:&lt;/H3&gt;&lt;FONT face="courier new" color=#008000&gt;
&lt;P&gt;So I've managed (thanks to your samples) to create a filter that adds a header. I call it SM_USER. When I use ASP to enumerate all the headers I can see it as HTTP_SM_USER in the ALL_HTTP and ALL_RAW header (with a value set).&lt;/P&gt;
&lt;P&gt;Unfortunately though when I list out the HTTP_SM_USER variable there is no value for it. &lt;/P&gt;
&lt;P&gt;I am setting it in the onPreProcessHeaders with a call sort of like this&lt;/P&gt;
&lt;P&gt;fRet = pPPH-&amp;gt;SetHeader(pfc, szMyHeader, szMyHeaderValue);&lt;/P&gt;
&lt;P&gt;The question is: What's happening here? Why can I see it in the ALL but not when I list out the particular header?&lt;/P&gt;
&lt;P&gt;Cheers,&lt;/P&gt;&lt;/FONT&gt;
&lt;H3&gt;Answer:&lt;/H3&gt;
&lt;P&gt;Request Headers, Response Headers, and Server Variables represent three totally different logical concepts available to server-side applications (though not all APIs expose these logical concepts), and you manipulate them in different ways.&lt;/P&gt;
&lt;P&gt;The main syntactic sugar which exists for legacy compat reasons with CGI is that Server Variables ALSO allow retrieval of certain Request Headers when using the special HTTP_ prefix.&lt;/P&gt;
&lt;P&gt;The following is how I treat the logical concepts...&lt;/P&gt;
&lt;H4&gt;Request Headers&lt;/H4&gt;
&lt;P&gt;Request Headers represent the header name/value pairs that the HTTP client, such as a web browser, sent to the server. A typical request looks like the following, and request headers are highlighted:&lt;/P&gt;&lt;PRE&gt;GET / HTTP/1.1\r\n
&lt;FONT color=#0000ff&gt;Host: localhost\r\n
Accept: */*\r\n
My-Request-Header: Dash\r\n
My_Request_Header: Underscore\r\n&lt;/FONT&gt;
\r\n&lt;/PRE&gt;
&lt;H4&gt;Response Headers&lt;/H4&gt;
&lt;P&gt;Response Headers represent the header name/value pairs that the HTTP server, such as a web server, sends to the client. A typical response looks like the following, and response headers are highlighted:&lt;/P&gt;&lt;PRE&gt;HTTP/1.1 200 OK\r\n
&lt;FONT color=#ff0000&gt;Content-Type: text/html\r\n
Server: Microsoft-IIS/6.0\r\n
Date: Thu, 20 Apr 2006 09:00:00 GMT\r\n
My-Response-Header: Dash\r\n
My_Response_Header: Underscore\r\n
Content-Length: 11\r\n&lt;/FONT&gt;
\r\n
Hello World&lt;/PRE&gt;
&lt;H4&gt;Server Variables&lt;/H4&gt;
&lt;P&gt;Server Variables represent runtime state available to the server-side application for request processing. The list of variables and syntax available on IIS is &lt;A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/iissdk/html/21b3be8f-d4ed-4059-8e21-6cba2c253006.asp"&gt;described here&lt;/A&gt;. They include state like:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;SCRIPT_NAME (client-requested URL, such as "/") 
&lt;LI&gt;AUTH_TYPE (type of authentication performed) 
&lt;LI&gt;etc&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;As stated earlier, it also supports syntactic sugar to access Request Headers via the HTTP_ prefix, in accordance to the CGI 1.1 Specification.&lt;/P&gt;
&lt;H4&gt;The CGI Specification for Accessing Request Headers&lt;/H4&gt;
&lt;P&gt;The &lt;A href="http://hoohoo.ncsa.uiuc.edu/cgi/env.html"&gt;CGI 1.1 Specification&lt;/A&gt;&amp;nbsp;defines the HTTP_ prefix to retrieve Request Headers. Dashes in Header Name transform into Underscores with HTTP_ prefix prepended and make them available as Server Variables (in the CGI context, available as Environment Variables).&lt;/P&gt;
&lt;P&gt;In other words, the HTTP_ACCEPT_ENCODING Server Variable name retrieves the Accept-Encoding: Request Header.&lt;/P&gt;
&lt;P&gt;The astute reader should notice a quirk between the HTTP and CGI specifications - in particular, how does one retrieve the Accept_Encoding: Request Header in CGI???&lt;/P&gt;
&lt;P&gt;Remember, HTTP Request Header names are message-headers, which &lt;A href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2"&gt;by definition&lt;/A&gt; are (I have collected all the relevant BNF definitions for reference):&lt;/P&gt;&lt;PRE&gt;       message-header = field-name ":" [ field-value ]
       field-name     = token
       field-value    = *( field-content | LWS )
       field-content  = &amp;lt;the OCTETs making up the field-value
                        and consisting of either *TEXT or combinations
                        of token, separators, and quoted-string&amp;gt;

       token          = 1*&amp;lt;any CHAR except CTLs or separators&amp;gt;
       separators     = "(" | ")" | "&amp;lt;" | "&amp;gt;" | "@"
                      | "," | ";" | ":" | "\" | &amp;lt;"&amp;gt;
                      | "/" | "[" | "]" | "?" | "="
                      | "{" | "}" | SP | HT

       OCTET          = &amp;lt;any 8-bit sequence of data&amp;gt;
       CHAR           = &amp;lt;any US-ASCII character (octets 0 - 127)&amp;gt;
       CTL            = &amp;lt;any US-ASCII control character
                        (octets 0 - 31) and DEL (127)&amp;gt;
       SP             = &amp;lt;US-ASCII SP, space (32)&amp;gt;
       HT             = &amp;lt;US-ASCII HT, horizontal-tab (9)&amp;gt;

       LWS            = [CRLF] 1*( SP | HT )
       TEXT           = &amp;lt;any OCTET except CTLs,
                        but including LWS&amp;gt;&lt;/PRE&gt;
&lt;P&gt;According to HTTP specifications, header names can include any CHAR except CTLs or separators. In particular, both "-" (dash) and "_" (underscore) are valid characters in header name.&lt;/P&gt;
&lt;P&gt;The problem should be clear now. The CGI 1.1 Specification defines a substitution of "-" in Request Header&amp;nbsp;to "_" in Server Varible names, but what is the substitution of "_" in Request Headers in Server Variable??? It cannot be "_" in Server Variable names, and it is conveniently undefined...&lt;/P&gt;
&lt;P&gt;Oops! Did we just spot a flaw in a sacred specification underlying major Internet Applications? You bet. Oh no, heavens forbid that there is a flaw in a publicly implemented specification! The sky is falling!&lt;/P&gt;
&lt;P&gt;And you can also bet that it is not the only one in existence... so the next time you think that a peculiar software behavior is due to a software bug and not a specification bug, think again... because believe it or not, even the W3C recommendations are not flawless. Remember,&amp;nbsp;to err is human, and humans write those documents and software.&lt;/P&gt;
&lt;P&gt;When troubleshooting, I always recommend to think and evaluate the situation instead of assuming one or another is magically sacred. For example, developers tend to assume that their code is perfect and that the flaws must be from the system.&lt;/P&gt;
&lt;H4&gt;The Solution in IIS 6.0&lt;/H4&gt;
&lt;P&gt;Yes, we decided to do something unspecified in IIS 6.0 to resolve this situation. Otherwise, how can you possibly retrieve the SM_USER request header within ASP?&lt;/P&gt;
&lt;P&gt;We introduced the HEADER_ prefix for Server Variable names which uses the name as-is to retrieve request headers. In other words:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;ServerVariable( "HEADER_SM_USER" ) retrieves SM_USER: Request Header 
&lt;LI&gt;ServerVariable( "HEADER_SM-USER" ) retrieves SM-USER: Request Header&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;It is a neat solution to the problem. Yes, it is unspecified, but I think users are better off with than without it.&lt;/P&gt;
&lt;H4&gt;Table of Operations&lt;/H4&gt;
&lt;P&gt;Whew... soapbox aside, the following table categorizes the available syntax to retrieve the three logical concepts of Request Header, Response Header, and Server Variable. Without parsing, of course...&lt;/P&gt;
&lt;TABLE border=1&gt;
&lt;TBODY&gt;
&lt;TR bgColor=#000000&gt;
&lt;TD width=100&gt;&lt;FONT color=#ffffff&gt;API / Concept&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD width="45%"&gt;&lt;FONT color=#ffffff&gt;Request Header&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD width="25%"&gt;&lt;FONT color=#ffffff&gt;Response Header&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD width="30%"&gt;&lt;FONT color=#ffffff&gt;Server Variable&lt;/FONT&gt;&lt;/TD&gt;
&lt;TR&gt;
&lt;TD&gt;ISAPI Filter&lt;/TD&gt;
&lt;TD&gt;PreprocHeaders:&lt;BR&gt;GetHeader("Header_As-is:")&lt;BR&gt;&lt;BR&gt;AuthComplete:&lt;BR&gt;GetHeader("Header_As-is:")&lt;BR&gt;&lt;BR&gt;Any event after PreprocHeaders:&lt;BR&gt;GetServerVariable:&lt;BR&gt;HTTP_ prefix&lt;BR&gt;HEADER_ prefix&lt;BR&gt;&lt;/TD&gt;
&lt;TD&gt;SendResponse:&lt;BR&gt;GetHeader("Header_As-is:")&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;GetServerVariable:&lt;BR&gt;HTTP_ prefix&lt;BR&gt;HEADER_ prefix&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;ISAPI Extension&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;GetServerVariable:&lt;BR&gt;HTTP_ prefix&lt;BR&gt;HEADER_ prefix&lt;/P&gt;&lt;/TD&gt;
&lt;TD&gt;Not Possible&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;GetServerVariable:&lt;BR&gt;HTTP_ prefix&lt;BR&gt;HEADER_ prefix&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;ASP&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;GetServerVariable:&lt;BR&gt;HTTP_ prefix&lt;BR&gt;HEADER_ prefix&lt;/P&gt;&lt;/TD&gt;
&lt;TD&gt;Not Possible&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;GetServerVariable:&lt;BR&gt;HTTP_ prefix&lt;BR&gt;HEADER_ prefix&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;ASP.Net&lt;/TD&gt;
&lt;TD&gt;Request.Headers("Header_As-is")&lt;BR&gt;&lt;BR&gt;GetServerVariable:&lt;BR&gt;HTTP_ prefix&lt;BR&gt;HEADER_ prefix&lt;/TD&gt;
&lt;TD&gt;Not Possible&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;GetServerVariable:&lt;BR&gt;HTTP_ prefix&lt;BR&gt;HEADER_ prefix&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;P&gt;The key take-aways:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;ISAPI Filters can retrieve everything as-is, assuming you are in the right filter event. You just need to remember that header names MUST have the ":" appended, while GetServerVariable() calls using HTTP_ or HEADER_ prefix do NOT have ":" 
&lt;LI&gt;GetServerVariable() calls using HTTP_ or HEADER_ prefix work everywhere (after PreprocHeaders event for ISAPI Filters) to retrieve request headers, especially when using HEADER_ prefix 
&lt;LI&gt;ASP.Net offers the Request.Headers collection to give you parsed access to the Request Headers 
&lt;LI&gt;Notice that ASP and ASP.Net behavior mirror that of ISAPI Extension... because they are implemented as ISAPI Extension DLLs on IIS&lt;/LI&gt;&lt;/UL&gt;
&lt;H4&gt;ASP Example:&lt;/H4&gt;
&lt;P&gt;For example, using &lt;A href="http://www.microsoft.com/windowsserver2003/iis/diagnostictools/default.mspx"&gt;WFetch&lt;/A&gt;, try making the following raw request:&lt;/P&gt;&lt;PRE&gt;GET /GetServerVariable.asp HTTP/1.1\r\n
&lt;FONT color=#0000ff&gt;Host: localhost\r\n
Accept: */*\r\n
My-Request-Header: Dash\r\n
My_Request_Header: Underscore\r\n&lt;/FONT&gt;
\r\n&lt;/PRE&gt;
&lt;P&gt;And have the following source code for /GetServerVariable.asp&lt;/P&gt;&lt;FONT face="courier new" color=#008000&gt;&lt;PRE&gt;&amp;lt;%
headerDash = "My-Request-Header"
headerUnder = "My_Request_Header"
HTTP_headerDash = "HTTP_" &amp;amp; headerDash
HTTP_headerUnder = "HTTP_" &amp;amp; headerUnder
HEADER_headerDash = "HEADER_" &amp;amp; headerDash
HEADER_headerUnder = "HEADER_" &amp;amp; headerUnder

Response.Write( HTTP_headerDash &amp;amp; " = " &amp;amp; Request.ServerVariables( HTTP_headerDash ) &amp;amp; VbCrLf )
Response.Write( HTTP_headerUnder &amp;amp; " = " &amp;amp; Request.ServerVariables( HTTP_headerUnder ) &amp;amp; VbCrLf )
Response.Write( HEADER_headerDash &amp;amp; " = " &amp;amp; Request.ServerVariables( HEADER_headerDash ) &amp;amp; VbCrLf )
Response.Write( HEADER_headerUnder &amp;amp; " = " &amp;amp; Request.ServerVariables( HEADER_headerUnder ) &amp;amp; VbCrLf )
%&amp;gt;&lt;/PRE&gt;&lt;/FONT&gt;
&lt;P&gt;You get the following output:&lt;/P&gt;&lt;FONT color=#008000&gt;&lt;PRE&gt;HTTP_My-Request-Header = Dash\r\n
HTTP_My_Request_Header = Dash\r\n
HEADER_My-Request-Header = Dash\r\n
HEADER_My_Request_Header = Underscore\r\n&lt;/PRE&gt;&lt;/FONT&gt;
&lt;P&gt;Notice that HTTP_ syntax only retrieves the header name with dashes, while HEADER_ syntax retrieves both headers with dashes and underscores individually.&lt;/P&gt;
&lt;H4&gt;ASP.Net Example&lt;/H4&gt;
&lt;P&gt;For example, using &lt;A href="http://www.microsoft.com/windowsserver2003/iis/diagnostictools/default.mspx"&gt;&lt;FONT color=#02469b&gt;WFetch&lt;/FONT&gt;&lt;/A&gt;, try making the following raw request:&lt;/P&gt;&lt;PRE&gt;GET /GetServerVariable.aspx HTTP/1.1\r\n
&lt;FONT color=#0000ff&gt;Host: localhost\r\n
Accept: */*\r\n
My-Request-Header: Dash\r\n
My_Request_Header: Underscore\r\n&lt;/FONT&gt;
\r\n&lt;/PRE&gt;
&lt;P&gt;Have the following source code for /GetServerVariable.aspx&lt;/P&gt;&lt;FONT face="courier new" color=#008000&gt;&lt;PRE&gt;&amp;lt;%
dim headerDash,headerUnder, HTTP_headerDash, HTTP_headerUnder, HEADER_headerDash, HEADER_headerUnder

headerDash = "My-Request-Header"
headerUnder = "My_Request_Header"
HTTP_headerDash = "HTTP_" &amp;amp; headerDash
HTTP_headerUnder = "HTTP_" &amp;amp; headerUnder
HEADER_headerDash = "HEADER_" &amp;amp; headerDash
HEADER_headerUnder = "HEADER_" &amp;amp; headerUnder

Response.Write( HTTP_headerDash &amp;amp; " = " &amp;amp; Request.ServerVariables( HTTP_headerDash ) &amp;amp; VbCrLf )
Response.Write( HTTP_headerUnder &amp;amp; " = " &amp;amp; Request.ServerVariables( HTTP_headerUnder ) &amp;amp; VbCrLf )
Response.Write( HEADER_headerDash &amp;amp; " = " &amp;amp; Request.ServerVariables( HEADER_headerDash ) &amp;amp; VbCrLf )
Response.Write( HEADER_headerUnder &amp;amp; " = " &amp;amp; Request.ServerVariables( HEADER_headerUnder ) &amp;amp; VbCrLf )
Response.Write( "Header(" &amp;amp; headerDash &amp;amp; ") = " &amp;amp; Request.Headers( headerDash ) &amp;amp; VbCrLf )
Response.Write( "Header(" &amp;amp; headerUnder &amp;amp; ") = " &amp;amp; Request.Headers( headerUnder ) &amp;amp; VbCrLf )
%&amp;gt;&lt;/PRE&gt;&lt;/FONT&gt;
&lt;P&gt;You get the following output:&lt;/P&gt;&lt;FONT color=#008000&gt;&lt;PRE&gt;HTTP_My-Request-Header = \r\n
HTTP_My_Request_Header = Dash\r\n
HEADER_My-Request-Header = \r\n
HEADER_My_Request_Header = \r\n
Header(My-Request-Header) = Dash\r\n
Header(My_Request_Header) = Underscore\r\n&lt;/PRE&gt;&lt;/FONT&gt;
&lt;P&gt;With ASP.Net, the Headers collection is the only way to get all the header values. HTTP_ prefix only retrieves the header name with dashes, as expected.&lt;/P&gt;
&lt;H4&gt;Conclusion&lt;/H4&gt;
&lt;P&gt;Whew... a very long winded entry. :-)&lt;/P&gt;
&lt;P&gt;But...&amp;nbsp;I wanted to be thorough on the subject of Request Headers, Response Headers, and Server Variables when it comes to ISAPI, ASP, and ASP.Net. So, now you have everything...&lt;/P&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=579743" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/ISAPI/default.aspx">ISAPI</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Sample+Code/default.aspx">Sample Code</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Tips/default.aspx">Tips</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/HOWTO_2E002E002E00_/default.aspx">HOWTO...</category></item><item><title>HOWTO: Get Field Data for Custom Logging with ISAPI Filter</title><link>http://blogs.msdn.com/david.wang/archive/2006/04/15/HOWTO-Get-Field-Data-for-Custom-Logging-with-ISAPI-Filter.aspx</link><pubDate>Sun, 16 Apr 2006 03:25:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:577030</guid><dc:creator>Anonymous</dc:creator><slash:comments>10</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/577030.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=577030</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=577030</wfw:comment><description>&lt;H3&gt;Question:&lt;/H3&gt;&lt;FONT face="courier new" color=#008000&gt;
&lt;P&gt;I'm trying to write a Filter that handles writing a W3C-compliant log file based on a special set of criteria.&amp;nbsp; I have found most of the needed information in GetServerVariables(), but I still need the following things:&lt;/P&gt;
&lt;P&gt;sc-status: The status code returned by the server on the request (i.e. 200 if the HTTP request was OK)&lt;/P&gt;
&lt;P&gt;sc-substatus: Substatus code, it's an entry in the current IIS log files and I'd like to keep it&lt;/P&gt;
&lt;P&gt;sc-win32-status: Appears to always be 0, but I'll keep it if I can.&lt;/P&gt;
&lt;P&gt;sc-bytes: The number of bytes sent back to the client as the response.&lt;/P&gt;
&lt;P&gt;time-taken: The amount of time it took IIS internally to process and complete the request.&lt;/P&gt;
&lt;P&gt;If anyone can help me with the necessary calls to get these values, that would be wonderful.&amp;nbsp; They are values in the normal IIS log files when W3C is selected, so I feel like they must be available.&lt;/P&gt;
&lt;P&gt;Thanks,&lt;/P&gt;&lt;/FONT&gt;
&lt;H3&gt;Answer:&lt;/H3&gt;
&lt;P&gt;You can do it the EASY way or the HARD way. :-) I'll provide you the info; you pick.&lt;/P&gt;
&lt;H4&gt;Easy Way&lt;/H4&gt;
&lt;P&gt;The info you want can be found in the&amp;nbsp;HTTP_FILTER_LOG structure available in the SF_NOTIFY_LOG ISAPI Filter event.&lt;/P&gt;&lt;FONT color=#008000&gt;&lt;PRE&gt;typedef struct _HTTP_FILTER_LOG
{
    const CHAR * pszClientHostName;
    const CHAR * pszClientUserName;
    const CHAR * pszServerName;
    const CHAR * pszOperation;
    const CHAR * pszTarget;
    const CHAR * pszParameters;

    DWORD  dwHttpStatus;
    DWORD  dwWin32Status;

    DWORD  dwBytesSent;             
    DWORD  dwBytesRecvd;            
    DWORD  msTimeForProcessing;     

} HTTP_FILTER_LOG, *PHTTP_FILTER_LOG;
&lt;/PRE&gt;&lt;/FONT&gt;
&lt;P&gt;The SF_NOTIFY_LOG event happens right before IIS is about to write the log entry for that request. The members of HTTP_FILTER_LOG are read/write, so it should be trivial for you to use it for custom logging purposes.&lt;/P&gt;
&lt;P&gt;What are the benefits of using HTTP_FILTER_LOG?&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;You do not need to call GetServerVariable() and [re]-allocate a buffer (see this &lt;A href="/david.wang/archive/2005/08/18/ISAPI_GetServerVariable.aspx"&gt;blog entry&lt;/A&gt; for an example) 
&lt;LI&gt;You can even alter what IIS will log by changing the fields of this structure (see this &lt;A href="/david.wang/archive/2005/09/28/HOWTO_ISAPI_Filter_which_Logs_original_Client_IP_for_Load_Balanced_IIS_Servers.aspx"&gt;blog entry&lt;/A&gt; for an example) - so you can modify pszParameters, have IIS keep the merged log files of all your subdomains (for analysis purposes), and still know which request went to which subdomain&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;The only thing that is missing is sc-substatus, and it cannot be retrieved in ISAPI Filter.&lt;/P&gt;
&lt;H4&gt;Hard Way&lt;/H4&gt;
&lt;P&gt;If you insist on doing it the hard way:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;sc-status can be obtained from SF_NOTIFY_SEND_RESPONSE, in the HTTP_FILTER_SEND_RESPONSE.HttpStatus member. This only works for structured responses... for raw responses, you have to buffer and parse SF_NOTIFY_SEND_RAW_DATA. 
&lt;LI&gt;sc-substatus and sc-win32-status:&lt;BR&gt;&lt;BR&gt;The only way on IIS6&amp;nbsp;to get sc-substatus and sc-win32-status for the response is in the async completion function of an HSE_REQ_EXEC_URL ServerSupportFunction call by calling HSE_REQ_GET_EXEC_URL_STATUS.&lt;BR&gt;&lt;BR&gt;Nowhere else in ISAPI Filter/Extension API do you have access to that value, even though we added sc-substatus in IIS6, because we would break binary compatibility by changing existing structures where it logically belongs: 
&lt;UL&gt;
&lt;LI&gt;You can't read/set it in ISAPI Filter 
&lt;LI&gt;You can't set it in ISAPI Extension 
&lt;LI&gt;You can read it in ISAPI Extension in async completion function HSE_REQ_EXEC_URL&lt;/LI&gt;&lt;/UL&gt;
&lt;LI&gt;sc-bytes - listen on SF_NOTIFY_SEND_RAW_DATA and track HTTP_FILTER_RAW_DATA.cbInData to count number of bytes transferred. Hard part is figuring out how to do this on a per-request basis because pFilterContext is per-connection. 
&lt;LI&gt;time-taken - Set timer between SF_NOTIFY_READ_RAW_DATA and SF_NOTIFY_END_OF_NET_SESSION. This is not compatible with IIS6 Worker Process Isolation Mode. &lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=577030" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/ISAPI/default.aspx">ISAPI</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Tips/default.aspx">Tips</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/HOWTO_2E002E002E00_/default.aspx">HOWTO...</category></item><item><title>IIS6 and HTTP Server API, Part 1</title><link>http://blogs.msdn.com/david.wang/archive/2006/04/07/IIS6-and-HTTP-Server-API-Part-1.aspx</link><pubDate>Fri, 07 Apr 2006 14:15:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:568724</guid><dc:creator>Anonymous</dc:creator><slash:comments>9</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/568724.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=568724</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=568724</wfw:comment><description>&lt;H3&gt;Question:&lt;/H3&gt;&lt;FONT face="courier new" color=#008000&gt;
&lt;P&gt;Hi,&lt;/P&gt;
&lt;P&gt;I've found these articles:&lt;BR&gt;&lt;A href="http://support.microsoft.com/default.aspx?scid=kb;en-us;q311852"&gt;http://support.microsoft.com/default.aspx?scid=kb;en-us;q311852&lt;/A&gt;&lt;BR&gt;&lt;A href="http://support.microsoft.com/default.aspx?scid=kb;en-us;q327611"&gt;http://support.microsoft.com/default.aspx?scid=kb;en-us;q327611&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;First of all I've got a question. READ_RAW_DATA can be used to modify the request body not only the headers. Why is it gone? If you don't want to modify its behavior you should add a new notification type (input and output as well) only for modifying the entity body.&lt;/P&gt;
&lt;P&gt;But my main question is that this article says that IIS 6 operates quite different in IIS 5 mode.&lt;/P&gt;
&lt;P&gt;I section "SSL in IIS 6.0" I've read that in IIS 5 mode IIS 6 hosts SSL and filters can use SF_NOTIFY_READ_RAW_DATA. This can mean one of the two tings:&lt;BR&gt;1. IIS 6 is not using HTTP API at all. In this case a lot of HTTP API based things can be broken, including HTTP API based Windows system services and SQL Server Yukon's web services. If it's the truth is a very strange thing.&lt;/P&gt;
&lt;P&gt;2. IIS 6 is using undocumented HTTP API calls that make it possible to process the raw data. In this case you should document these APIs as they could be used by others.&lt;/P&gt;
&lt;P&gt;httpapi.dll exports some undocumented functions (without header declarations) that are used only by IIS. But as HTTP API is an operating system service and not part of IIS I think you should document all the functionality of HTTP API.&lt;/P&gt;
&lt;P&gt;Sincerely,&lt;/P&gt;&lt;/FONT&gt;
&lt;H3&gt;Answer:&lt;/H3&gt;
&lt;P&gt;Thanks for the query.&amp;nbsp; I've actually not seen either of these KBs until now, but I can tell you that the KB is wrong to say that SSL in IIS6 uses the SF_NOTIFY_READ_RAW_DATA filter notification to function. It does not. SSL in IIS6 is not an ISAPI Filter.&amp;nbsp; I'm also going to try to clarify the info in 311852 because I think the usage of the words "READ_RAW_DATA" and "SF_NOTIFY_READ_RAW_DATA" are quite erratic/confusing, and I'm also going to work to get these KBs fixed.&lt;/P&gt;
&lt;H4&gt;HTTP.SYS, Conceptually...&lt;/H4&gt;
&lt;P&gt;The crux of what is going on is this (rough conceptual details):&lt;/P&gt;
&lt;P&gt;HTTP.SYS basically:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Reads data from the networking layer 
&lt;LI&gt;Parses it 
&lt;LI&gt;Does some basic HTTP-level validations according to HTTP/1.1 spec 
&lt;LI&gt;Routes it to a user mode process for request execution&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;All of these steps happen in kernel mode.&lt;/P&gt;
&lt;H4&gt;SSL and ReadRawData, conceptually...&lt;/H4&gt;
&lt;P&gt;Two concepts muck with this architecture in IIS -- SSL and SF_NOTIFY_READ_RAW_DATA filters.&amp;nbsp; Both concepts require access to the raw data stream prior to it being parsed by HTTP.SYS, meaning that it has to be somehow interjected between steps #1 and #2 above.&amp;nbsp; Since we cannot just execute existing SF_NOTIFY_READ_RAW_DATA filters (which are user mode DLLs) in kernel mode, this means that HTTP.SYS will need to have some "mechanism" to stream raw data from #1 into a user mode process, let it munge the data however it wants, and then return the data to HTTP.SYS in kernel mode for #2 and continue request handling.&amp;nbsp; Needless to say, this is quite a privileged operation.&lt;/P&gt;
&lt;P&gt;Thus, both SSL and SF_NOTIFY_READ_RAW_DATA rely on this "mechanism" to stream raw data from kernel mode into user mode and back, prior to step #2. Now, HTTP.SYS happens to only support this "mechanism" for a SINGLE user mode process for various technical reasons (including the fact that host headers are not known [SF_NOTIFY_READ_RAW_DATA could change it as well]).&lt;/P&gt;
&lt;P&gt;Therefore, all requests that require SSL and/or SF_NOTIFY_READ_RAW_DATA go through this mechanism -- first, the data is read in kernel mode, then transitioned into user mode to munge/decrypt, then transitioned back into kernel mode to parse, and later shuttled back into user mode to execute. Not very pretty, but it works and is not the default case.&lt;/P&gt;
&lt;H4&gt;IIS6 Process Model and SSL/ReadRawData&lt;/H4&gt;
&lt;P&gt;Now, what does this have anything to do with IIS6 modes?&amp;nbsp; Well, in IIS5 Compatibility Mode, the "mechanism" can use inetinfo.exe as the lone user mode process to handle the raw data since both ISAPI Filters and SSL needs to run in that process anyway for compat reasons, and so SF_NOTIFY_READ_RAW_DATA is cleanly supported.&lt;/P&gt;
&lt;P&gt;This all breaks down in IIS6 Worker Process Isolation Mode because individual w3wp.exe load their own ISAPI Filters -- so multiple instances of the same Filter DLL can be in memory in separate w3wp.exe -- and what happens if this Filter uses SF_NOTIFY_READ_RAW_DATA ?&amp;nbsp; We now have &amp;gt;1 process wanting the raw data routed to it, but HTTP.SYS can only route to a single process.&amp;nbsp; Now, you may argue that we should have spun up one single surrogate process to host all ISAPI Filters and SSL, but this design destroys Worker Process Isolation Mode -- this surrogate process is once again a single point of failure for all w3wp.exe and is no better than the IIS5 process model (substitute inetinfo.exe for the single surrogate process and you get the same looking picture).&amp;nbsp; All of the other designs at trying to enable SF_NOTIFY_READ_RAW_DATA in IIS6 Worker Process Isolation Mode have other similar but fatal flaws.&lt;/P&gt;
&lt;H4&gt;The User-Oriented Compromise&lt;/H4&gt;
&lt;P&gt;So, the whole SF_NOTIFY_READ_RAW_DATA concept just does not work with Worker Process Isolation Mode, and hence we did the next best thing -- disable the broken concept, but give a better alternative that allows 99.9% of people to do what they actually wanted to do and live with breaking that 0.1%:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Let SSL have the lone raw data "mechanism" and host it in lsass.exe to&lt;BR&gt;have only one process involved in decrypting/encrypting data 
&lt;LI&gt;Disable SF_NOTIFY_READ_RAW_DATA in IIS6 Worker Process Isolation Mode 
&lt;LI&gt;Make sure that the new IIS6 feature, HSE_REQ_EXEC_URL, can take care of the 99.9% usage case of SF_NOTIFY_READ_RAW_DATA (which is the munge the incoming request, in particular POST entity body from FORMs)&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;From my perspective, the main users that are left in the cold by this decision are the people who wrote proprietary non-opaque stream filters, such as custom encryption/compression, and want to run in IIS6 Worker Process Isolation Mode.&amp;nbsp; There are no solutions in this case other than IIS5 Compatibility Mode.&lt;/P&gt;
&lt;P&gt;Your suggestion of "...If you don't want to modify its behavior you should add a new notification type (input and output as well) only for modifying the entity body." was considered at one point, but we rejected it.&amp;nbsp; We already introduced HSE_REQ_EXEC_URL in ISAPI Extension in IIS6 to handle the 99.9% usage case of modifying the entity body, and ISAPI Filter is not the preferred IIS extension mechanism (ISAPI Extension is far richer and deterministic), so we chose not to add any new filter notifications.&lt;/P&gt;
&lt;P&gt;So, if your whole reason for SF_NOTIFY_READ_RAW_DATA is to get access and control of the incoming request, there are better and supported ways of doing this in IIS6.&amp;nbsp; Configure a ISAPI Extension as a Wildcard Application Mapping and use HSE_REQ_EXEC_URL.&amp;nbsp; You will notice that HSE_REQ_EXEC_URL is able to modify the entire request that is processed by IIS, including URL, headers, entity body, IIS impersonation token, and some ServerVariable values (like AUTH_TYPE, AUTH_USER, LOGON_USER, etc), and when configured as a Wildcard Application Mapping, it gets first crack at all the requests and can filter/redirect as it pleases. HSE_REQ_EXEC_URL is even able to consume and otherwise munge the entire entity body without the child URL realizing it, and it is fully compatible with SSL.&lt;/P&gt;
&lt;P&gt;This is basically what most people want to do on the server, and IIS6 gives it to you in that manner.&amp;nbsp; We didn't have this in prior versions of IIS, so people have had to hack together Filters using SF_NOTIFY_READ_RAW_DATA to buffer and modify incoming requests (including entity body) -- which doesn't work with SSL, SF_NOTIFY_PREPROC_HEADERS filter and combo ISAPI Extension if you just want to change URL, headers, and fudge some ServerVariable values if you coordinate with SF_NOTIFY_AUTHENTICATION, and so on. HSE_REQ_EXEC_URL has none of those hacky limitations -- you simple change the entire request that gets executed by IIS and then tell IIS to execute it.&lt;/P&gt;
&lt;P&gt;You can still do all those hacky things in IIS5 Compatibility Mode (that's what compatibility is for), but we highly discourage it in IIS6 Worker Process Isolation Mode because there are better alternatives.&amp;nbsp; If we didn't have better alternatives, we would have made sure that SF_NOTIFY_READ_RAW_DATA remained the same in all modes.&lt;/P&gt;
&lt;P&gt;If you want to treat HTTP logically, such as URL, headers, entity-body, you can either do the hard work of parsing through data stream as with the APIs you're asking about, or you can use the logical API of HSE_REQ_EXEC_URL to modify the exact same thing prior to IIS executing the request.&amp;nbsp; I leave it up to you to decide which is easier and makes more sense.&lt;/P&gt;
&lt;H4&gt;Conclusion&lt;/H4&gt;
&lt;P&gt;Personally, I suggest that you don't get hung up on SF_NOTIFY_READ_RAW_DATA nor HTTPAPI -- you're really not "losing" anything important other than a big hairball mess.&lt;/P&gt;
&lt;P&gt;As for your statement in #2 about IIS6 using undocumented HTTP API to process the raw data -- I am not a lawyer nor am I authoritative on the subject, but I can tell you that:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;The IIS and HTTP.SYS teams spent a lot of time with the lawyers, who grilled us over all of this 
&lt;LI&gt;Legally, IIS6 is considered a part of Windows Server 2003 OS; thus this is merely a private API between two OS components -- which is perfectly acceptable by the consent decree&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=568724" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/ISAPI/default.aspx">ISAPI</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Tips/default.aspx">Tips</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/HTTP.SYS/default.aspx">HTTP.SYS</category></item><item><title>HTTP.SYS, IIS, and the 100 continue</title><link>http://blogs.msdn.com/david.wang/archive/2006/04/05/HTTP-SYS-IIS-and-the-100-continue.aspx</link><pubDate>Wed, 05 Apr 2006 10:20:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:568709</guid><dc:creator>Anonymous</dc:creator><slash:comments>23</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/568709.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=568709</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=568709</wfw:comment><description>&lt;H3&gt;Question:&lt;/H3&gt;&lt;FONT face="courier new" color=#008000&gt;
&lt;P&gt;Hi David,&lt;/P&gt;
&lt;P&gt;"My Company"&amp;nbsp;is a leading middleware provider for mobile multiplayer games. Cutsomer like Disney, THQ etc.&lt;/P&gt;
&lt;P&gt;The backend is built on .NET. We went live in the US with W2K3/IIS6 which is great.&lt;/P&gt;
&lt;P&gt;But we have a major IIS6 issue. The handsets connect through HTTP/POST and sometimes the Server answers with "100 continue" and this leads to crashes on certain phones.&lt;/P&gt;
&lt;P&gt;In IIS5 we wrote an ISAPI filter - which works well.&lt;/P&gt;
&lt;P&gt;Question:&lt;/P&gt;
&lt;P&gt;1) Is IIS6 sending the "100 continue". We assume yes ... (due to file must exist)&lt;/P&gt;
&lt;P&gt;2) Will an ISAPI Extension be able to fullfill this?&lt;/P&gt;
&lt;P&gt;I would really apprciate your help.&lt;/P&gt;
&lt;P&gt;thanks,&lt;/P&gt;&lt;/FONT&gt;
&lt;H3&gt;Answer:&lt;/H3&gt;
&lt;P&gt;&amp;lt;soapbox&amp;gt;&lt;/P&gt;
&lt;P&gt;Actually, I would&amp;nbsp;frame it differently - this is probably NOT a "major&amp;nbsp;IIS6 issue".&lt;/P&gt;
&lt;P&gt;Clients which advertise to be HTTP/1.1 compliant and then crash on "100 continue"&amp;nbsp;are the real problem (they are not following public specifications), and servers that allow such broken clients are also a part of the problem.&lt;/P&gt;
&lt;P&gt;Technically, these phones should just keep crashing until the consumer gets sick of it and switches to another phone that works correctly. This is the way to get the phone manufacturers to write/use properly implemented networking protocol stacks - when their customers hit their pocketbooks. As middleware, it should not differ if the consumer uses one phone or another to run your games... as long as&amp;nbsp;the consumer has *A* phone that runs your games.&lt;/P&gt;
&lt;P&gt;On the other hand, if the server keeps hacking to work around client-side bugs, the phone manufacturers never get wind of their problems and have ZERO incentive to fix their phones, leading to accumulation of server-side hacks over time that increase server maintenance costs for you.&lt;/P&gt;
&lt;P&gt;Thus, it is in your best interest to notify and get phone manufacturers to fix their buggy software.&lt;/P&gt;
&lt;P&gt;I understand that business pressures can force you to compromise and otherwise work-around such issues in the spirit of "making things work", but I just want to remind you of the implications of your actions on your long-term interests.&lt;/P&gt;
&lt;P&gt;&amp;lt;/soapbox&amp;gt;&lt;/P&gt;
&lt;P&gt;On Windows Server 2003, it is a cooperation between IIS6 in user mode and HTTP.SYS in kernel mode that drives HTTP request/response serving. Logically speaking:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;HTTP.SYS picks up data off the network, parses it into HTTP requests, identifies which Application Pool it belongs, and places it into its queue. 
&lt;LI&gt;IIS6 user mode worker process picks up requests from the queue of its Application Pool, processes each by running either a user-supplied ISAPI/CGI or the built-in IIS Static File Handler, and hands the response back to HTTP.SYS to send over the wire.&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;A "100 continue", like a "400 Bad Request" or a Kernel Response Cache Hit, is special in that HTTP.SYS transparently handles it in kernel mode without notifying user mode of anything. In addition, ISAPI Extensions cannot interact with any response output - they can only generate response output, not see results of response output. Thus, an ISAPI Extension will never be able to interact with requests that generate "100 continue" nor "100 continue" responses themselves to suppress them.&lt;/P&gt;
&lt;P&gt;On IIS6, the only way to inject user mode processing into these transparent request handlings of HTTP.SYS is to run in IIS5 Compatibility Mode and use an ReadRawData/SendRawData ISAPI Filter. ReadRawData forces HTTP.SYS to hand the raw data off the network into user mode for filtering PRIOR to parsing that user mode output into HTTP requests to place into queues.&lt;/P&gt;
&lt;P&gt;Of course, this method completely defeats the purpose of running IIS6 with Application Pools and process&amp;nbsp;isolation (a single failure in this filtering user mode process halts the entire server)... but such is the&amp;nbsp;server-side compromise when the client is buggy...&lt;/P&gt;
&lt;P&gt;FYI: This approach will not work on Vista Server/IIS7. HTTP.SYS will no longer hand raw data off the network into user mode for filtering prior to parsing, so it will be impossible for user mode code to know that a request which triggers the automatic "100 continue" happened.&lt;/P&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=568709" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/ISAPI/default.aspx">ISAPI</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS+7.0+_2800_beta_2900_/default.aspx">IIS 7.0 (beta)</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/HTTP.SYS/default.aspx">HTTP.SYS</category></item><item><title>HOWTO: Install and run PHP on IIS7</title><link>http://blogs.msdn.com/david.wang/archive/2006/04/04/HOWTO-Install-and-run-PHP-on-IIS7.aspx</link><pubDate>Tue, 04 Apr 2006 12:56:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:567868</guid><dc:creator>Anonymous</dc:creator><slash:comments>66</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/567868.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=567868</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=567868</wfw:comment><description>&lt;H3&gt;Question:&lt;/H3&gt;&lt;FONT face="courier new" color=#008000&gt;
&lt;P&gt;Hi all,&lt;/P&gt;
&lt;P&gt;I have trouble to configure PHP 5 (CGI mode) in IIS 7 on Vista 5308...&lt;/P&gt;
&lt;P&gt;What I did:&lt;BR&gt;- Download and unpack PHP&lt;BR&gt;- set user permission to PHP folder AND script folder (IIS_IUSRS and Internet Guest Account)&lt;BR&gt;- in IIS Manager: Added pages in Default documents&lt;BR&gt;- Added in "ISAPI and GCI Restrictions": Allowed PHP&amp;nbsp; group ID PHP path c:\php\php-cgi.exe&lt;BR&gt;- Added in "Global Modules" PHP as integratedmode path c:\php\php-cgi.exe&lt;BR&gt;- Added in "Handler" PHP to map *.php,*.php3 type:File path c:\php\php-cgi.exe all verb handler:cgimodule&lt;BR&gt;- Added in "module": PHP code:c:\php\php-cgi.exe module:native entry:local&lt;/P&gt;&lt;/FONT&gt;
&lt;H3&gt;Answer:&lt;/H3&gt;
&lt;P&gt;Unfortunately, at this point in the beta cycle, IIS7 configuration failures are still cryptic, so you basically need to really know what you are configuring. However, most things are conceptually the same as prior IIS versions - just maybe named/organized or configured differently.&lt;/P&gt;
&lt;P&gt;For many reasons, I do not use the UI nor give instructions for the UI (except if explicitly talking about a UI-behavior), so all my instructions directly manipulate the necessary .config files with the right values to generate the desired behavior.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;The UI is still under development and can change from build to build, including introduce bugs.&amp;nbsp;A UI bug should not prevent you from using server features. 
&lt;LI&gt;The server behavior (and hence configuration) rarely changes. The UI surrounding it may change in response to concepts/feedback, so UI-based instructions can be out-of-date, especially during beta. 
&lt;LI&gt;I want to illustrate how to configure and use IIS, not how to use the UI to generate the necessary configuration.&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;In your case, you basically chose to configure all possible locations, but that is not all correct and thus IIS7 will mysteriously fail. In particular, configuring PHP as a "Global Module" or "Module" is incorrect.&lt;/P&gt;
&lt;H4&gt;Instructions&lt;/H4&gt;
&lt;P&gt;This is all you should do (and why):&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Install PHP into %SYSTEMDRIVE%\Inetpub\PHP (I want to easily inherit any IIS7-related ACLs) 
&lt;LI&gt;Download and unpack PHP into %SYSTEMDRIVE%\Inetpub\PHP (For this example, I chose the latest currently available build, PHP 5.1.2 ZIP package, from &lt;A href="http://www.php.net/downloads.php"&gt;http://www.php.net/downloads.php&lt;/A&gt;&amp;nbsp;) 
&lt;LI&gt;Use the recommended PHP.INI-Recommended INI file inside the PHP directory (or whatever PHP configuration you need) 
&lt;LI&gt;Configure a Handler for *.php using %SYSTEMDRIVE%\Inetpub\PHP\PHP5ISAPI.DLL or %SYSTEMDRIVE%\Inetpub\PHP\PHP-CGI.EXE (I prefer the ISAPI version on IIS) and order the Handler AHEAD of the StaticFile Handler 
&lt;LI&gt;Configure an ISAPI and CGI Restrictions entry to enable the added Handler for PHP&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;This is all you need to do. Conceptually, it is not any different than prior IIS versions, like IIS6, and the necessary data is pretty much the same. The only difference is that you are configuring a Handler instead of an Application Mapping. This is because we have reconciled the IIS Application Mapping with the ASP.Net httpHandlers (as well as many other things, such as ISAPI Filters and httpModules) to form the new IIS7 Integrated Pipeline. The new unified section is called "handlers".&lt;/P&gt;
&lt;H4&gt;The Details&lt;/H4&gt;
&lt;P&gt;The astute Reader should note that it is important to place the PHP Handler ahead of the StaticFile Handler. Why?&lt;/P&gt;
&lt;P&gt;Unlike the IIS Application Mapping list, which is unordered, the Handlers list is ordered. This means:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;For Application Mappings prior to IIS7, we chose to have arbitrary behavior of wildcard application mappings always run first to handle a request, and if unhandled, then the first Application mapping with a matching extension is executed). 
&lt;LI&gt;For Handlers in IIS7, we chose to have first-match (of extension/verb) wins. This gives user complete control over Handler resolution order; no arbitrary ordering imposed by IIS core.&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;In other words, you can configure IIS7 Handlers to act like pre-IIS7 Application Mappings by doing the following (conceptual). Note that IIS6 hardcodes the ordering of WildcardApplicationMappings and StaticFileModule to be first and last, respectively... while IIS7 allows you to order the Handlers however you want:&lt;/P&gt;&lt;FONT face="courier new" color=#008000&gt;
&lt;P&gt;&amp;lt;handlers&amp;gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;add modules="WildcardApplicationMapping1" verb="*" path="*" /&amp;gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;add modules="WildcardApplicationMapping2" verb="*" path="*" /&amp;gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;add modules="IsapiModule" verb="GET,HEAD,POST" path="*.asp" /&amp;gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;add modules="StaticFileModule" verb="*" path="*" /&amp;gt;&lt;BR&gt;&amp;lt;/handlers&amp;gt;&lt;/P&gt;&lt;/FONT&gt;
&lt;P&gt;Of course, with power comes responsibility. With such a flexible system, it means that the StaticFile Handler, which maps to all extensions and all verbs, must be configured last so that it is the Handler of last resort after all other Handlers fail to match. This means that any added&amp;nbsp; handlers must be ahead of the "wildcard" handlers since otherwise the wildcard handler will get picked first.&lt;/P&gt;
&lt;H4&gt;Instructions (Automation)&lt;/H4&gt;
&lt;P&gt;Ok, suppose the Instructions weren't clear enough. Here is the commandline version of the above instructions. :-)&lt;/P&gt;&lt;FONT face="courier new" color=#008000&gt;
&lt;P&gt;MKDIR %SYSTEMDRIVE%\Inetpub\PHP&lt;BR&gt;ECHO Extract PHP files from ZIP archive into %SYSTEMDRIVE%\Inetpub\PHP&lt;BR&gt;COPY /Y %SYSTEMDRIVE%\Inetpub\PHP\PHP.INI-Recommended %SYSTEMDRIVE%\Inetpub\PHP\PHP.INI&lt;BR&gt;PUSHD %SYSTEMROOT%\System32\inetsrv&lt;BR&gt;APPCMD SET CONFIG -section:handlers -+[name='PHP',path='*.php',verb='GET,HEAD,POST',modules='IsapiModule',scriptProcessor='%SYSTEMDRIVE%\Inetpub\PHP\php5isapi.dll',resourceType='File']&lt;BR&gt;APPCMD SET CONFIG -section:isapiCgiRestriction -+[path='%SYSTEMDRIVE%\Inetpub\PHP\php5isapi.dll',allowed='true',groupId='PHP',description='PHP5']&lt;BR&gt;POPD&lt;/P&gt;&lt;/FONT&gt;
&lt;P&gt;Yes, I am using the new APPCMD commandline tool for manipulating IIS7 configuration to make scriptable changes. You can view it as the IIS7 uber-replacement for adsutil.vbs and all of the IIS6 commandline tools. Bear with the cryptic syntax for the moment - it is really quite handy once you get past the syntax (especially the syntax for collection manipulation - I can spend a blog entry just talking about it alone ;-) ).&lt;/P&gt;
&lt;H4&gt;Conclusion&lt;/H4&gt;
&lt;P&gt;The above instructions are all the IIS-specific steps necessary to get PHP to work on IIS7. I was able to run a PHP page containing &amp;lt;?php phpinfo();?&amp;gt; after making just those changes on top of a default IIS7 installation. If you still have problems, it is most likely coming from PHP itself.&lt;/P&gt;
&lt;P&gt;For example, php-cgi.exe does not seem to work because its CGI output is missing the Status: field as required by CGI 1.1 specification.&lt;/P&gt;&lt;FONT color=#008000&gt;
&lt;P&gt;[Modified 4-4-2006] php-cgi.exe does work on IIS7. You need to modify PHP.INI to have the line:&lt;/P&gt;
&lt;P&gt;cgi.force_redirect = 0&lt;/P&gt;
&lt;P&gt;If you do not do this modification, PHP-CGI.EXE outputs a security warning response without proper response&amp;nbsp;headers, which causes IIS to return a 502 Bad Gateway. Strangely,&amp;nbsp;running&amp;nbsp;PHP-CGI.EXE from the commandline does not generate this error - I guess using NPH CGI is the only way to debug PHP, because running it from the commandline is not 100% indicative of web-server Runtime behavior.&lt;/P&gt;
&lt;P&gt;[Modified 6-14-2006] Removed the following lines of batch script to work-around of APPCMD manipulation of StaticFile module since the bug got fixed.&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#ff0000&gt;APPCMD SET CONFIG -section:handlers --[name='StaticFile',path='*',verb='*']&lt;BR&gt;&lt;/FONT&gt;&lt;FONT color=#ff0000&gt;APPCMD SET CONFIG -section:handlers -+[name='StaticFile',path='*',verb='*',modules='StaticFileModule,DefaultDocumentModule,DirectoryListingModule',resourceType='Either',requireAccess='Read']&lt;/FONT&gt;&lt;/P&gt;&lt;/FONT&gt;&lt;FONT color=#008000&gt;
&lt;P&gt;[Modified 6-24-2006] Linked to newer instructions &lt;A href="http://blogs.msdn.com/david.wang/archive/2006/06/24/HOWTO_Install_and_Run_PHP_on_IIS7_Part_3.aspx"&gt;here&lt;/A&gt;.&lt;/P&gt;&lt;/FONT&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=567868" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/ISAPI/default.aspx">ISAPI</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS+7.0+_2800_beta_2900_/default.aspx">IIS 7.0 (beta)</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Tips/default.aspx">Tips</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/HOWTO_2E002E002E00_/default.aspx">HOWTO...</category></item><item><title>Thoughts on Changing Response Content Type</title><link>http://blogs.msdn.com/david.wang/archive/2006/04/03/Thoughts-on-Changing-Response-Content-Type.aspx</link><pubDate>Mon, 03 Apr 2006 13:18:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:567165</guid><dc:creator>Anonymous</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/david.wang/comments/567165.aspx</comments><wfw:commentRss>http://blogs.msdn.com/david.wang/commentrss.aspx?PostID=567165</wfw:commentRss><wfw:comment>http://blogs.msdn.com/david.wang/rsscomments.aspx?PostID=567165</wfw:comment><description>&lt;H3&gt;Question:&lt;/H3&gt;&lt;FONT face="courier new" color=#008000&gt;
&lt;P&gt;Hi&lt;/P&gt;
&lt;P&gt;I´m using IIS 5 with ASP NET 2.0. I´ve develop an isapi application. the response content type its text/hml it´s posible chaged to text/xml&lt;/P&gt;
&lt;P&gt;thanks &lt;/P&gt;&lt;/FONT&gt;
&lt;H3&gt;Answer:&lt;/H3&gt;
&lt;P&gt;In general, the handler which generates the response should own its Content-Type because it should be the only entity that knows the true "type of content" that is generated.&lt;/P&gt;
&lt;P&gt;Thus, I recommend that you change the ISAPI application to have the right logic to output the right Content-Type.&lt;/P&gt;
&lt;P&gt;If you cannot change the ISAPI application to send the right Content-Type, then you need to put this logic elsewhere:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;ISAPI Filter - If you just want to change the Content-Type response header, then you can write a SF_NOTIFY_SEND_RESPONSE filter to modify that response header (assuming the response is logical). If you want to change response entity, then that is much harder because it requires buffering of response in&amp;nbsp;SF_NOTIFY_SEND_RAW_DATA to perform the necessary modifications. This also turns off various caches and destroy static file performance for resources like text/xml or text/html.&lt;/LI&gt;
&lt;LI&gt;ASP.Net can only change the output of static file or ASP.Net pages serviced by ASP.Net. In particular, ASP.Net cannot change the output of an ISAPI application.&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;I do not know WHY you want to do this, but what you want to do does not appear to be architecturally sound.&lt;/P&gt;
&lt;P&gt;//David&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=567165" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/david.wang/archive/tags/ISAPI/default.aspx">ISAPI</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/IIS/default.aspx">IIS</category><category domain="http://blogs.msdn.com/david.wang/archive/tags/Your+Questions/default.aspx">Your Questions</category></item></channel></rss>