Wednesday, March 28, 2007

QA - Popular IIS7 Concepts

Question:

Got another dumb question that I'm just struggling with.

What is meant by distributed configuration in IIS 7.0?

My guess (which I hate guessing) is that they are talking about the applicationHost.config file and web.config file(s). Would they also be talking about the ability to extend the schema?


Answer:

I suggest first reading this blog entry on distributed configuration.

"Distributed Configuration" refers to unified configuration hierarchy merged from multiple sources (i.e. files in arbitrary directories). Pragmatic way to think about it involves applicationHost.config and web.config file(s) and how values contained in them are merged into an unambiguous and effective result for every possible URL. It is analogous to "distributed NTFS ACLs", where every file resource either inherits from its containing directory or has its own overrides, and when a CreateFile() call accesses the file, an effective access check is verified.

Ability to extend the schema would be called "Extensible Configuration". Just as IIS7 can be viewed as Microsoft providing 40+ modules to provide IIS6-level behavior on top of the new Integrated Pipeline, the IIS7 configuration system can be viewed as Microsoft shipping new/existing ConfigSectionHandlers to provide a web.config-like user experience on top of the new extensible configuration system.

A related topic - "Delegatable Administration" refers to multiple users able to administer their own website; which basically piggybacks on "Distributed Configuration" and "filesystem ACLs" to determine who can configure what and where, and "Integrated Pipeline" to control what actually executes for a given configuration.


//David

Wednesday, March 07, 2007

HOWTO - Configure Orphaning on IIS6

Question:

Hi. We are getting an error on the production server: A process serving application pool (pool name) was orphaned, but the specified orphan action (some action) could not be executed.

I remember someone added something to get a memory dump on application hangs there, but the file in (some action) is no longer there. How do I remove the action associated with it?

Answer:

Why, you simply remove that something that someone added which is no longer there, of course... :-P

Joking aside, the relevant metabase properties in your situation are:

How to find all this? Search with key terms like:

OrphanWorkerProcess site:microsoft.com

At this point, you have two basic choices to address the error:

  1. Turn off Orphaning. This stops IIS from orphaning, which also prevents it from running the failing action
  2. Delete the OrphanActionEXE and OrphanActionParams properties. IIS continues to orphan worker processes but stops running the custom action

Personally, I recommend #1 because Orphaning helps debugging when monitored... and hopefully you find the issues in test environments and do not intend to debug production servers

Now, suppose you want to delete the above Orphaning properties. How to find where they are set, what their current values are, and how to delete them? Use your friend ADSUTIL.VBS. For example, the following illustrate sample management of worker process Orphaning.

PUSHD %SYSTEMDRIVE%\Inetpub\Adminscripts

REM Identify all AppPools that set OrphanWorkerProcess
CSCRIPT ADSUTIL.VBS FIND OrphanWorkerProcess

REM Retrieve the value of OrphanWorkerProcess at the global level
CSCRIPT ADSUTIL.VBS GET W3SVC/AppPools/OrphanWorkerProcess

REM Delete the OrphanWorkerProcess property for the DefaultAppPool
CSCRIPT ADSUTIL.VBS DELETE W3SVC/AppPools/DefaultAppPool/OrphanWorkerProcess
POPD

//David

Monday, January 29, 2007

HOWTO: POST Resources to IIS

Question:

i'm working on a piece that requires companies to be able to POST data to one another's web sites using just the URL without page name (i.e. http://doc_uploads.partnetsite.com). there's also no need for any kind of web interface at either end, so i can't expect to grab the data from a form field and save to disk. simply put, using an application like CURL, i should be able to execute a command that says "post myFile.txt to http://doc_uploads.partnersite.com".

i need to figure out how to configure doc_uploads.mysite.com to listen for POST commands and start up an application to capture the POST data and save to disk.

i've seen a few articles on how to use microsoft's posting acceptor to do file uploads, but this requires having the user fill in fields on a page which i can't ask the sender to do.

anybody have any idea how to pull this off? i thought i knew IIS pretty well, but this one has me totally stumped.

Answer:

I can think of a few ways to do what you are asking on IIS, and each has its benefits and drawbacks. In no particular order:

  • Use IIsWebFile to configure a specific URL's Default Document to be the POST acceptor.
  • Use ISAPI Filter to rewrite specific incoming URL without page name to URL with the POST acceptor as the page name
  • Enable and use WebDAV PUT

Whichever is "better" depends on which benefit(s)/drawback(s) you require. The benefits and drawbacks of each approach are as follows, in no particular order.

DefaultDocument/IIsWebFile

This solution is simply IIS configuration of the POST acceptor code. It requires IIS6 on Windows Server 2003, and the POST request URL must end with a backslash to invoke the Default Document without needing 302 courtesy redirection for POST support from the client (i.e. client sends POST to http://doc_uploads.partnetsite.com/).

ISAPI Filter

The ISAPI Filter solution requires writing and maintaining C code for the ISAPI Filter, code for the POST acceptor, and IIS configuration for both the ISAPI Filter and POST acceptor code. It works on any IIS version and there are no requirements on the format of the POST request URL.

WebDAV

WebDAV is purely IIS configuration - no POST acceptor nor C code required. It works on all IIS versions that support WebDAV (pretty much all currently supported IIS versions except the latest IIS7 release, but that should be fixed soon), and the request must look like PUT http://doc_uploads.partnetsite.com/myFile.txt and myFile.txt will be available for retrieval by anyone via GET http://doc_uploads.partnetsite.com/myFile.txt UNLESS you control all applications on IIS from inadvertently serving that resource via some mechanism (NTFS ACLs, IIS Access Permissions, etc).

i.e. once you PUT a file to the web server, it can be retrieved via GET. This is different from a POST acceptor which can be configured to store files outside the URL namespace to avoid inadvertent web-based access of those uploaded resources.

Conclusion

I have only provided the categories of solutions possible - the specific solution for your situation requires more details about your requirements. You do not need to worry about user interactivity, FORM fields, grabbing data, Posting Acceptor, etc - all those tasks can be automated. There are many ways to "post myFile.txt to http://doc_uploads.partnersite.com", and you have only scratched the surface of your requirements...

//David

Friday, January 12, 2007

Why IIS can lose configuration changes on server reboot

Question:

We are running IIS 6 on Windows 2003. Our public website is configured to to allow anonymous access using the default IUSR_ account.

Everything works just like it is supposed to until the server is rebooted. After a reboot, our public website challenges users to provide credentials. To fix this, we go into IIS Directory Security and re-enter the anonymous access account password to the same password that is in Active Directory. Then, everything works again.

What is going wrong that causes IIS to lose the anonymous password when the server reboots? Is there any way to fix this problem so that IIS will remember the password?

Answer:

By default, IIS remembers configuration changes, such as altering the anonymous user password, unless you terminate IIS before it persists that change to disk. IIS6 runtime configuration is hosted by the IISADMIN service inside the inetinfo.exe process.

So, the real question is whether something:

  1. Killed IISADMIN service on the reboot, before it persisted the change to disk
  2. Or changed the anonymous user password to an invalid value on the server restart.

To verify what is going awry:

  1. Enter the password such that anonymous access works on IIS
  2. Open the IIS Manager UI, Right click on the Computer Name, select "All Tasks", and choose "Save Configuration to Disk". This forces IIS to persist the password to disk.
  3. Go ahead and reboot the server as you normally do

If anonymous access works after the reboot, then your problem was that the reboot was killing IIS prior to it persisting the encryptped password to disk. You intentionally persisted the change to disk from within the UI, thus breaking the cycle.

If anonymous access still fails, then your problem is that something outside of IIS runs during the reboot/restart process with administrative privileges and changes AT LEAST the anonymous user password in IIS to an incorrect value. You will have to figure out the identity of that arbitrary something and correct it - it is running with Administrative privileges and may be doing other inappropriate things.

//David

Wednesday, December 27, 2006

QA: IIS and Environment Variable Updates

Question:

Hi,

I want to store uploaded file in a directory C:\Inetpub\Users\Upload. c:\Inetpub is the home directory I have set, so it is pretty much HOME\Users\Upload.

However, when I use following commands, the file is stored in c:\Users\Uoload.

$upload_dir = "$HOME/Users/Upload";
$upload_filename = "$upload_dir/$filename";

if (! open UPLOADFILE, ">$upload_filename")
{
print "error-$!
\n";
}

binmode UPLOADFILE;

while ( <$upload_filehandle> )
{
print UPLOADFILE;
}

close UPLOADFILE;

My question is what am I doing wrong, and is there a predefined variable for home directory ?

Answer:

Sounds like the "HOME" environment variable is not set.

If you just added the environment variable, you need to reboot Windows for it to take effect for an NT service like IIS. This is because NT services like IIS inherit their environment from services.exe, which does not get updated when you change system environment until you reboot.

If you did not just add the HOME environment variable -- then the problem is that the HOME variable does not exist on Windows. The Windows environment variables most similar in function to the *nix HOME variable are HOMEDRIVE and HOMEPATH.

There is a generic way to "update" the environment variables of an NT service WITHOUT rebooting Windows, but it is not guaranteed to work for all services. You can edit:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\<ServiceShortName>\Parameters\Environment

and add a String Registry value with a name/value same as the environment variable name/value, i.e. with the command:

REG.EXE add "HKLM\SYSTEM\CurrentControlSet\Services\IISADMIN\Parameters\Environment" /v HOME /t REG_SZ /d "C:\Inetpub" /f

//David

Tuesday, December 26, 2006

QA - Setting up IIS Virtual Host

Question:

I am a web developer and I would like to setup IIS to do this:
1. My physical structure:
d:/www/company1
d:/www/company2
...
d:/www/companyN
Each folder is a website for that company
2. On my local system, I would like to access to a company's website as:
http://company1/index.php
http://company2/index.php

Before using IIS, I was using Apache and we can setup httpd.conf as:

DocumentRoot "d:/www/company1"
ServerName company1


DocumentRoot "d:/www/company2"
ServerName company2


and define domain company1, company2 in
c:/windows/system32/drivers/etc/hosts as:
127.0.0.1 company1
127.0.0.1 company2

How do I setup this feature in IIS. Thanks very much for your help.

Answer:

In general, one can set up similar web server configuration on IIS and Apache, given comparable customization module(s) and configuration. In this case, the desired configuration can be performed in a couple of ways using the scriptable administration interface of IIS.

With IIS6, there is a commandline tool, iisweb.vbs, which uses the scriptable administration interface and illustrates how to perform and automate the task:

iisweb.vbs /create D:\www\company1 Company1 /d:company1

The same configuration can be set more manually on prior IIS versions with a more generic tool, adsutil.vbs, located in %systemdrive%\inetpub\adminscripts. The following command sequence works on all IIS versions and is equivalent to what iisweb.vbs performs:

adsutil.vbs CREATE W3SVC/1000 IIsWebServer
adsutil.vbs SET W3SVC/1000/ServerBindings "127.0.0.1:80:company1"
adsutil.vbs CREATE W3SVC/1000/ROOT IIsWebVirtualDir
adsutil.vbs SET W3SVC/1000/ROOT/Path "D:\www\company1"

Note it is important to use a unique ID # for each website (i.e. I chose 1000) and the proper KeyType (case sensitive) for each configuration node needs to be provided on the CREATE - and iisweb.vbs takes care of that amongst other features and details.

You can use this script to check your website configuration.

//David

Thursday, December 21, 2006

QA - COM Initialization and IIS

Question:

Sometimes people (my customers) have a problem with COM Initialization in an ISAPI application. It seems sometimes they do not have to call CoInitializeEx and sometimes they do. Since CoInitializeEx "must" be called prior to using COM and sometimes the isapi works without calling this function, it seems IIS or something else has already called this function.

Can someone tell me, what is IIS's behavior in regard to COM initialization?

Answer:

IIS's behavior in regards to COM initialization is clean - it simply does nothing.

Thus, the behavior you describe sounds like a common bug with some ISAPI DLL running on IIS - calling CoInitializeEx() on threads not owned by the ISAPI. Since IIS reuses threads from its thread pool to call into ISAPI, the following sequence can occur and cause what you observe:

  1. IIS does not call CoInitializeEx() on any of the threads in its thread pool.
  2. IIS uses thread 1 from its thread pool to call the ISAPI entrypoint.
  3. In the entrypoint, ISAPI calls CoInitializeEx() on thread 1 but does not call CoUninitialize() prior to exiting the entrypoint and returning control to IIS.

    thread 1 has now called CoInitializeEx() once.

  4. IIS later uses thread 2 from its thread pool to call the ISAPI entrypoint. Since this thread has never called CoInitializeEx(), thread 2 has called CoInitializeEx() once.
  5. IIS later re-uses thread 1 from its thread pool to call the ISAPI entrypoint.

    thread 1 has now called CoInitializeEx() twice.

At this point, this ISAPI has seriously confused things. There is no way for it to call the correct number of CoUninitialize() on all the IIS threads (suppose the ISAPI entrypoint is never invoked again by IIS - no chance for it to call CoUninitialize() correct number of times, and no way for new user of the same thread to know how many times to call CoUninitialize()). It has tainted the thread pool shared by all other ISAPIs. All sorts of badness can commence at this point.

The simple rule of thumb: do not call CoInitialize()/CoUninitialize() on a thread which you do not own.

//David

Wednesday, December 20, 2006

QA - IIS6 Debugging with NTSD, Setup

Question:

Using some very helpful guidence from this forum, I made my first attempt at trying to catch a problem I see perioidcally in my ISAPI module.

I installed the latest NTSD.EXE and supporting DLL's on the server of interest and loaded them by adding the following registry entry and restarting IIS:

REG ADD "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\w3wp.exe" /v Debugger /d "C:\DEBUG\NTSD.EXE -g -G" /t REG_SZ /f

Note I wasn't attempting at this point to monitor remotely as I have RDO access to this server.

NTSD loaded as expected as a process along with W3WP.EXE (as shown in Task Manager). My ISAPI app was exposed to the load overnight and when the morning heavy loads hit, the event log posted the two events shown below. At that point in time, IIS stopped processing requests (although it still seemed to be running). So we removed this server from the WLBS array and began to look for some signs of the debugger's dump.

The problem is I can't find any results from the debugging process. Now this might have been as simple as not having a client running on that server which is monitoring the debug process. But I figured NTSD would throw up some sort of message box indicating a dump was occuring and where. But we didn't see anything like that.

I'm likely missing something obvious in this overall process -- can anyone see what I'm doing wrong?

Event Type: Warning
Event Source: W3SVC
Event Category: None
Event ID: 1010
Date: 12/15/2006
Time: 3:01:16 PM
User: N/A
Computer: DAZLOADBAL3
Description:
A process serving application pool 'DefaultAppPool' failed to respond to a ping.
The process id was '3800'.

Event Type: Information
Event Source: W3SVC
Event Category: None
Event ID: 1082
Date: 12/15/2006
Time: 3:01:16 PM
User: N/A
Computer: DAZLOADBAL3
Description:
A worker process with pid '3800' that serves application pool
'DefaultAppPool' has been determined to be unhealthy (see previous event log
message), but because a debugger is attached to it, the World Wide Web
Publishing Service will ignore the error.

Answer:

Ah, this attempt is correct except for one tiny detail - how to manipulate the debugger when it is auto-attached to an NT Service via Image File Execution Options. Unfortunately, the current situation is unrecoverable, so you will have to start over and account for the missing but critical detail.

Debuggers like CDB, NTSD, and WINDBG from the Microsoft Debugging Toolkit are general purpose debuggers which expect interactive command input to perform tasks like taking a crash dump, disassemble instructions, examine memory, etc. On the other hand, JIT Debuggers like OCA and Dr. Watson are specialized debuggers which automatically perform certain pre-programmed tasks upon triggering.

Common Ways to Manipulate a Debugger

Basically, the question is "now that I have a debugger attached to the process of interest, how do I manipulate the debugger to do what I want?"

The following are some common ways to manipulate a NTSD debugger:

  • Make the debugger command window show up on a WinStation which you can access by launching the debugger interactively as the logged-on user
  • Make the debugger command window show up on a WinStation which you can access by making the NT Service interactive with the Console desktop (WinStation#0)
  • Make the debugger into a "conduit" for an eventual debugging client by piping usermode output into a kernel mode debugger with -d
  • Make the debugger into a "conduit" for an eventual debugging client by opening a TCP/IP port or NamedPipe with -server

The Astute reader should note that there are other debugging methods, such as JIT Debugger, Kernel Debugger, etc... but they are not really relevant nor useful here, so I will skip them for the sake of logical clarity.

Yes, it may seem like a large number of choices for something as simple as "how do I manipulate the debugger", but rest assured, they exist because at one point or another some Microsoft product team needed the feature to debug some aspect of Windows. One may never need to use all of the options, but the utility of having the right option for the right situation means everything in a debugger. Remember, this is the same Debugging Toolkit used within Microsoft to debug native code, so it is plenty powerful when properly wielded.

The Issue, Reformulated

Now that I have enumerated some options, the issue should hopefully make more sense.

  • The NTSD debugger is configured to auto-attach via Image File Execution Options to the W3WP.EXE process launched by an NT Service, which does not interact with the Console desktop by default.
  • An unhandled exception occurred in the W3WP.EXE process, is caught by the attached NTSD debugger (also non-interactive with the Console desktop), and this halts all code execution within the W3WP.EXE process.
  • The NTSD debugger is awaiting commands following the caught exception, but you cannot input them into any debugger commandline window since it is not interacting with the Console Desktop, nor are there any queued commands to the debugger.
  • And since a Windows Process only has one Debugger port, you cannot attach a second debugger via any other method to regain control of the debugger/process...
  • Thus, the current debugging session is inaccessible and dead.
  • To add insult to injury - when W3SVC wants to recycle and/or terminate a monitored W3WP, and it detects that a debugger is already attached onto that W3WP, it will simply skip over taking action against it (i.e. the second event log entry mentioned above). So, not only is the W3WP.EXE halted from executing code and is inaccessible for debugging, IIS also skips cleaning it up.

    This is ok, though, because the feature was added during IIS6 development as a fail-safe against losing W3WP.EXE for investigations. Yes, the behavior looks silly when misconfigured, but the benefits outweigh the occassional mishap.

Corrective Actions

How to address this issue? Well, one can reconfigure the system to support debugging in any of the above ways that I specied earlier. This is how to do each:

  • Make the debugger command window show up on a WinStation which you can access by launching the debugger interactively as the logged-on user

    With the target W3WP.EXE already running, run: C:\DEBUG\NTSD -g -G -p {PID of W3WP.EXE}   If there is only one W3WP.EXE, you can use -pn w3wp.exe to select the unambiguous process name "w3wp.exe" to attach to.

  • Make the debugger command window show up on a WinStation which you can access by making the NT Service interactive with the Console desktop (WinStation#0)

    REG ADD "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\w3wp.exe" /v Debugger /d "C:\DEBUG\NTSD.EXE -g -G" /t REG_SZ /f
    SC CONFIG IISADMIN type= share type= interact
    SC CONFIG W3SVC type= own type= interact
    NET STOP /y IISADMIN
    NET START W3SVC

    Be careful with the SC commands - the exact parameters and whitespacing are (unfortunately) important. In particular, neither type=interact, nor type =interact, nor just type= interact work.

    The NTSD window now automatically shows up in WinStation#0 (the local console) for each new W3WP.EXE.

  • Make the debugger into a "conduit" for an eventual debugging client by piping usermode output into a kernel mode debugger with -d

    REG ADD "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\w3wp.exe" /v Debugger /d "C:\DEBUG\NTSD.EXE -g -G -d" /t REG_SZ /f

  • Make the debugger into a "conduit" for an eventual debugging client by opening a TCP/IP port or NamedPipe with -server

    • REG ADD "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\w3wp.exe" /v Debugger /d "C:\DEBUG\NTSD.EXE -server tcp:port=%d -g -G" /t REG_SZ /f
    • REG ADD "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\w3wp.exe" /v Debugger /d "C:\DEBUG\NTSD.EXE -server npipe:pipe=w3wp%d -g -G" /t REG_SZ /f

Conclusion

Which one is "best"? They are all "best" for certain situations and painfully inadequate for the wrong situations... so "best" is really subjective to the debugging task at hand. I recommend evaluating the needs of the debugging situation and then selecting the proper debugging approach that you are comfortable with. While the above list is not conclusive, it should suffice for most debugging situations.

Personally, I favor the -server TCP/IP accessed via a non-console WinStation on the server because it alters no service/server configuration. Yes, the commandline syntax can be complicated, but that's what batch scripting is for. :-)

//David

Thursday, December 07, 2006

QA - On Process, Threading, and DLL Lifetimes

Question:

In a thread "Critical Section shared between ISAPI", David Wang promised a "blog entry about how ISAPI/CGI/ASP.Net binaries load in various IIS versions and configurations".

A serious background about process/thread issues in ISAPI and extension DLL loading/unloading sequence would be very much welcome, because 1) the topic was never covered thoroughly in official docs, 2) things changed in IIS6.

Here are few questions that come to my mind.

* Knowing if my extension will be loaded into one or more process spaces is crucial, because DLL's data will either be shared by instances, or exist as separate copies and this, of course, leads to different synchronization strategy. According to David, I can enforce IIS to keep my extension within one w3wp.exe process, provided I do not use Web Garden. Is this 100% guaranteed? I suspect it is not true on error or overload circumstances.

Let's assume 1st instance of worker process is not responding to ping. Will IIS try politely to terminate 1st instance BEFORE launching the 2nd one? From ISS point of view, 1st instance is inactive and useless, but actually it may still work and use shared resource.

* Is there any occasion for IIS to SUSPEND an extension's thread? (I mean - a thread of one instance, without suspending a thread of another instance). I hope not, because this would play havoc with any concurrency efforts!

* As Ian suggested in the same thread, mutex may solve the problem in case we are afraid of cross-process interaction. Fine, but sometimes we must have a singleton for other reasons than synchronization. My extension, for example, may load about 100-300MB in response to single request. I do not want this stuff to be loaded into memory twice. I need a single process instance - can I be sure?

* IIS5 docs claim that GetExtensionVersion and TerminateExtension functions get called once per DLL's lifetime in IIS space. Was it true? Is this still true in IIS6?

* Is it reasonable and save to spawn a new thread from within an extension? Is it save to leave "something" running between subsequent HTTPExtensionProc calls? (what I did is monitoring/logging thread that performs every 5 seconds or so) How about a timer?

* What is the relation of IIS6 "Application Pool" to Windows API "Thread Pool"?

Answer:

Excellent questions... Sorry I have not yet gotten around to writing that blog entry yet... I have many, many others yet to write. They are all in various stages of construction. Maybe during the upcoming holiday downtime I will find some time to wrap some of them up. But in the meantime, let me attend to your direct questions...

According to David, I can enforce IIS to keep my extension within one w3wp.exe process, provided I do not use Web Garden. Is this 100% guaranteed?

That conclusion is not 100% correct because the devil's in the details. I did not mention the details earlier because the simpler explanation of Web Garden suffices for most people. And... people's eyes usually glaze over when I start on the details, so I try to withhold them unless someone asks for them. ;-)

Now, IIS does NOT have a feature to enforce a "singleton w3wp.exe process loads a given ISAPI Extension DLL" no matter what. However, IIS does have features which when properly configured CAN result in a "singleton w3wp.exe process loading a given ISAPI Extension DLL".

Thus, if you can control IIS Application Pool and ISAPI Execution configuration for the entire server, you can create an environment of a "singleton w3wp.exe process loading a given ISAPI Extension DLL". If you cannot control those configurations for the entire IIS server, then you MUST rely on other mechanisms to ensure your desired behavior.

The following configuration options must be controlled on the entire server to create your desired behavior:

  • DisallowOverlappingRotation - Ensure the replacement w3wp.exe process comes up AFTER the old w3wp.exe terminates.
  • MaxProcesses - Maximum number of w3wp.exe to concurrently service a given Application Pool. Web Garden has a value greater than 1.
  • AppPoolId - The Application Pool which services a given URL namespace. Since Application Pools partition the URL namespace and ISAPI Extensions load and execute inside some partition of the URL namespace serviced by IIS, controlling the Application Pool directly affects the number of times an ISAPI Extension DLL image loads.
  • [Optional]ScriptMaps - Application Mappings determine which ISAPI Extension DLL or CGI EXE execute for any resource types. This configuration is at a per-URL level.
  • The URL itself to invoke the ISAPI Extension DLL.

What you must ensure is that the URL used to invoke your ISAPI Extension DLL, and [optionally] any ScriptMap settings that map that ISAPI Extension DLL, all execute within one specific AppPoolId whose MaxProcesses value is 1 and DisallowOverlappingRotation is TRUE.

Now, I think I shall pause for a moment for the full meaning of the prior sentence to sink in... :-) Yes, there are many possible solution configurations, but they all fit the above criteria.

Of course, if you CANNOT control those IIS configuration values for the entire server, then you CANNOT ensure "singleton w3wp.exe process loads a given ISAPI Extension DLL" and must use other mechanisms. The only alternative I can think of is to load your code into a separate Singleton process on the server and inter-process communicate between it and w3wp.exe.

Will IIS try politely to terminate 1st instance BEFORE launching the 2nd one? From ISS point of view, 1st instance is inactive and useless, but actually it may still work and use shared resource.

IIS behavior depends on the configuration of DisallowOverlappingRotation. If FALSE (default), then IIS will launch the 2nd one before terminating the 1st one. If TRUE, then IIS will launch the 2nd one after the 1st one terminates and stops using shared resources.

* Is there any occasion for IIS to SUSPEND an extension's thread?

No, IIS does not suspend an ISAPI Extension's threads during execution.

* IIS5 docs claim that GetExtensionVersion and TerminateExtension functions get called once per DLL's lifetime in IIS space. Was it true? Is this still true in IIS6?

Yes and Yes. IIS always calls GetExtensionVersion() right after it loads the ISAPI DLL into a given process's memory address space and TerminateExtension() before unloading the ISAPI DLL from memory. In addition, various IIS features affect when an ISAPI DLL is loaded and unloaded.

Unfortunately, most people do not understand when an ISAPI DLL loads/unloads and incorrectly assume IIS guarantees random behavior/properties such as the number of times or timing of when those entry points are invoked... thus leading to confusion between IIS versions. In reality, you just need to understand when and what causes IIS to load/unload an ISAPI DLL.

IIS loads ISAPI Extension DLLs on demand to handle any given request. IIS also caches (i.e. leaves the DLL loaded in memory) the ISAPI Extension DLLs between requests to improve performance. This blog entry details how IIS selects the handler for a given request, so you know how to trigger your ISAPI Extension DLL. And the "Cache ISAPI application" checkbox in the "App Mappings" tab controls whether IIS loads/unloads the ISAPI Extension DLL between every single request, or just load on the first request and unload when the process terminates.

* Is it reasonable and save to spawn a new thread from within an extension? Is it save to leave "something" running between subsequent HTTPExtensionProc calls? (what I did is monitoring/logging thread that performs every 5 seconds or so) How about a timer?

Whether the action is "Reasonable" depends more on the function and design of the ISAPI Extension DLL. IIS places no requirements one way or another.

As for safety - it is safe to spawn your own threads from within an ISAPI Extension - for example, ISAPI Extension using a Thread Pool to queue/process requests asynchronously does this, by design. What you need to ensure is that if those threads touch the ECB (or any other memory, for that matter), that the ECB is valid. For synchronous request, the ECB will not be valid in between HttpExtensionProc calls (its lifetime is tied to the HttpExtensionProc lifetime for that particular request). For asynchronous requests, ECB will stay valid until HSE_REQ_DONE_WITH_SESSION is called on that ECB.

* What is the relation of IIS6 "Application Pool" to Windows API "Thread Pool"?

No direct relation. IIS6 "Application Pool" merely groups process-oriented configuration to partitions of URL namespace. "Thread Pool" is a method of scheduling code execution amongst threads vying for CPU time-slices

//David

Thursday, November 30, 2006

QA - Maximizing IIS6 Concurrent Connections

Question:

Dear All,

We are using Win 2003 sp1 with IIS 6.0 installed Default website properties connection set to 30 sec Running IIS with ASP Files, all working good

The problem is: when we have 10,000 con current session on the server, the IIS Stop Responding on port 80 and the clients get error Connection TimeOut ,

Is there any IIS or Port 80 Limits

Any Advice will be appreciated ?

p.s

the server CPU and memory are very Low

Thanks

Answer:

The connection limit you observe comes from HTTP.SYS because each HTTP connection takes a small chunk of kernel NonPagedPool memory.

On 32bit Windows, this memory is FAR smaller than the amount of physical memory available, around 250MB, and it will usually run out long before available physical memory and cause symptoms that look like this... especially if you have long-lived concurrent connections.

Thus, to maximize the number of concurrent users to IIS6, you want to do all of the following:

  • Make the connections timeout faster (ConnectionTimeout metabase property).
  • Make the connections non-keepalive (AllowKeepAlive metabase property).
  • Remove any of the following artificial limits.

    But, remember that HTTP.SYS is still constrained by available NonPagedPool memory, so this change only helps if you had an artificially lower limit to begin with (i.e. on 32bit Windows, if MaxConnections was set to 5,000 for whatever reason, you can remove the artificially lower limit by changing it to 100,000, but you will never get to 100K).

  • Move to a Windows OS with a Memory Manager that utilizes the Dynamic Memory model, such as Windows Server 2003 SP1 64bit or Windows Vista.

    This memory model removes all the hard-coded and soft-coded limits on various memory pools of Windows, such as NonPagedPool, and allows them to dynamically grow and shrink according to utilization and up to all available physical memory. This means you can add more concurrent connections by simply adding more RAM.

All of the above are EXACTLY what microsoft.com and msn.com does across all their servers that serve low-latency, high-volume traffic - such as their image servers that house static content shared across the entire web farm. At a massive scale, the speed gained from KeepAlive is not worth the concurrent users capacity lost by consuming kernel NonPagedPool memory, and they remove both artificial and soft-coded limits of NonPagedPool memory by using Windows Server 2003 SP1 64bit.

Good luck,

//David

Monday, November 20, 2006

QA - On Application Pool Time Limits

My ISAPI errors seem to be settling down a bit and I'm left with a periodic message like the one below. First, by "recycle" does this mean that IIS restarts automatically? It seems like that's what's happening.

Secondly, how and where does one set the processing time limit? Is this a relatively short time like seconds or hours?

Thirdly, what causes a worker process to exceed it's time limit.

And finally, how can I associate the process ID with a specific ISAPI module?

Lots of questions!! Sorry!

Event Type: Information
Event Source: W3SVC
Event Category: None
Event ID: 1074
Date: 11/20/2006
Time: 3:46:54 PM
User: N/A
Computer: DAZLOADBAL3
Description:
A worker process with process id of '2260' serving application pool
'DefaultAppPool' has requested a recycle because the worker process reached
its allowed processing time limit.

For more information, see Help and Support Center at
http://go.microsoft.com/fwlink/events.asp.

Answer:


  1. It depends on what you mean by "IIS". The event says that a worker process of a particular application pool is recycling. To me, IIS is not "any particular application pool or worker process" but rather "the service that maintains the activity and availability of all application pools (and subordinate worker processes)". Thus to me, this "recycle" does not mean that IIS is automatically restarting.
  2. Process recycling configuration is associated with an Application Pool - in this case, DefaultAppPool. "Processing Time Limit" is associated with the "Recycle worker processes (in minutes):" configuration option of the Application Pool. Its default value is 1740 (29 hours) and can be customized.
  3. You can view process recycling as either caused by unexpected error or by expected benign condition. By default, IIS only reports unexpected error-caused recycling to the event log. However, you are seeing an event for an usually benign recycling metric -- Processing Time Limit -- which basically happens X minutes after a request triggers the Application Pool to start, regardless if the worker process is healthy or not. These benign recycling events are normally not logged, so your server has non-standard configuration of the LogEventOnRecycle property.

    In other words, suppose it is set to 5 minutes. It means that 5 minutes AFTER you have made a request to that application pool to spin up a w3wp.exe, the Process Time Limit will be reached and the w3wp.exe recycled. You should note the benign nature of this recycle metric - nothing is wrong; the Application Pool is just proactively recycling on its own. This is why this recycle metric does not log an event in default configuration.

    Plus, if you make no subsequent requests to the application pool after the recycling, this recycling metric will not be in effect because no w3wp.exe of the application pool is running.

  4. Use the following command to determine which PID has the ISAPI DLL loaded: tasklist /m <ISAPI_Module_Name.dll>

    Use the following command to determine which Application Pool the PID belongs to: iisapp.vbs /p <PID>

//David

QA - URL Authentication... or is it Authorization?

Authentication, Authorization... what's the difference? Actually, a whole lot, as you can read in the following...

Question:

I need to authenticate users agains an Active Directory (or rather ADAM)...

I've red about a new feature in IIS 6.0: URL Authentication and I did manage to set-up a situation where users are authenticated by use of LDAP query: (&(objectCategory=user)(CN=*)))

But, now comes the stange part, only users logged-in on the server where IIS is configured are authenticated correctly. For example:

  • IIS/URL Authentication is configured at server Server1 to protect virtual directory /URLTest.
  • When user 'admin' is logged in Server1, he is able to go to http://localhost/URLTest
  • When user 'test' is logged in Server1, he is also able to go to the url above,
  • When users 'admin' or 'test' are logged-in on another server, they are not able to navigate to Server1/URLTest because they cannot be authenticated...

What is wrong?

Answer:

The problem is that "URL Authentication" does not exist.

The feature is actually called "URL Authorization". Authorization (i.e. what can a user do?) is totally different than Authentication (i.e. what user are you?)

"URL Authorization" takes effect AFTER Authentication completes, since you need to know WHO the user is before trying to determine WHAT the user is authorized to do.

Since you say you cannot authenticate to this server when logged into a remote machine, what you configured for "URL Authorization" is not involved at all.

Your problem has to do with why those users cannot authenticate from a remote machine. The best way is to look at the IIS web log entries for these remote access attempts to see what is wrong. I suggest reading this blog entry.

//David

Wednesday, November 08, 2006

HOWTO - Update Applications under Debug on IIS 6

Question:

Hi,

we are using a IIS 6.0 server as remote webserver for developing vstudio .NET applications. When debugging we always have to restart IIS (or recycle application pool), in order to overwrite the new compiled DLLs.

The DLL is locked by the w3wp.dll process and the dll therefore cannot be overwritten.

Is there any way to tell IIS (w3wp.dll) not the cache the dll or better free the dll directly after execution?

Answer:

Standard debugging procedures differ depending on the type of application.

ISAPI Extension DLL

You can uncheck the "Cache application files" option under the "App Mappings" tab of the application to force IIS to load/unload the ISAPI Extension DLL for every single request to that resource extension's handler. Be warned that this option is debug-only (do NOT uncheck this on production servers) and often exposes startup/shutdown bugs in your ISAPI Extension DLL.

ISAPI Filter DLL

You should simply recycle the Application Pool(s) which have loaded the Filter DLL. If it is a Global Filter, then you must recycle all Application Pools. If it is a Site Filter, then you must recycle all Application Pools used by applications of the Website which loads that Site Filter.

ASP.Net httpModule and httpHandler

You can dynamically change either the DLL in /bin or source code in global.asax or /App_Code, depending on how you configured the httpModule or httpHandler. No restart/recycle required.

//David

Friday, November 03, 2006

QA - Obscure Behavior simultaneously across Multiple Machines?

Question:

On our IIS 5 (w2k server etc) using perl 5, our bulletin board has suddenly stopped adding new files. After some testing, its fine updating existing ones, but seems unable to add new - writing them via code. Since I havne't changed anything in the code, is it possibly some obscure piece of system-wide policy has caused this?

The exact same symptoms are happening on another server, and the problem is not restricted to one folder either.

Answer:

Come now... baffling events can be viewed as logical events whose logic is not immediately clear to the observing party. It does not mean toss logic aside in favor of random fantasies. ;-) I mean, computers do not yet dream/fantasize... they are stubbornly logical and require strict instructions in the form of binary code.

Put another way (proof through contradiction) - suppose the issue is some obscure piece of system-wide policy - how likely is the same SYSTEM-WIDE policy simultaneously applied on two DIFFERENT servers and multiple folders?

Since you observe the same behavior from multiple servers, and it is highly unlikely for two random system-wide configuration changes to generate matching behaviors, I do not believe in merely blaming "obscurities in Windows". Instead, I would look for what resources are COMMON to both servers and look for limits in them - such as:

  • The Bulletin Board software itself
  • Commonly shared UNC fileshares used by the BBS (Are you sure "adding new files to the BBS" actually creates files in the FileSystem? Having URLs do not mean there are real files because URLs are merely resource descriptors and not strictly mapped to filenames on a FileSystem)
  • Commonly shared SQL database between the servers
  • etc.

I mean, here are some suggested thoughts:

  • Maybe the Bulletin Board software has a built-in limit on the number of posts for a given forum, so as soon as you reached it from one server, the other server will also fail for that forum
  • Or the NTFS Disk Quota on a commonly shared UNC fileshare for a commonly shared access-user is reached by one server and seen from the other
  • Or the number of rows in your SQL database table is capped and you cannot add any more files/posts

In general, I do not believe in "obscure behavior" on a Computer because it usually means I have not tried hard enough to think outside the box, to gather information and knowledge, to make sense of the situation. I mean, it is tempting to hypothesize from one's knowledge of the system even if it is better to logically deduce from facts.

//David

Thursday, November 02, 2006

HOWTO: Change the ID of an IIS Website

Question:

Hello,

When I install Sharepoint Services, it creates a web site with an identifier. (Ex. ID 4).

I need to change that ID for another one. So I use this command :

cscript adsutil.vbs move w3svc/4 w3svc/4243.

It does the trick...

But, when I look in IIS I see that the site is stopped and I have this error in the event logs:

Event Type:           Error 
Event Source: W3SVC
Event Category: None
Event ID: 1007
Date: 2006-10-25
Time: 13:19:46
User: N/A
Computer: U1U206V
Description:

Cannot register the URL prefix 'http://*:24051/' for site '42'. The
necessary network binding may already be in use. The site has been
deactivated. The data field contains the error number.

For more information, see Help and Support Center at
http://go.microsoft.com/fwlink/events.asp.

Data:
0000: b7 00 07 80 ·..€

If I start the site manually, the site starts fine with no errors.

I just want to know if there's a way to not receive this error message when I change the ID of my site.

Can someone confirm that?

Or is there another way to do it to prevent errors?

Thx in advance for your help.

Answer:

The reason you get that error event when you MOVE a running website is due to how ADSUTIL.VBS MOVE works. You can treat the MOVE operation as a non-atomic COPY-then-DELETE... because it is designed to move arbitrary metabase nodes (for example, one vdir to another vdir in a different tree hierarchy).

Thus, when you use it to move from website ID 4 to website ID 42, what happens is this:

  1. The entire metabase node under website ID 4 is copied to website node with ID 42
  2. Since website ID 4 is running and website node with ID 42 is a website, website ID 42 also attempts to run
  3. However, since both website nodes are duplicates and have the exact same bindings, the new website ID 42 cannot start due to duplicate bindings and trigger the error event
  4. After website ID 42 finishes copying, node ID 4 is deleted
  5. But website ID 42 has already failed to start; there is no state change here

The key is to STOP the source website ID prior to executing the MOVE. In that case, there will be no duplicate bindings simultaneously running during the COPY-the-DELETE, and one will not see the error event.


CSCRIPT %SYSTEMDRIVE%\Inetpub\AdminScripts\adsutil.vbs STOP_SERVER W3SVC/4
CSCRIPT %SYSTEMDRIVE%\Inetpub\AdminScripts\adsutil.vbs MOVE W3SVC/4 W3SVC/42
CSCRIPT %SYSTEMDRIVE%\Inetpub\AdminScripts\adsutil.vbs START_SERVER W3SVC/42

//David