Standardizované CGI (ač mocné) je často nevhodné už jen proto, že je nevyhnutelně pomalé. Ani případné další nástroje, které daný webový server nabízí (např. LiveWire/JavaScript pod servery Netscape, ASP u IIS, PHP pro Apache) nemusí být dostatečné pro každý problém. Pokud je třeba těsná integrace se serverem nebo pro danou aplikaci rozhraní "standardního" nástroje není z nějakého důvodu vhodné, nastupují na scénu serverové moduly.
Prakticky každý pokročilejší WWW server nabízí možnost, jak rozšířit funkčnost pomocí externích modulů. Apache server má jako moduly implementováno prakticky všechno (přístup je "co může být modul, tak je modul"), do Netscape serveru lze modul zapojit pomocí NSAPI, webový server IIS firmy Microsoft má ISAPI. Bohužel nejsou navzájem kompatibilní - jednak proto, že vnitřní architektura každého serveru je odlišná, a typicky také každý má trochu odlišné cíle. IIS asi na ničem jiném než Microsoft Windows nepojede a nemusí se tedy starat o přenositelnost do jiného prostředí. Netscape servery musí v NSAPI podstatně víc věcí "obalovat", řešit například různou implementaci vláken a ostatních služeb systému tak, aby se modul napsaný v NSAPI pokud možno bez větších problémů dal přeložit a používat jak na serveru běžícím na Solarisu, tak na stroji s MS Windows. Oba komerční servery byly také poměrně dobře vyladěny s tím, aby i složité věci byly možné. Naproti tomu Apache API je hlavně jednoduché a teprve potom něco jiného - hlavní vývojovou silou byla spíš snaha přesunou kód z jádra do modulů a nabídnout programátorsky rozumně "čisté" API. Založené je na myšlence, že každý modul zpracovává určité speciální soubory, bez nutnosti spolupráce nebo komunikace mezi moduly. Některé typy aplikací (například filtry zajišťující transparentně kompresi nebo překódování češtiny) u Apache tedy nejsou možné vůbec nebo jen za cenu velkého "znásilnění" serveru, někdy dokonce s nutností upravit jeho zdrojový kód. V mnoha jiných ohledech je ale Apache API kompletnější než ISAPI. Většina funkcionality jádra Apache je exportována v poměrně obecné formě přes API a moduly mohou, v případě potřeby bez větších problémů, převzít některé funkce jádra serveru a provádět je samy.
Internet Information Server (IIS) Microsoftu tedy nabízí vlastní rozhraní - ISAPI. Modul se napíše jako samostatný program a zkompiluje se do podoby sdílené knihovny (DLL). Serveru se pak řekne, že daný modul má nahrát (typicky pomocí GUI nástrojů, jako je třeba MS Management Console), webový server se restartuje, nahraje si příslušné moduly do paměti a modul je možné začít používat. Moduly běží (na rozdíl například od CGI) ve stejném kontextu a adresovém prostoru jako webový server. Má to své výhody - data mezi "jádrem" serveru a ISAPI aplikací se předávají snadno a rychle pomocí ukazatelů, modul může jednoduše zjišťovat podrobnosti o serveru. Daní je ovšem to, že případná chyba v modulu může způsobit pád celého WWW serveru. Jako programovací jazyk lze použít C nebo C++. Podle pověstí lze použít i Visual Basic, ale jeho nasazení asi v tomto případě není příliš moudré, i kdyby bylo možné. Rozhraní je definováno jako reentrantní a ISAPI moduly musí s tímto počítat.
ISAPI aplikace mohou být v základě dvou druhů. Extension (rozšíření) jsou prakticky rychlejší obdobou CGI, volají se explicitně, tj. v URL se používá plná cesta k příslušnému DLL. Druhý typ je filtr, který se nijak v URL neuvádí a zvenčínemusí být jeho funkce nijak nápadná. Filtr při inicializaci serveru "řekne", pro jaké události má být volán, a server mu pak dává vědět, pokud k těmto událostem dojde. Filtry mají mnohem větší kontrolu nad prací serveru, umožňují implementaci poměrně zajímavých věcí a právě jimi se budeme dále zabývat. V dalším popisu se také omezíme na rozhraní pro jazyk C1.
Každý filtr musí implementovat dvě základní funkce - GetFilterVersion( ) a HttpFilterProc( ).
GetFilterVersion( ) se volá při nahrávání filtru serverem. Filtr si zde může vybrat2, zda bude volán pro požadavky používajících zabezpečení SSL a/nebo pro ty nezabezpečené. Určí, jakou prioritu má modul v rámci serveru mít (malou, střední nebo velkou), a vybere si, které fáze zpracování požadavku jej zajímají. Mohou to být tyto:
čtení dat od klienta | - čtení nezpracovaných dat od klienta, tj. dat před tím, než je jádro serveru nějak zpracuje | |
předzpracování hlaviček | - volá se, když server ukončí čtení hlaviček požadavku klienta | |
mapování URL | - určení fyzického souboru pro dané URL | |
posílání dat klientovi | ||
zápis do logu | ||
ukončení požadavku | - (nově v IIS 4.0) | |
ukončení relace s klientem |
HttpFilterProc( ) volá server ve chvíli, kdy se vyskytne událost, na kterou se filtr zaregistroval. Tato funkce typicky ověřuje, jaká událost nastala, a zajistí provedení příslušné obslužné rutiny. Informace o požadavku dostane ve speciální struktuře (tzv. kontextu serveru), pomocí níž lze také zjistit informace o serveru, který požadavek zpracovává. V tomto kontextu serveru si může modul (pokud to potřebuje) uložit i svá vlastní data týkající se daného požadavku. Pro každou událost se dále v samostatné struktuře předávají pro ni specifická data.
Při vyřizování požadavku dojde postupně k jedné nebo více z následujících událostí. Některé z nich se mohou vyskytnout několikrát (např. čtení dat od klienta), jiné se vyskytnou právě jednou (mapování URL).
Modul dostane k dispozici data od klienta přesně tak, jak je klient poslal, včetně hlaviček. Modul se nesmí spoléhat na nějaké formátování (data mohou být předána v několika částech) ani na množství (může se stát, že klient posílá data po jednotlivých znacích).
Modul musí umět zvládat interakce serveru s klienty používajícími HTTP/1.1. Při POST-požadavku (například posílání obsahu formuláře) totiž nejdříve klient pošle hlavičky požadavku - dojde tedy k událostem čtení dat od klienta, předzpracování hlaviček a mapování URL. Pak server klientovi odpoví "Jsem připraven, můžeš poslat data", v řeči protokolu HTTP/1.1 "100 Continue", tzn. vyvolá se událost posílání dat klientovi. Na to klient odpoví posláním zbylých dat, tj. opět dojde k události čtení dat od klienta. Pak teprve proběhne vlastní zpracování požadavku a další vyvolání události posílání dat klientovi už znamená předávání "opravdových" dat klientovi.
Pokud filtr používá událost čtení dat od klienta, v IIS 4.0 jej nelze nasadit jen na některé virtuální servery, musí být nasazen globálně. To samé platí pro IIS 3.0, který ovšem nepodporuje jiné než globální filtry.
Tato událost je vyvolána, jakmile server přečte všechny hlavičky poslané klientem. V reakci na událost je teoreticky možno upravovat hodnotu hlaviček a dokonce některé mazat (nastavenímhlavičky na prázdný řetězec). Bohužel neexistuje rozhraní na získání seznamu všech předaných hlaviček a jediná cesta, jak je zjistit, je přes kontext serveru pomocí funkce GetServerVariable() získat hodnotu proměnné serveru ALL_HTTP. Ta obsahuje hlavičky tak, jak je poslal klient; text je třeba projít a seznam jednotlivých hlaviček zjistit "ručně".
Ale ani nastavení hlaviček z ovladače této události nemá příliš vliv na vnitřní struktury používané serverem - například ASP stránky dostanou vstupní hlavičky tak, jak je poslal klient, a nikoli tak, jak byly přenastaveny modulem.3 To dělá tuto událost prakticky nepoužitelnou.
Vyvolána ve chvíli, kdy se rozhoduje o přesném umístění souboru, na který vede URL požadované klientem. Dojde k ní vždy až po fázi předzpracování hlaviček. Modul dostane k dispozici URL a má možnost nastavit fyzickou cestu k souboru. Tolik teorie.
Bohužel ale tato událost není vyvolána, pokud jádro serveru usoudí, že soubor, na něž ukazuje URL, neexistuje. Nelze tedy například používat nějaké předpony URL pro rozhodování, co a jak se má předat. V IIS 3.0 a 4.0 je navíc vážná chyba, která se projevuje právě pokud některý z modulů používá mapování URL4. Obecně tedy bohužel ani tahle událost není příliš v praxi využitelná.
Data jsou opět předávána bez jakéhokoli formátování, včetně úvodních hlaviček. Modul se nesmí spoléhat na to, že hlavičky dostane najednou (to sice obvykle - např. pro ASP - platí, ale neplatí například pro výstup z FrontPage extensions). Pokud chce data zpracovávat, modul musí umět zpracovat chunked Transfer-Encoding, ve kterém se data předávají HTTP/1.1 klientům. Modul také nemůže předpokládat, že první posílání dat klientovi znamená posílání konečné odpovědi klientovi; jak už bylo uvedeno u popisu čtení dat od klienta, nemusí to tak být vždy.
Vyvolá se na konci zpracování požadavku a slouží pro změny logovací zprávy. Mně osobně přijde tato možnost poměrně neužitečná. Lepší by byl interface k serveru, kterým by se dal formát logu nastavit podobně jako v serveru Apache.
Tato událost je implementována pouze pod IIS 4.0 (pokusně zjištěno, že potřebná verze API je 262144). Dá se využít například pro uvolnění prostředků, které modul použil pro zpracování požadavku. Pod IIS 3.0 je nutné ke stejnému účelu využít událost zápis do logu.
Opravdu velmi smutné je to, že se tato událost za určitých nedefinovaných okolností5 nezavolá a příslušné vyčištění struktur modulu je třeba zajistit při začátku dalšího požadavku. Vzhledem k tomu, že server modulu nedává nijak najevo, kdy přesně začíná zpracování dalšího samostatného požadavku klienta, musí si tento fakt modul ošetřovat sám, což je náchylné na chyby.
Volá se ve chvíli, kdy se ruší spojení s klientem, po zpracování jednoho či více požadavků. Lze využít pro vyčištění struktur modulu použitého při obsluze klienta.
DLL může implementovat funkci DllMain() (nezapomenout exportovat!). Ta je volána vždy, když je sdílená knihovna modulu serverem nahrána, přestane se používat nebo když v rámci serveru vznikají/zanikají nová vlákna. Lze tak na dané události zareagovat a kupříkladu alokovat potřebné prostředky, ať už globální či pro daná vlákna lokální (pomocí funkcí Tls*()).
Sympatické na ISAPI je to, že spoustu věcí přes toto rozhraní (pravda po nemalém úsilí) lze uskutečnit - např. Apache podporu pro filtry zatím nemá žádnou. Na druhou stranu, u Apache je k dispozici zdrojový kód, a když člověk nějakému chování nerozumí, prostě si to přečte v kódu. Tato možnost u IIS hodně chybí.
Je smutné, že dokumentace přibalená k MS VC++ 5.0 je totálně nedostatečná. Dává sice poměrně podrobné informace o použitých strukturách a konstantách, ale vůbec neřeší interakce s klienty a návaznost jednotlivých fázi zpracování požadavku. Ani na WWW stránkách MS se ohledně ISAPI nedá nalézt mnoho hodnotných informací.
Dále je nešťastné, že i v tomto případě je vidět nedotaženost tak vlastní produktům Microsoftu - solidní myšlenka, na papíře či monitoru to všechno vypadá dobře, ale pak to ztroskotává na implementaci a při bližším ohledání člověk zjišťuje, že je to téměř nepoužitelné6.
Co bych na závěr vytkl, je příliš malá abstrakce klienta - ISAPI modul se musí starat o příliš mnoho věcí, které by stejně dobře a lépe mohlo zvládnout jádro IIS. Nakonec to dopadá tak, že ISAPI filtr je do značné míry malým WWW serverem uvnitř IIS, což rozhodně není optimální.
Co dodat? Je dobře, že ISAPI existuje. Pokud používáte IIS, potřebujete dosáhnout maximálního výkonu a napsat něco v C/C++, není problém, je to ta pravá cesta. Jako vždy, je nutné si předem rozmyslet, jestli se investice do vývoje ISAPI modulu vyplatí - pro valnou většinu aplikací bude pravděpodobně ASP či něco podobného lepší volbou. Pokud ale potřebujete z aplikace dostat maximální výkon7, máte kam jít.
1 | Pro C++ se spokojíme s prohlášením, že C++ rozhraní
pro ISAPI pracuje jen s minimálním objektovým obalem rozhraní pro jazyk C,
rozdíly nejsou nijak zásadní. Případný zájemce potřebné informace
snadno nalezne v dokumentaci ISAPI.
... zpět do textu |
2 | Výběr je provádí nastavením příslušného
příznaku SF_NOTIFY_*. Přesné názvy konstant viz
dokumentace ISAPI, zde se spokojíme s jejich opisem.
... zpět do textu |
3 | Pro zvídavé: hodnotu oné proměnné ALL_HTTP
nelze z modulu měnit.
... zpět do textu |
4 | Pokud za URL na ASP skript omylem přidáte lomítko,
soubor samozřejmě není nalezen (neexistuje). IIS si ale
tuto chybu zapamatuje a hlásí pak Soubor nenalezen
pro další požadavky i pro URL bez toho lomítka. Bližší informace viz
článek p. Dočekala na Světu namodro Snadné DoS pomocí
přebytečného lomítkaz 21.5.1999.
... zpět do textu |
5 | ...nebo poměrně záhadných - např. v druhé
fázi autentifikace typu NTLM
... zpět do textu |
6 | Přesně tak, aby člověk musel investovat mnohem
větší úsilí, než původně chtěl, ale přitom je jasné, že danou
věc je možné uskutečnit.
... zpět do textu |
7 | ...a přechod na Unix/Apache pro vás není
uskutečnitelná volba
... zpět do textu |