Welcome to MSDN Blogs Sign in | Join | Help

Feature additions and bug fixes coming up in WinCache

We are seeing huge momentum behind adoption of WinCache. In the month of September, WinCache v1 Beta was downloaded more than 13,000 time making it one of the most downloaded IIS extensions in the first month following its release. Many happy customers chose to run it on their production servers despite its beta tag. We got very encouraging feedback from the beta release and we are pushing hard for our next release. Our next release date is approaching fast and I wanted to give an update on new features and bug fixes. If your favorite feature was missing in the beta and is also not in the list below, please email me or post on the forums so that we can consider it for future releases of WinCache.

Bug Fixes
  1. We fixed a bug due to which same file was getting included twice even when include_once/require_once was used. Some PHP applications didn’t work due to this bug.
  2. Few customers reported crashes when running WinCache on their test servers. This was because file cache component didn’t cache zero-size files properly.
  3. One customer didn’t see enough performance improvement when using cakephp. This was because of a bug in opcode cache which caused us to recompile the code every time.
  4. One bug caused opcode cache’s total hit count to be negative after first request. Also hit count of different file entries in file cache was one more than expected. Both of these bugs are fixed.
  5. WinCache caches compile time errors and warnings generated by PHP engine. We fixed a bug due to which warning/errors caching sometimes caused next request to return 500.

New features

  1. We are adding an API “bool wincache_refresh_if_changed()” to force a file change check on a file next time it is used from cache. This is useful if you change a file in PHP code and you want next request to load the file from disk again. You can pass blank or null which tells API to refresh all the files in the cache. An array of absolute/relative paths as argument will make WinCache only refresh these file entries. Please note that file will be reloaded from disk only if a file change is detected. This API enforces a file change check and not necessarily removal of cached entry.
  2. In the next release, users will be able to disable file change checks completely by setting value of wincache.chkinterval to 0. When file change checks are disabled, changed file will be reloaded only when wincache_refresh_if_changed is called or IIS application pool is recycled. Cached files will still get removed by scavenger when the file is not used for a long time causing wincache to load the file again.
  3. Another feature getting added is to enable administrators choose a list of websites for which wincache.ocenabled property is opposite of global setting affecting all the websites. This was requested by a hoster who wanted a safe and quick mechanism to turn-off WinCache for websites which run into some problems.
  4. By default, caching in WinCache will be disabled when PHP is running in CLI (command line) mode. Setting wincache.enablecli to 1 will turn on caching in CLI mode.
  5. We will be exposing total_cache_uptime property from wincache_fcache_fileinfo() and wincache_ocache_fileinfo() which will give total cache uptime in seconds.
  6. On popular demand, we are including a PHP script (wincache.php) in WinCache setup which users can use to see cache statistics.
If you haven’t had a chance to test your PHP application with WinCache yet, download and try it out today. We are looking forward to your feature requests, bugs and general feedback to guide us for future releases of WinCache.

Thanks,
Kanwal
Posted by kjsingla | 0 Comments
Filed under: , ,

Migrating FastCGI configuration from IIS 5.1/6.0 to IIS 7.0/7.5

Problem
As you know FastCGI functionality on IIS 5.1 and IIS 6.0 is provided by
FastCGI ISAPI extension which is available as an independent download. On IIS 7.0 and beyond, FastCGI functionality is provided by IIS FastCGI module which comes with the operating system. These components use different configuration stores to store the settings that affect their behavior. FastCGI ISAPI uses an INI file named fcgiext.ini as its configuration store whereas FastCGI module keeps configuration in applicationHost.config in xml format with rest of IIS configuration. Before, migrating from IIS6 to IIS7 involved migrating fcgiext.ini configuration to FastCGI module manually as there was no automated way to do that. MSDeploy only had functionality to migrate metabase configuration but because FastCGI ISAPI configuration is kept separately in an INI file, that wasn’t possible.

Solution
MSDeploy v1 RTW which is released today include a new provider named fcgiextConfig to automate task of migrating FastCGI ISAPI INI configuration to FastCGI module section. FcgiextConfig provider accepts a path which can be “APPHOST” or ”INI”. When path “APPHOST” is specified, configuration from system.webServer/fastCgi section in applicationHost.config is read. Path “APPHOST” should only be used on systems running FastCGI module (i.e. IIS7 and beyond). When path “INI” is specified, this provider reads the configuration from file “%windir%\system32\inetsrv\fcgiext.ini” and produce xml which looks like FastCGI module configuration. Once INI settings are mapped to produce the xml, msdeploy engine can compare configuration of FastCGI ISAPI with FastCGI module and make changes to FastCGI module configuration as required. Again, path “INI” can only be used on a machine which is running FastCGI ISAPI.

Examples
Dump command to dump INI settings looks like following.
        msdeploy –verb:dump –source:fcgiExtConfig=ini –xml
        msdeploy –verb:dump –source:fcgiExtConfig=apphost –xml

Sync command to move INI settings to FastCGI module section is following.
        msdeploy –verb:sync –source:fcgiExtConfig=ini –dest:fcgiExtConfig=apphost -whatif


Details
Here is how INI settings are mapped to system.webServer/fastCgi settings. For each entry “<extension>:<optionalsiteid>=<sectionname>” under [Types] section of fcgiext.ini, we read the settings under section [<sectionname>]. If optional site id is present, we look for an entry for the same extension but without site id. If an entry is found, we use configuration under this entry as base set of settings for the site specific entry. So if “php=basephp” and “php:1=site1” entries are present in INI, settings under section [basephp] provide base values and then settings under section [site1] override base settings to generate configuration for “php:1” process pool. If a property is not present in the INI file, we assume the default value as assumed by FastCGI ISAPI extension. A FastCGI process pool entry is created for each INI entry with a valid ExePath. Various INI file settings are mapped to a FastCGI process pool as below.
 
fcgiext.ini configuration setting system.webServer/fastCgi/application property
ExePath fullPath
Arguments arguments
QueueLength queueLength
MaxInstances maxInstances
IdleTimeout idleTimeout
ActivityTimeout activityTimeout
RequestTimeout requestTimeout
InstanceMaxRequests instanceMaxRequests
FlushNamedPipe flushNamedPipe
Protocol protocol
RapidFailsPerMinute rapidFailsPerMinute
EnvironmentVars environmentVariables
ResponseBufferLimit Ignored
IgnoreExistingFiles Ignored
IgnoreDirectories Ignored
UnhealthyOnQueueFull Ignored

If the source version is FastCGI ISAPI 1.5, some additional properties get picked and mapped as below.
 
StderrMode stderrMode
MonitorChangesTo MonitorChangesTo
SignalBeforeTerminateSeconds signalBeforeTerminateSeconds

If FastCGI ISAPI version on source and FastCGI module version on destination are not compatible, fcgiextConfig provider will block the migration. Note that fcgiextConfig provider doesn’t take care of migrating the relevant IIS ScriptMaps. You can migrate ScriptMaps using a separate msdeploy migrate operation.

Hope this helps.
Kanwal
Posted by kjsingla | 0 Comments
Filed under: , ,

How WinCache make PHP run faster

Why WinCache?
Use of
PHP accelerators is very common to make PHP run faster. Most of the existing PHP accelerators which are in use today are designed keeping *nix architecture in mind and mostly doesn’t work well on Windows. For example, APC which is very commonly used on *nix doesn’t work on Windows. Xcache does work in ISAPI mode but crashes when PHP is running in FastCGI mode with multiple php processes alive. Absence of a stable PHP accelerator on Windows made people complain about PHP performance on Windows all the time. Another complaint which we heard consistently from customers running PHP on windows was that file operations on Windows were slower than on *nix. Reason being Windows’s CreateFile system call which is much more expensive than a fopen call on *nix as CreateFile goes through a much more complex security check involving ACLs. Windows programmers typically try to reduce CreateFile calls by caching file contents or keeping the open handles around so that security check happens only once. Because PHP opens the file every time it is required, PHP end up performing slower on Windows than *nix. To solve these problems, IIS team started working on an advanced caching solution few months ago which has resulted in the component known as Windows Cache Extension for PHP (WinCache in short). Below are short descriptions of the caching components which are available in WinCache.

Opcode cache
This is a standard opcode cache similar to other existing opcode caches available for *nix which works by overwriting zend_compile_file function pointer so that all compile file requests go through WinCache. WinCache let PHP core compile the PHP file to generate opcodes first time it is executed. It then caches these opcodes in memory which is shared with other PHP processes. When the same file is executed a second time, WinCache directly points to the cached opcodes in memory and skips compilation altogether. Generating opcodes from PHP code is a very expensive operation and major chunk of request processing time is spent here by PHP core. So having a solution like this saves 50%-70% of request execution time. WinCache creates a separate cache for all child processes under one worker process. So different worker processes keep separate caches and is more secure than current caching solutions.


File content cache
WinCache has a file content cache component which caches content of files in memory to avoid opening files every single time. This component works by replacing zend_stream_open_function function pointer so that file open requests go through WinCache. When a file is opened for the first time, we read the file contents into file cache. When the file is opened again, pointer to file contents in memory is returned instead of opening the file from disk. In our testing we saw that file operations are reduced by approx 80%-90% for each request for most of the popular PHP applications like wordpress, drupal, gallery etc. This is a huge win especially when content is on a UNC share. In our testing we found that PHP’s throughput is significantly impacted when content is moved to UNC share. With WinCache, throughput with content on UNC remains almost same as when content is present on the local machine.


Relative path cache
When we first added the file content cache, we relied on PHP engine to resolve relative path to absolute path as PHP has fairly complex logic to resolve relative paths. Given that most PHP applications use relative paths, this path resolution end up doing a lot of expensive file operations. To reduce these file operations we added a relative path cache component to WinCache which keeps relative path to absolute path mapping in memory. In PHP 5.3, all calls to zend_resolve_path goes through this cache and in PHP 5.2 zend_stream_open_function uses this cache to get the full path. Relative path to absolute path mapping is impacted by lot of parameters like current working directory, current executing file, value of include_path etc. We do take all these parameters into account to ensure that path resolution with WinCache and without WinCache is exactly the same. All you get with this cache is way less file operations but same result.


All the three caches are connected so that if a file is changed, file entry in opcode cache, file cache and relative path cache gets removed in one go so that all three caches stay always in sync. We have tested WinCache with PHP 5.2 and PHP 5.3. Read documentation to learn about properties you can set to control behavior of these caches.

Links
Download link for PHP 5.2, VC6, NTS -
http://go.microsoft.com/?linkid=9681612
Download link for PHP 5.3, VC9, NTS - http://go.microsoft.com/?linkid=9681610
Documentation:
http://learn.iis.net/page.aspx/678/using-windows-cache-extension-for-php/
Forums - http://forums.iis.net/t/1160554.aspx

Please try out WinCache beta release and give us feedback.

Thanks,
Kanwal
Posted by kjsingla | 0 Comments
Filed under: , ,

FastCGI ISAPI 1.5 Beta for WinXP and Win2K3

IIS team today released FastCGI ISAPI 1.5 Beta for WinXP and Win2K3 which has some very nice additions to existing FastCGI ISAPI functionality. Following additions have been made to FastCGI ISAPI 1.0.

1. Few features we added to FastCGI module in IIS 7.5 have been added to FastCGI ISAPI 1.5 as well. These include MonitorChangesTo, StderrMode and Real-time tuning. Read more about these here.

2.
Few customers complained about IIS terminating the FastCGI processes abruptly (on running into IdleTimeout, InstanceMax etc) without giving them a chance to run cleanup code in the FastCGI application. In FastCGI ISAPI 1.5, we have added ability to get a signal from IIS whenever we are about to terminate a child process. To enable this, you need to set SignalBeforeTerminateSeconds property in fcgiext.ini to greater than 0. When this functionality is enabled, IIS will create an inheritable event and pass its handle value to child process as value of _FCGI_SHUTDOWN_EVENT_ environment variable. If FastCGI ISAPI ever encounters a situation (like worker process shutting down, fcgiext.ini changed, file being monitored changed etc) and is required to terminate the child process, it will first signal this event and wait for a maximum of SignalBeforeTerminateSeconds for process to terminate on its own. On detecting event being signaled, child processes can terminate themselves cleanly. If child process are still alive after the wait period, IIS will forcibly terminate them.

3.
Name of the named pipe through which communication with FastCGI process is taking place is communicated as value of _FCGI_X_PIPE_ environment variable.

4. Many customers faced trouble with FastCGI 1.0 because they accidently added some invalid configuration to fcgiext.ini and couldn’t decipher the cryptic error message they got from FastCGI ISAPI. In this release, if we see an invalid property present or invalid value of an enum type property, we exactly tell you what’s wrong in fcgiext.ini and where the error is.

5. FastCGI 1.0 had very strict checking on validity of response headers. We got bunch of reports from customers who complained about this difference of behavior compared to other web servers. In this release, we have removed these checks. If we find an invalid response header, we silently drop it from the response. This behavior matches behavior of other web servers.

6. Customers easily ran into ActivityTimeout especially while running install.php code of popular PHP applications. So we have changed default value of ActivityTimeout from 30 seconds to 70 seconds.

Below are the download links to FastCGI ISAPI 1.5 Beta. If you are using WebPI, FastCGI 1.5 is available under “what’s new” section. You can upgrade from FastCGI ISAPI 1.0 to FastCGI ISAPI 1.5 Beta or do a fresh install today. If you any feedback or trouble using FastCGI ISAPI 1.5, please report it on forums.

X86 – http://download.microsoft.com/download/C/7/8/C783108B-2FA7-4245-835C-E0B887A394D1/fcgisetup32.msi
X64 – http://download.microsoft.com/download/5/9/6/596CF5CB-9581-48A5-9205-4D9E3B4FB9BD/fcgisetup64.msi 

We also worked with Mike from Coast Research who fixed many issues in libfcgi.dll. Also he made changes to libfcgi.dll to make it handle SIGTERM properly using SignalBeforeTerminate functionality we added to FastCGI. Mike kindly released updated libfcgi (called libfcgi2.dll) so that others can now write a FastCGI application with much less pain. You can download libfcgi2.dll from here. Mike has published samples and documentation on how to use libfcgi2.dll on www.coastrd.com. Complete white paper on additions to libfcgi2.dll can be found here.

Thanks,

Kanwal

Posted by kjsingla | 1 Comments
Filed under: ,

Using advanced logging to log custom module data

Advanced logging module which media team released few days ago uses IIS tracing subsystem and allow module developers to log custom data in W3C compatible format using familiar IHttpTraceContext interface. If you are a module developer and want to generate W3C style logs for requests with custom data, doing it with advanced logging module is very easy. All you need to do is call IHttpTraceContext::RaiseTraceEvent passing data you want to publish. Your module installer can then configure advanced logging module to make it dump this data in a log file and you have complete logging solution without ever have to deal with log files yourself. Let’s see what it takes to make use of this functionality.

Generating trace information in your module

I have a simple class below which hides some of the complexity of IIS tracing subsystem. You can use most of the code below directly in your module. Just change area GUID and also code under RaiseEvent to dump your own custom data.

#include <httpserv.h>
#include <httptrace.h>

//
// Start of the new provider class AdvancedLoggingTraceProvider,
//
// GUID: {f41fdbf7-1b0a-45e9-9666-2918b0a9d144}
// NOTE:
// You must use this provider GUID for advanced logging to
// process events generated by your module
//
// Description: IIS: Advanced Logging
//
class AdvancedLoggingTraceProvider
{
public:
    static
    LPCGUID
    GetProviderGuid(
        VOID
    )
    {
        static const GUID AdvLoggingProviderGuid =
          {0xf41fdbf7,0x1b0a,0x45e9,{0x96,0x66,0x29,0x18,0xb0,0xa9,0xd1,0x44}};
        return &AdvLoggingProviderGuid;
    };

    enum enumAreaFlags
    {
        IISAdvancedLoggingGeneral = 0x0000
    };

    static
    LPCWSTR
    TranslateEnumAreaFlagsToString(
        enum enumAreaFlags EnumValue
    )
    {
        switch( (DWORD) EnumValue )
        {
            case 0x0000: return L"IISAdvancedLoggingGeneral";
        }
        return NULL;
    };

    static
    BOOL
    CheckTracingEnabled(
        IHttpTraceContext * pHttpTraceContext,
        enumAreaFlags       AreaFlags,
        DWORD               dwVerbosity
    )
    {
        HRESULT                  hr;
        HTTP_TRACE_CONFIGURATION TraceConfig;
        TraceConfig.pProviderGuid = GetProviderGuid();
        hr = pHttpTraceContext->GetTraceConfiguration( &TraceConfig );
        if ( FAILED( hr )  || !TraceConfig.fProviderEnabled )
        {
            return FALSE;
        }
        if ( TraceConfig.dwVerbosity >= dwVerbosity &&
             (  TraceConfig.dwAreas == (DWORD) AreaFlags || 
             ( TraceConfig.dwAreas & (DWORD)AreaFlags ) == (DWORD)AreaFlags ) )
        {
            return TRUE;
        }
        return FALSE;
    };
};

//
// Start of the new event class IISAdvancedLoggingGeneralEvents
// GUID: {ffa89186-ec59-42c1-988f-bbbd0ee48d4f}
// Use any random area GUID
// Description: Advanced Logging general events
//
class IISAdvancedLoggingGeneralEvents
{
public:
    static LPCGUID GetAreaGuid( VOID )
    {
        static const GUID AreaGuid =
          {0xffa89186,0xec59,0x42c1,{0x98,0x8f,0xbb,0xbd,0x0e,0xe4,0x8d,0x4f}};
        return &AreaGuid;
    };

    class SAMPLE_TRACE_EVENT
    {
    public:
        static
        HRESULT
        RaiseEvent(
            IHttpTraceContext * pHttpTraceContext,
            LPCGUID      pContextId,
            LPCWSTR      pSample_Data1,
            LPCWSTR      pSample_Data2
        )
        {
            HTTP_TRACE_EVENT Event;
            Event.pProviderGuid  = AdvancedLoggingTraceProvider::GetProviderGuid();
            Event.dwArea         =  AdvancedLoggingTraceProvider::IISAdvancedLoggingGeneral;
            Event.pAreaGuid      = IISAdvancedLoggingGeneralEvents::GetAreaGuid();
            Event.dwEvent        = 1;
            Event.pszEventName   = L"SAMPLE_TRACE_EVENT";
            Event.dwEventVersion = 1;
            Event.dwVerbosity    = 0;
            Event.cEventItems    = 6;
            Event.pActivityGuid  = NULL;
            Event.pRelatedActivityGuid = NULL;
            Event.dwTimeStamp    = 0;
            Event.dwFlags        = HTTP_TRACE_EVENT_FLAG_STATIC_DESCRIPTIVE_FIELDS;

            // pActivityGuid, pRelatedActivityGuid, Timestamp to be filled in by IIS

            HTTP_TRACE_EVENT_ITEM Items[ 3 ];
            Items[ 0 ].pszName = L"ContextId";
            Items[ 0 ].dwDataType = HTTP_TRACE_TYPE_LPCGUID; // mof type (object)
            Items[ 0 ].pbData = (PBYTE) pContextId;
            Items[ 0 ].cbData = 16;
            Items[ 0 ].pszDataDescription = NULL;
            Items[ 1 ].pszName = L"Sample_Data1";
            Items[ 1 ].dwDataType = HTTP_TRACE_TYPE_LPCWSTR; // mof type (string)
            Items[ 1 ].pbData = (PBYTE) pSample_Data1;
            Items[ 1 ].cbData  =
                 ( Items[ 1 ].pbData == NULL )? 0 : ( sizeof(WCHAR) * (1 + (DWORD) wcslen( (PWSTR) Items[ 1 ].pbData  ) ) );
            Items[ 1 ].pszDataDescription = NULL;
            Items[ 2 ].pszName = L"Sample_Data2";
            Items[ 2 ].dwDataType = HTTP_TRACE_TYPE_LPCWSTR; // mof type (string)
            Items[ 2 ].pbData = (PBYTE) pSample_Data2;
            Items[ 2 ].cbData  =
                 ( Items[ 2 ].pbData == NULL )? 0 : ( sizeof(WCHAR) * (1 + (DWORD) wcslen( (PWSTR) Items[ 2 ].pbData  ) ) );
            Items[ 2 ].pszDataDescription = NULL;

            Event.pEventItems = Items;
            pHttpTraceContext->RaiseTraceEvent( &Event );

            return S_OK;
        };

        static
        BOOL
        IsEnabled(
            IHttpTraceContext *  pHttpTraceContext
        )
        {
            return AdvancedLoggingTraceProvider::CheckTracingEnabled(
                                 pHttpTraceContext,
                                 AdvancedLoggingTraceProvider::IISAdvancedLoggingGeneral,
                                 0 );
        }
    };
};

Once you have class(es) similar to SAMPLE_TRACE_EVENT defined for the event(s) you want to generate, you can just call RaiseEvent in your module. Before generating the event you should call IsEnabled() to check if a relevant trace provider is present with tracing enabled. Here is what you will write in your module.

IHttpTraceContext* pTraceContext = pContext->GetTraceContext();

if( IISAdvancedLoggingGeneralEvents::SAMPLE_TRACE_EVENT::IsEnabled( pTraceContext ) == TRUE )
{
    IISAdvancedLoggingGeneralEvents::SAMPLE_TRACE_EVENT::RaiseEvent( pTraceContext,
                                                                     NULL,
                                                                     “Data1”,
                                                                     “Data2” );
}

Configuring advanced logging to generate logs 


Now to dump data generated by your module in advanced logging logs, you need to create log definition and specify event names as field names in advanced logging configuration. To do this, follow the steps as below in advanced logging UI.

1.       Go to advanced logging UI and create a new log definition.

2.       Specify details and then add additional fields which your module is generating. In the sample above these are Sample_Data1 and Sample_Data2. As advanced logging require a unique name to identify the event uniquely, we recommend people to use “<ModuleName>_” as prefix in the event names.

3.       Restart w3svc (this requirement will be removed in the next release of advanced logging).

That’s it. Log file produced will have single entry for each request received by the server. If a request gets short circuited and never reaches your module code where you generate the trace event, Sample_Data1 and Sample_Data2 field values will be blank strings. To log only requests which reached your module’s RaiseEvent call, make Sample_Data1 and Sample_Data2 fields “required” in advanced logging configuration. Note that ability to mark a field “required” is not there in the UI yet. You can edit the IIS configuration file manually or you other tools like appcmd, configuration editor to do it. More detailed steps to configure advanced logging are available here.

Hope this helps.
Kanwal

Posted by kjsingla | 0 Comments
Filed under: ,

Implementing IAppHostPathMapper in C

Few days ago I was required to implement IAppHostPathMapper interface in native C to map configuration path MACHINE/WEBROOT/APPHOST to DefaultAppPool.config and struggled with finding good documentation. With help of some incomplete, hard to find documentation and some head banging here is what worked for me. Hopefully this will be useful for few others J.

#include <ahadmin.h>

//
// PathMapper object structure with VTable pointer and reference counter
// Add other private data in this structure
//
typedef struct TestPathMapper
{
    IAppHostPathMapperVtbl * lpVtbl;
    ULONG                    cRef;
} TestPathMapper;

//
// Method definitions
//
STDMETHODIMP TestPathMapper_QueryInterface(TestPathMapper *, REFIID, LPVOID FAR *);
STDMETHODIMP_(ULONG) TestPathMapper_AddRef(TestPathMapper *);
STDMETHODIMP_(ULONG) TestPathMapper_Release(TestPathMapper *);
STDMETHODIMP TestPathMapper_MapPath(TestPathMapper *, BSTR, BSTR, BSTR *);

//
// IAppHostPathMapper VTable structure
//
static const IAppHostPathMapperVtbl vtblTestPathMapper =
{
    TestPathMapper_QueryInterface,
    TestPathMapper_AddRef,
    TestPathMapper_Release,
    TestPathMapper_MapPath
};

STDMETHODIMP
TestPathMapper_QueryInterface(
    TestPathMapper * pThis,
    REFIID           riid,
    LPVOID FAR *     lppvObj
)
{
    if ( !pThis || pThis->lpVtbl != &vtblTestPathMapper || !lppvObj )
    {
        return E_INVALIDARG;
    }

    if ( !memcmp(riid, &IID_IUnknown, sizeof( IID ) ) ||
         !memcmp(riid, &IID_IAppHostPathMapper, sizeof( IID ) ) )
    {
        pThis->lpVtbl->AddRef( pThis );
        *lppvObj = pThis;

        return S_OK;
    }

    *lppvObj = NULL;
    return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG)
TestPathMapper_AddRef(
    TestPathMapper * pThis
)
{
    if (!pThis || pThis->lpVtbl != &vtblTestPathMapper)
    {
        return 1;
    }

    return InterlockedIncrement( &pThis->cRef );
}

STDMETHODIMP_(ULONG)
TestPathMapper_Release(
    TestPathMapper * pThis
)
{
    LONG cRef;   
    if ( !pThis || pThis->lpVtbl != &vtblTestPathMapper )
    {
        return 1;
    }

    cRef = InterlockedDecrement( &pThis->cRef );
    if (cRef == 0)
    {
        pThis->lpVtbl->Release(pThis);
        pThis->lpVtbl = NULL;
        free( pThis );

    }

    return cRef;
}

STDMETHODIMP
TestPathMapper_MapPath(
    TestPathMapper * pThis,
    BSTR             bstrConfigPath,
    BSTR             bstrMappedPhysicalPath,
    BSTR *           pbstrNewPhysicalPath
)
{
    BSTR bstrNewPath  = NULL;
    if ( !pThis || pThis->lpVtbl != &vtblTestPathMapper )
    {
        return E_INVALIDARG;
    }

    if( wcscmp( bstrConfigPath, L"MACHINE/WEBROOT/APPHOST" ) == 0 )
    {
        bstrNewPath = SysAllocString( L"%systemdrive%\\inetpub\\temp\\apppools\\DefaultAppPool.config" );
    }
    else
    {
        bstrNewPath = SysAllocString( bstrMappedPhysicalPath );
    }

    if( bstrNewPath == NULL )
    {
        return E_OUTOFMEMORY;
    }

    *pbstrNewPhysicalPath = bstrNewPath;
    return S_OK;
}

int _tmain(
    int argc,
    _TCHAR* argv[]
)
{
    HRESULT   hr        = S_OK;
    DWORD     dwCount   = 0;
    VARIANT   varUnknown;

    TestPathMapper *            pTestPathMapper    = NULL;
    IAppHostAdminManager *      pAMgr              = NULL;
    IAppHostElement *           pElement           = NULL;
    IAppHostElementCollection * pElementCollection = NULL;   

    BSTR bstrPathMapper       = SysAllocString( L"pathMapper" );
    BSTR bstrConfigPath       = SysAllocString( L"MACHINE/WEBROOT/APPHOST" );
    BSTR bstrSitesSectionName = SysAllocString( L"system.webServer/caching" );

    if( bstrPathMapper == NULL || bstrConfigPath == NULL || bstrSitesSectionName == NULL )
    {
        hr = E_OUTOFMEMORY;
        goto Finished;
    }

    hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    if( FAILED(hr) )
    {
        goto Finished;
    }

    hr = CoCreateInstance( &CLSID_AppHostAdminManager,
                           NULL,
                           CLSCTX_INPROC_SERVER,
                           &IID_IAppHostAdminManager,
                           (void**) &pAMgr );
    if( FAILED( hr ) )
    {
        goto Finished;
    }

    //
    // Create pathMapper structure
    //
    pTestPathMapper = (TestPathMapper *)malloc( sizeof( TestPathMapper ) );
    if( pTestPathMapper == NULL )
    {
        hr = E_OUTOFMEMORY;
        goto Finished;
    }

    pTestPathMapper->lpVtbl = &vtblTestPathMapper;
    pTestPathMapper->cRef   = 1;

    VariantInit(&varUnknown);

    V_VT(&varUnknown)      = VT_UNKNOWN;
    V_UNKNOWN(&varUnknown) = pTestPathMapper;

    //
    // Set pathMapper metadata
    //
    hr = pAMgr->lpVtbl->SetMetadata( pAMgr, bstrPathMapper, varUnknown );
    if( FAILED( hr ) )
    {
        goto Finished;
    }

    //
    // Get the sites section and get number of sites
    //
    hr = pAMgr->lpVtbl->GetAdminSection( pAMgr, bstrSitesSectionName, bstrConfigPath, &pElement );
    if( FAILED( hr ) || pElement == NULL )
    {
        goto Finished;
    }

    hr = pElement->lpVtbl->get_Collection( pElement, &pElementCollection );
    if( FAILED( hr ) || pElementCollection == NULL )
    {
        goto Finished;
    }

    hr = pElementCollection->lpVtbl->get_Count( pElementCollection, &dwCount );
    if( FAILED( hr ) )
    {
        goto Finished;
    }

    wprintf( L"%d\n", dwCount );

Finished:

    VariantClear(&varUnknown);

    if( pElementCollection != NULL )
    {
        pElementCollection->lpVtbl->Release( pElementCollection );
        pElementCollection = NULL;
    }

    if( pElement != NULL )
    {
        pElement->lpVtbl->Release( pElement );
        pElement = NULL;
    }

    if( pAMgr != NULL )
    {
        pAMgr->lpVtbl->Release( pAMgr );
        pAMgr = NULL;
    }

    SysFreeString( bstrPathMapper );
    SysFreeString( bstrConfigPath );
    SysFreeString( bstrSitesSectionName );

    CoUninitialize();
    return 0;
}

Thanks.
Kanwal
Posted by kjsingla | 1 Comments
Filed under:

New features in configuration system and appcmd in IIS 7.5

Following new features have been added to IIS configuration system and appcmd command line tool in IIS 7.5.

Configuration System

1.       Configuration system tracing and logging
In IIS 7.5, IIS native configuration system can generate trace events capturing all IIS configuration activity. Because all IIS administrative tools (WMI, appcmd, MWA, UI, Powershell etc) call into native configuration system, events are generated irrespective of which administrative tool is used to read/write IIS configuration. Tracing is not enabled by default. You can go to “Application and Service Logs->Microsoft->Windows->IIS-Configuration” in event viewer and enable tracing. IIS generates 4 kinds of events. These are administrative, operational, analytic and debug. Right click on areas in event viewer and select “enable log” for categories you want to enable tracing.


2.       Ability to work with multiple .Net versions
IIS configuration system has the ability to work with multiple .net versions. For this configuration system honors metadata defaultManagedRuntimeVersion. If set, IIS will use this information to find which machine.config and root web.config it should work against. Default version assumed is v2.0. If you want to make configuration system go against other .net version, you are required to set defaultManagedRuntimeVersion metadata. If the configuration path identifies an application, configuration system automatically figures out the version by checking managedRuntimeVersion set for the application pool for the application. All administrative tools are updated to allow working with different versions of NetFx.


3.       Shared configuration now can be configured to use polling instead of change notifications to track changes to applicationHost.config file. This can be configured by specifying “enableUncPolling” and “pollingPeriod” properties in configurationRedirection section. Default value of enableUncPolling is false which means the feature is not turned on by default.

4.        “availableReadableMetadata” and “availableWritableMetadata” metadata properties are available in IAppHostAdminManager, IAppHostElement, IAppHostMethod, IAppHostProperty interfaces. These can be used to find what readable and writable metadata is available for each object.

Appcmd

1. Appcmd now has ability to add <clear/> tag in the collections using '~'. Command to clear <error> collection entries in httpErrors section will be following.
appcmd set config /section:httpErrors /~


2. Previously appcmd could only add one type of addElement collection type which limited its utility for authorization section as the addElement could be allow or deny. Now appcmd can be used to add element of a paricular type. Command to add deny entry in authorization section will be as following.
appcmd set config /section:authorization /+deny[verbs='put']
appcmd set config /section:authorization /-deny[verbs='get']


3. /clr switch can be used to specify which version of .net framework you want to work against when dealing with machine.config and web.config. If this switch is not specified, version dotnet version 2.0 will be assumed.appcmd list config /section:profile /clr:4 (or v4 or 4.0).

4. /admin switch can be used to map configuration path MACHINE/WEBROOT to administration.config. This enables appcmd to be used against administration.config. So command to read moduleProviders section from administration.config will be “appcmd list config /section:moduleProviders /admin”

Hope this helps.
Kanwal

Posted by kjsingla | 0 Comments

IIS 7.5 updates to custom errors and compression

Looking at number of people reaching my first blog post while searching for information on IIS 7.5, I figured I should do few more posts on changes which are coming in IIS 7.5. In this blog post I am covering new features which have been added to compression and custom errors modules in IIS 7.5.

Changes to custom errors

1.       system.webServer/httpErrors section is made delegation safe
In IIS 7.0, httpErrors section was not delegated by default which means custom errors were not available to site owners for customization. Reason why the section was not delegated is because once the section is delegated, site owners are free to return any file they can read as a custom errors response which wasn’t secure. Server administrators can delegate the section securely using custom application pool identities and file ACLs which require lot of work. In IIS 7.5, if system.webServer/httpErrors@allowAbsolutePathsWhenDelegated property is set to false custom errors module will only allow paths relative to site root folder (not absolute paths) when the section is delegated. If server administrators want to allow absolute paths in web.config files even when section is delegated, they can set allowAbsolutePathsWhenDelegated property to true. Error 500.19 (configuration error) with detailed error description “Absolute physical path <folder> is not allowed in system.webServer/httpErrors section in web.config file.  Use relative path instead.will be generated if allowAbsolutePathsWhenDelegated is set to false and an absolute path is detected in web.config. This restriction is applied to properties path and prefixLanguageFilePath but not defaultPath. Here is how httpErrors section will look like if a site owner wants to configure localized custom errors when only relative paths are allowed.

<httpErrors>
    <
clear/>
    <!--
Make module return %SITEROOT%\myerrorsfolder\%LANGUAGECODE%\401.htm -->
    <
error statusCode="401" prefixLanguageFilePath="myerrorsfolder" path="401.htm" />
    <
error ...
</
httpErrors>


With this feature, hosters can now easily delegate custom errors section to site owners. Note that this feature will be available in Win2K8 sp2 as well.

2.       Changes to default configuration
With httpErrors section now made delegation safe, the section is delegated in a fresh install. Because the behavior is controlled by system.webServer/httpErrors@allowAbsolutePathsWhenDelegated property, this attribute is locked in the default configuration. This ensures that this property cannot be overridden by site owners to enable absolute file paths. As relative path restriction is not applied to defaultPath property, system.webServer/httpErrors@defaultPath is locked as well and cannot be used in web.config files.


Changes to compression

1.       Changes to schema
If a file is too small, compressed version of a file can be larger than original file. Due to this minimum file size for static compression (property system.webServer/httpCompression@minFileSizeForCompression) has been changed from 256 bytes to 2700 bytes. Also default value of system.webServer/urlCompression@doDynamicCompression is changed to true which will result in dynamic compression being enabled out of the box.


2.       Changes to default configuration
application/atom+xml”, “application/xaml+xml“ mime types are added to list of mime types for which static compression is enabled by default.


3.       Dynamically compressed response is flushed periodically
system.webServer/httpCompression@dynamicCompressionBufferLimit property controls maximum amount of dynamically compressed response IIS will buffer before forcing a flush to client.

In addition to these changes in compression, we are looking into completely supporting compression scheme extensibility as there has been some interest in writing new compression schemes. I will update this post with details on that when I know for sure that we are doing it for IIS 7.5.


Hope this helps.
Kanwal

Posted by kjsingla | 1 Comments
Filed under: ,

Improvements to FastCGI in IIS 7.5

Following improvements have been made to FastCGI module in Win7 (and Win2K8 R2).

1.       Monitor changes to a file

We have added ability to monitor changes to a file for each FastCGI process pool. Module will recycle FastCGI processes for the process pool if a change to the file is detected. Users can specify file path to monitor using system.webServer/fastCgi/application@monitorChangesTo property. Value can be absolute path to the file or path relative to the folder containing FastCGI executable. In case of php, if fullPath property is set to “c:\php\php-cgi.exe”, monitorChangesTo property can be set to “php.ini” or “c:\php\php.ini”. If file to monitor is not found, status code 500 is returned on each request. Default value of this property is blank which means no file monitoring.

2.       Real-time tuning

In win2k8, maxInstances property could be configured to a constant value greater than 0. This value dictated maximum number of FastCGI processes which can be launched for each application pool which is also equal to maximum number of requests which can be processed simultaneously as one process handle only one request at a time. Default value of this property was 4. Administrators were required to do some testing with different values to find the optimal number of maxInstances for their server. In win7, if you set maxInstances to 0, FastCGI module automatically adjusts this number up or down every few seconds based on the system load and number of requests waiting in the queue.

3.       Tracing

In win7, STDERR stream can be used to send trace messages to FastCGI module which are logged to IIS FREB trace (if enabled). Trace messages are identified by start markers “IIS_TRACE_INFO:”, “IIS_TRACE_WARNING:”, “IIS_TRACE_ERROR:”. Depending on which start marker is used, information/warning/error trace event is generated. End of a trace statement should be marked by “:IIS_TRACE_END”.  You can send as many trace statements as you like for each request. Most application frameworks running as FastCGI provide a way to send text on STDERR stream. In case of php, function error_log() can be used. So error_log(“IIS_TRACE_WARNING: Warning event.:IIS_TRACE_END”); statement in PHP code will make “Warning event” appear in IIS trace logs. Getting your application traces with traces generated by web server can be a very powerful tool for debugging problems. Note that starting from PHP 5.2.7 you can pass a parameter to the error_log() function to specify that the logged message should be always sent to STDERR regardless of error_log setting in php.ini. To do that use message_type parameter set to 4, e.g. error_log(“IIS_TRACE_WARNING: Warning event.:IIS_TRACE_END”, 4);

4.       STDERR stream handling

FastCGI module can now be configured to handle text sent on STDERR stream differently. In IIS7 we used to return status code 500 and send data received on STDERR stream as the response. Now the behavior can be modified by setting system.webServer/fastCgi/application@stderrMode property. Value of this property can be set to one of following values.

ReturnStderrIn500 - We will set status code 500 and send whatever we receive on STDERR as response. This is the default value and gives same behavior as IIS7.
ReturnGeneric500 - Module will set status code 500 but will return a generic 500. This configuration is useful if you want to enable detailed error logging for server but don’t want to return these errors to users.
IgnoreAndReturn200 - Text on STDERR stream is completely ignored and we will send what we receive on STDOUT as response with status code 200. This property is useful if use debug statements for tracking purposes.
TerminateProcess – FastCGI process will get terminated and generic 500 error message will be returned.

Trace statements are removed from STDERR stream before they are subjected to stderrMode logic. Post Win7 Beta, we are looking into making FastCGI work with CpuLimit feature and also dropping invalid response headers instead of failing requests with 500.

Hope this helps.
Kanwal
Posted by kjsingla | 1 Comments

Generating trace information in IIS native modules

IIS7 has really nice tracing capabilities which are available to all module developers. If you want to make it easy for you and your customers to debug problems in your modules, you should definitely be using them. You can use IHttpTraceContext::RaiseTraceEvent or IHttpTraceContext::QuickTrace in your IIS7 native modules to generate trace information. Once you have your module generating trace information, you or your customers can set trace rules to collect trace data for particular requests.

Simplest way to generate trace events is by using IHttpTraceContext::QuickTrace API. You can get trace context for a particular request using IHttpContext::GetTraceContext and then dump two strings and a HRESULT with a particular verbosity (or trace level) using QuickTrace. Trace level for QuickTrace API can be from 1-7 but you will probably never use values 6, 7 which are for MODULE_START and MODULE_END events. Below is sample code calling QuickTrace.

#include <httptrace.h>

#define TRACE_CRITICAL_ERROR  1
#define TRACE_ERROR           2
#define TRACE_WARNING         3
#define TRACE_INFO            4
#define TRACE_VERBOSE         5

IHttpContext *     pContext...
IHttpTraceContext* pTraceContext = pContext->GetTraceContext();

pTraceContext->QuickTrace(
    L"Invalid data"      // Data1
    L"More Data",          // Data2
    0x8000000D,            // HRESULT
    (UCHAR)TRACE_WARNING); // Trace level

Each trace event requires a trace provider and trace area with which it is associated. When you use QuickTrace, trace provider “WWW Server” and trace area “Module” is used. So for trace events generated by IHttpTraceContext::QuickTrace to appear in trace logs, tracing for provider “WWW Server” and area “Module” must be enabled. If you want to dump information in a more custom format, you can use IHttpTraceContext::RaiseTraceEvent API instead which gives you the maximum flexibility. Types of data which you can dump in a trace event using RaiseTraceEvent are defined by HTTP_TRACE_TYPE enum in httptrace.h. Here is a sample code which generate trace events as generated by QuickTrace using RaiseTraceEvent instead.

HRESULT                  hr            = S_OK;
DWORD                    dwVerbosity   = 3; // Warning
DWORD                    dwArea        = 512;
BOOL                     fTraceEnabled = FALSE;

//
// TraceProviderGuid for WWW Server picked from applicationHost.config
//
static const GUID TraceProviderGuid =
        {0x3a2a4e84,0x4c21,0x4981,{0xae,0x10,0x3f,0xda,0x0d,0x9b,0x0f,0x83}};
static const GUID TraceAreaGuid =
        {0x528dbd54,0x1b68,0x4f58,{0xa8,0xc0,0xb3,0x92,0xdf,0x6d,0xf0,0xc9}};

// IHttpContext*   pContext;
IHttpTraceContext* pTraceContext = pContext->GetTraceContext();

//
// Check if tracing for provider + area is enabled before generating the event
//
HTTP_TRACE_CONFIGURATION TraceConfig;
TraceConfig.pProviderGuid = &TraceProviderGuid;

hr = pTraceContext->GetTraceConfiguration( &TraceConfig );

if( !FAILED(hr) && TraceConfig.fProviderEnabled == TRUE )
{
    if( ( TraceConfig.dwVerbosity >= dwVerbosity ) &&
        ( ( TraceConfig.dwAreas & dwArea ) == dwArea ) )
    {
        fTraceEnabled = TRUE;
    }
}

if( fTraceEnabled == TRUE )
{
    PCWSTR pData1      = L"Invalid data";
    PCWSTR pData2      = L"More data";
    DWORD  dwHresult   = 0x8000000D;

    HTTP_TRACE_EVENT Event;

    Event.dwEvent        = 150;    // Change this number for different events
    Event.pszEventName   = L"SampleModuleTraceEvent";
    Event.cEventItems    = 3;      // count of trace event items
    Event.dwArea         = dwArea;
    Event.dwEventVersion = 1;
    Event.dwVerbosity    = dwVerbosity;
    Event.pAreaGuid      = &TraceAreaGuid;
    Event.pProviderGuid  = &TraceProviderGuid;
    Event.dwFlags        = HTTP_TRACE_EVENT_FLAG_STATIC_DESCRIPTIVE_FIELDS;
    // IIS will set pActivityGuid, pRelatedActivityGuid, Timestamp
    Event.pActivityGuid = NULL;
    Event.pRelatedActivityGuid = NULL;
    Event.dwTimeStamp = 0;

    HTTP_TRACE_EVENT_ITEM TraceItems[ 3 ];
    TraceItems [ 0 ].pszName = L"Data1";
    TraceItems [ 0 ].dwDataType = HTTP_TRACE_TYPE_LPCWSTR;
    TraceItems [ 0 ].pbData = (PBYTE) pData1;
    TraceItems [ 0 ].cbData  = (pData1 == NULL ) ? 0 : (sizeof(WCHAR) * (1 + wcslen(pData1)));
    TraceItems [ 0 ].pszDataDescription = NULL;

    TraceItems [ 1 ].pszName = L"Data2";
    TraceItems [ 1 ].dwDataType = HTTP_TRACE_TYPE_LPCWSTR;
    TraceItems [ 1 ].pbData = (PBYTE) pData2;
    TraceItems [ 1 ].cbData = (pData2 == NULL ) ? 0 : (sizeof(WCHAR) * (1 + wcslen(pData2)));
    TraceItems [ 1 ].pszDataDescription = NULL;

    TraceItems [ 2 ].pszName = L"ErrorCode";
    TraceItems [ 2 ].dwDataType = HTTP_TRACE_TYPE_ULONG;
    TraceItems [ 2 ].pbData = (PBYTE) &dwHresult;
    TraceItems [ 2 ].cbData = 4;
    TraceItems [ 2 ].pszDataDescription = NULL;

    Event.pEventItems = TraceItems;
    pTraceContext->RaiseTraceEvent( &Event );
}

You can point your customers to this article to enable tracing and generate detailed traces. In the code above we used 'WWW Server' trace provider and 'Module' as trace area. If you want to define your own trace provider to group traces from your module, you can define your own trace provider and/or trace area. I will cover that in my next post.

Thanks.
Kanwal
Posted by kjsingla | 1 Comments

Using IMetadataInfo::GetModuleContextContainer to store configuration data

If you write an IIS7 module, you would typically want to control its behavior based on some configuration data. IIS7 makes it really easy for developers to extend configuration schema so that their customers can put configuration data for their modules with other IIS configuration in applicationHost.config and web.config. Native module developers can then use AhAdmin APIs to read this configuration data. Reading configuration data for each request can be expensive. Developers would typically think of improving the performance by reading the configuration data once and then keeping the data in memory which can be used for other requests. To do this right, you need to worry about following things.
  • Keep unique data only for the paths where configuration changes and not for all possible configuration paths.
  • Detect configuration changes so that module always behave as per the latest configuration.
  • Only delete the configuration data of paths which are affected by the change and not throw everything.
  • Make sure this configuration store is accessed in thread safe manner.
Instead of implementing this from scratch, you can use IMetadata interface available in httpserv.h. Call IHttpContext::GetMetadata() to get IMetadata object which is different for each unique configuration path and then use the IMetadataInfo::GetModuleContextContainer()->SetModuleContext() to store any data you like for your module. Configuration data is a perfect example of kind of data you would like to store for a unique configuration path. Stored data can be retrieved using IMetadataInfo::GetModuleContextContainer()->GetModuleContext(). In addition to making sure that a unique metadata object is available only for paths where configuration changes, IIS will also take care of deleting proper stored contexts when a change notification arrives. Here is how doing this will look like in code.

//
// Globals
//
HTTP_MODULE_ID g_pModuleContext = NULL;
IHttpServer *  g_pGlobalInfo    = NULL;

HRESULT
WINAPI
RegisterModule(
    DWORD                          dwServerVersion,
    IHttpModuleRegistrationInfo *  pModuleInfo,
    IHttpServer *                  pGlobalInfo
)
{
    ...

    g_pGlobalInfo    = pGlobalInfo;
    g_pModuleContext = pModuleInfo->GetId( );

    ...
}

class MODULE_CONFIG : public IHttpStoredContext
{
public:

    //
    // Always call this method to get configuration data
    //
   
static
    HRESULT
    GetModuleConfig(
        IHttpContext *   pContext,
        MODULE_CONFIG ** ppModuleConfig
    );

    HRESULT
    Initialize(
        IHttpContext * pContext
    );

    // virtual
    VOID
    CleanupStoredContext(
        VOID
    )
    {
        delete this;
    }

    //
    // Stored Configuration Data
    //
};

//static
HRESULT
MODULE_CONFIG::GetModuleConfig(
    IHttpContext *   pContext,
    MODULE_CONFIG ** ppModuleConfig
)
{
    HRESULT                       hr                 = S_OK;
    MODULE_CONFIG *               pModuleConfig      = NULL;
    IHttpModuleContextContainer * pMetadataContainer = NULL;

    pMetadataContainer = pContext->GetMetadata()->GetModuleContextContainer();
    pModuleConfig = (MODULE_CONFIG *)pMetadataContainer->GetModuleContext( g_pModuleContext );

    if ( pModuleConfig != NULL )
    {
        //
        // We found stored data for this module for the metadata
        // object which is different for unique configuration path
        //
        *ppModuleConfig = pModuleConfig;
        return S_OK;
    }

    //
    // If we reach here, that means this is first request or first
    // request after a configuration change IIS core will throw stored context
    // if a change notification arrives for this metadata path
    //
    pModuleConfig = new MODULE_CONFIG();
    if ( pModuleConfig == NULL )
    {
        return E_OUTOFMEMORY;
    }

    //
    // Read module configuration data and store in MODULE_CONFIG
    //
    hr = pModuleConfig->Initialize( pContext );
    if ( FAILED( hr ) )
    {
        pModuleConfig->CleanupStoredContext();
        pModuleConfig = NULL;

        return hr;
    }

    //
    // Store MODULE_CONFIG data as metadata stored context
    //
    hr = pMetadataContainer->SetModuleContext( pModuleConfig,
                                               g_pModuleContext );
    if ( FAILED( hr ) )
    {
        pModuleConfig->CleanupStoredContext();
        pModuleConfig = NULL;

        //
        // It is possible that some other thread stored context before this thread
        // could do. Check returned hr and return context stored by other thread
        //
        if ( hr == HRESULT_FROM_WIN32( ERROR_ALREADY_ASSIGNED ) )
        {
            *ppModuleConfig = (MODULE_CONFIG *)pMetadataContainer->GetModuleContext( g_pModuleContext );
            return S_OK;
        }
    }

    *ppModuleConfig = pModuleConfig;
    return hr;
}

HRESULT
MODULE_CONFIG::Initialize(
    IHttpContext * pContext
)
{
    HRESULT                hr               = S_OK;
    IAppHostAdminManager * pAdminManager    = NULL;
    IAppHostElement *      pAppHostElement  = NULL;
    BSTR                   bstrSectionName  = NULL;
    BSTR                   bstrConfigPath   = NULL;

    ...

    pAdminManager = g_pGlobalInfo->GetAdminManager();
    ...

    //
    // Read configuration data at metapath of IMetadataInfo object
    //
    bstrConfigPath = SysAllocString( pContext->GetMetadata()->GetMetaPath() );
    ...

    hr = pAdminManager->GetAdminSection( bstrSectionName,
                                         bstrConfigPath,
                                         &pAppHostElement);
    ...
}

Hope this helps.
Kanwal
Posted by kjsingla | 0 Comments

Generating configuration to allow/deny access to countries

There have been few requests on forums where people wanted to control access to sites based on country from where request originated. We recommended people to use IP restriction module functionality which required people to add IP address ranges of the countries they want to grant or deny access in ipSecurity section. This is easier said than done. There are a number of IP-to-country mapping lists available for free and updated frequently. These lists are usually in CSV format and identify countries (and sometimes even regions in the country) to which a particular IP-range is assigned.

Read full post here.

Thanks,
Kanwal
Posted by kjsingla | 0 Comments

Enabling intellisense for AhAdmin code in scripts

MWA (Microsoft.Web.Administration) is pretty popular among developers who wish to read/write IIS configuration because of ease of writing managed code (which enables its use in powershell as well) and also because it provides a more intuitive wrapper over the IIS configuration system which is easier to work with. MWA has concept of application pool collection, sites, bindings etc while IIS configuration system only understands sections, elements, properties, location paths etc and not application pools, virtual directories which makes life of developers writing AhAdmin code very difficult. If you disagree, try adding a binding using MWA and then using AhAdmin. Presence of tools like genscode makes using MWA even more easier. Even though its painful to use AhAdmin interfaces, you don't get intellisense in scripts when working with COM components and you are required to refer configuration schema all the time, people do write AhAdmin code for variety of reasons. One reason is because people want to access IIS configuration in VBScript/JavaScript. Another reason is because people want to write code which works on server core as well (appcmd and WMI are alternatives as well). I have been thinking of ways to make life of AhAdmin developers easier for sometime.

Read full blog post here.

Thanks.
Kanwal

Posted by kjsingla | 0 Comments

Tool to generate AhAdmin code for actions in IIS7 UI

If you ever write AhAdmin code, this is for you.I have written a tool which generates equivalent JavaScript/C#/VB.Net AhAdmin code for actions done in IIS7 UI.

IIS7 configuration system gives access to IIS config files via a set of COM APIs. You can read/write to IIS configuration using these APIs in a script or C/C++ or using any of the managed language. All the tools which are shipped with IIS like managed APIs (Microsoft.Web.Administration), UI (IIS Manager), WMI provider uses these COM APIs internally to perform all the configuration operations. When you do an action in the UI, UI is making calls to these COM APIs (actually via MWA). Programming against COM APIs provided by IIS configuration system requires extensive understanding of these APIs and also IIS schema. Also you are required to know what exactly you want to change in the configuration. So when if you want to enable IIS detailed error messages programmatically, you are required to know which property should be changed to what value to have the desired effect. IIS Manager does present the properties in more readable form which makes it easier to decide what changes you want to make to the system which is helpful to the administrators but not developers. Imagine if you could just do an operation using IIS Manager and get the equivalent AhAdmin code which you can copy-paste in your application.

Read full post here.

-Kanwal-

Posted by kjsingla | 0 Comments

Exposing runtime data from IIS worker processes

Dynamic properties feature in IIS7 configurtion system lets you expose dynamic data as configuration system properties. RscaExt.xml which is part of IIS7 has all the RSCA (runtime status and control APIs) functionality exposed as dynamic properties through configuration system (read more here). One of the things RSCA let you do is expose custom runtime data from a worker process using GetCustomData method. You can call GetCustomData method for a worker process instance which sends a GL_RSCA_QUERY notification to the worker process. Once this notification is generated, IIS core engine then calls all the modules which subscribed to GL_RSCA_QUERY global notification. These modules can then return any runtime data they want to which is seen as the return value of GetCustomData method. RequestMonitorModule is one example of such module which subscribes to RQ_BEGIN_REQUEST, RQ_END_REQUEST to keep track of requests getting processed and also subscribes to GL_RSCA_QUERY to expose this requests in flight data through RSCA. CGlobalModule::OnGlobalRscaQuery method which gets invoked on GL_RSCA_QUERY event accepts IGlobalRSCAQueryProvider as an argument which can be used to get the function name (referred to as GuidOfFunctionCall in rscaext.xml), function parameters and also to set the return value of GetCustomData.

Read full post here.

-Kanwal-
Posted by kjsingla | 0 Comments
More Posts Next page »
 
Page view tracker