Strašlivé dědictví přípon aneb preCondition "managedHandler" a HTTP moduly

Na počátku byla tma. Pak se zrodil HyperText Transfer Protocol. Mezitím pravda nastala celá řada dalších věcí, ale ty nejsou pro náš příběh příliš podstatné. Podstatné je, co se dělo potom. Webové servery byly úplně původně navrženy spíš jako úložiště dat, než jako aplikační platforma. Na server se uložil soubor, klient si jej vyžádal a server mu ho poslal, aniž se sebeméně zajímal o jeho obsah. Bylo to takové jeddnodušší FTPko.

Základ webových aplikací, tak jak je známe dnes, byl položen teprve ve chvíli, kdy se web server začal zajímat o to, co vlastně posílá. Kdy ho bylo možno zkonfigurovat tak, aby za určitých okolností klientovi neposlal obsah požadovaného souboru, alébrž spustil jakýsi kód a klientovi poslal výsledek. Historicky za tímto účelem existovaly dvě technologie: CGI (Common Gateway Interface) a ISAPI (Internet Server Application Programming Interface). Ač je jejich princip fungování dost odlišný, jedno mají společné: požadavky na ně se typicky mapují pomocí přípon. Podle přípony požadovaného souboru server rozpoznal, co s ním má provést. Soubory s příponou .php se tak poslaly jednomu procesoru, .asp jinému a cokoliv neznámého poslalo beze změny.

Na platformě Microsoft byl tento přístup ortodoxně dodržován Internet Information Services až do verze 6.0. Ve výchozí konfiguraci byly jednotlivé typy aplikačních platforem rozděleny právě pomocí přípon. V zásadě vám nic nebránilo v tom, aby v rámci jednoho virtuálního web serveru vedle sebe běželo například ASP.NET, ASP a PHP. Jednotlivé platformy o požadavcích, které nešly na "jejich" přípony prostě nevěděly.

Pak přišla verze 7.0 se zcela novou architekturou, kterou sice je možné pokládat za ideového nástupce ISAPI extensions, ale ve které na příponách až tak nezáleželo – protože tou dobou se z různých důvodů z přípony stala persona non grata. HTTP moduly, známé už od ASP.NET verze 1.0, najednou dostaly nové pole působnosti: používají se na všechny požadavky, bez ohledu na přípony.

Nové možnosti ale přinesly i nové problémy. Ukážeme si to na příkladu modulů pro Forms Authentication a URL Authorization. Na IIS 6.0 se toto zabezpečení vztahovalo pouze na ASP.NET stránky. I když jste pomocí URL authorization zakázali uživateli přístup, stále mohl číst například obrázky nebo CSS soubory. Pokud byste takovou aplikaci beze změny přenesli na IIS 7, její chování by se změnilo. Autorizační pravidla by se začala vztahovat na všechny soubory a dokud byste nepřihlášeným uživatelům explicitně nepovolili přístup např. ke kaskádovým stylům a obrázkům, zobrazila by se např. přihlašovací stránka bez nich, jinak než bylo původně zamýšleno.

Pročež přišel Microsoft s preCondition zvanou managedHandler. Obecně mechanismus preConditions slouží k natažení správné verze modulu či handleru v závislosti na verzi .NET runtime nebo architektuře procesoru (32/64- bit) a vyhodnocuje se jenom jednou, při spouštění application poolu. Leč managedHandler je výjimkou: vyhodnocuje se při každém požadavku a pokud je přítomen, spustí se pouze v případě, že je požadavek dalším nastavením mapován na handler psaný v managed kódu (což se stále děje typicky pomocí přípon). V zásadě lze tedy pomocí této preCondition emulovat chování předchozích verzí.

Výchozí nastavení je takové, že všechny managed moduly, které jsou součástí .NET Frameworku, mají tuto podmínku nastaveny. Když se podíváte do svého souboru applicationHost.config, najdete tam něco na tento způsob:

<system.webServer>

    <modules>

        <add name="OutputCache" type="System.Web.Caching.OutputCacheModule" preCondition="managedHandler" />

        <add name="Session" type="System.Web.SessionState.SessionStateModule" preCondition="managedHandler" />

        <add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule" preCondition="managedHandler" />

        <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" preCondition="managedHandler" />

        <add name="DefaultAuthentication" type="System.Web.Security.DefaultAuthenticationModule" preCondition="managedHandler" />

        <add name="RoleManager" type="System.Web.Security.RoleManagerModule" preCondition="managedHandler" />

        <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" preCondition="managedHandler" />

        <add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule" preCondition="managedHandler" />

        <add name="AnonymousIdentification" type="System.Web.Security.AnonymousIdentificationModule" preCondition="managedHandler" />

        <add name="Profile" type="System.Web.Profile.ProfileModule" preCondition="managedHandler" />

        <add name="UrlMappingsModule" type="System.Web.UrlMappingsModule" preCondition="managedHandler" />

    </modules>

</system.webServer>

(seznam modulů odpovídá verzi runtime 2.0, ve verzi 4.0 jich ještě několik přibylo)

Pokud si napíšete a zaregistrujete vlastní HTTP modul, záleží na vás, zda preCondition="managedHandler" použijete či nikoliv.

Pokud chcete, aby se konkrétní modul (typicky právě FormsAuthentication a UrlAuthorization) použil, musíte ho ve web.configu své aplikace odregistrovat a zaregistrovat znovu, tentokrát bez preCondition:

<system.webServer>

    <modules>

        <remove name="FormsAuthentication" />

        <remove name="UrlAuthorization" />

        <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />

        <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />

    </modules>

</system.webServer>

Pokud chcete použít všechny takové moduly, má pro vás IIS k dispozici techniku dle mého názoru dosti zvrhlou, override na override. Konfigurační sekce modules totiž oplývá atributem s poetickým názvem runAllManagedModulesForAllRequests. Pokud je nastaven na true, pak se preCondition="managedHandler" bude ignorovat. Toto nastavení se vám bude hodit zejména v případě, že používáte URL routing v ASP.NET 4.0, neboť i ten je standardně nastaven tak, že se aplikuje jenom v případě managed handleru. U jeho registrace v applicationHost.config vidíte v akci ještě jednu preCondition, a to požadavek na verzi .NET Runtime 4.0:

<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="managedHandler,runtimeVersionv4.0" />

Problematice preConditions jako takových se budu věnovat v samostatném článku.

  • Altairis
  • Nemesis
  • Microsoft MVP
  • IIS
  • ASP.NET