Webový server lapache Abstrakt lapache (lukas light apache) je jednoduchý UNIXový webový server podporující: podmnožinu HTTP 1.0/1 protokolu virtuální servery (s vlastními chybovými stránkami a kořenem dokumentů) vyhledávání indexových souborů (pokud v URI není zadána celá cesta) výpis adresářů (pokud neexistuje žádný indexový soubor) dynamické stránky (libovolně rozšiřitelné pomocí dynamických knihoven) Ovládání Požadavky Operační systém Linux nebo Solaris s nainstalovaným překladačem gcc. Instalace Po rozbalení archivu do adresáře, kam program chcete nainstalovat, v něm spusťte příkaz make. Na Solarisu musíte použít pro kompilaci jiný konfigurační soubor programu make, a to MakefileSolaris. Kompilaci provedete příkazem make f MakefileSolaris. Spuštění aplikace Hlavní program se jmenuje lapache. Spouští se tedy příkazem./lapache. Seznam nepovinných parametrů, defaultní hodnoty jsou uvedeny v závorkách: nastavení parametrů spojení o p port nastaví číslo portu (80) o b backlog nastaví maximální počet čekajících spojení (10) o s connections nastaví počet spouštěných procesů - současně obsluhovaných klientů (5) o c configfile nastaví soubor s nastavením (lapache.conf) nastavení výstupních souborů o -e errorfile nastaví výstup chybových hlášek do souboru errorfile (chybový výstup) o -l logfile nastaví výstup logových hlášek do souboru logfile (standardní výstup) o -o outfile nastaví stadnardní výstup do souboru outfile (standardní výstup) Konfigurační soubor Konfigurační soubor je řádkově orientovaný. Na pořadí řádek nezáleží. Vždy první slovo na řádce je klíčové (příkaz), další slova slouží jako jeho parametry. Mezi parametry a příkazem může být libovolný počet mezer a tabelátorů. Řádka začínající znakem # je ignorována (slouží jako komentář). V klíčových slovech nezávisí na velikosti písmen.
Seznam příkazů, defaultní hodnoty jsou uvedeny v závorkách: Port N stejné jako přepínač -p ProcessCount N stejné jako přepínač -s ProcessLife N nastaví životnost jednoho procesu na N obsloužených klientů. Slouží k tomu, aby nedocházelo ke ztrácení paměti (500) ErrorFile F stejné jako přepínač -e LogFile F stejné jako přepínač -l OutputFile F stejné jako přepínač -o Index Name1 [Name2 [...]] nastaví jména souborů Name1, Name2... jako indexové soubory Extension Lib Regexp1 [Regexp2 [...]] pokud URI (požadovaný soubor) odpovídá jednomu ze zjednodušených regulárních výrazů, pak se pro její zpracování použije dynamická knihovna Lib o jediný speciální znak je zde * - může se vyskytovat na začátku, konci nebo na začátku i konci slova a pak znamená, že URI musí končit (resp. začínat, či obsahovat) dané slovo DocumentRoot P nastaví P jako cestu hlavní adresář webového serveru (.) ErrorDocument E F nastaví soubor F jako chybový soubor pro zpracování HTTP chyby E (pouze pro chyby 403, 404 a 500). Cesty se považují za relativní k hlavnímu adresáři virtuálního serveru (na začátek však uvádějte /). Pro zadání absolutní cesty začněte znaky //. VirtualHost Name vytvoří nový virtuální server pro doménu Name. Až do příkazu EndHost jsou všechna nastavení načítána do tohoto virtuálního serveru. Použitelné příkazy jsou DocumentRoot a ErrorDocument (viz. výše). Všechny nenastavené věci dědí virtuální server od svého otce. EndHost ukončí nastavení aktuálního virtuálního serveru Ukázkový konfigurační soubor lapache.conf Port 8080 ProcessCount 2 ProcessLife 100 LogFile /dev/null Index index.htm index.html index.php Extension liblcgi.so /cgi-bin/* *.sh *.cgi DocumentRoot /home/www/www ErrorDocument 404 /error_404.html ErrorDocument 403 /error_403.html VirtualHost lapache DocumentRoot /home/www/lapache ErrorDocument 404 /404.html EndHost VirtualHost secondhost DocumentRoot /home/www/blabla ErrorDocument 404 /404.html ErrorDocument 403 /404.html EndHost Ovládání za běhu Aplikace se za běhu nijak ovládat nedá. Ukončujte ji posláním ukončovacího příkazu. Například Ctrl+C.
Programátorská dokumentace lapache je naprogramován v jazyce C a používá UNIXové API. Zdrojové soubory main.c hlavní soubor, vytváří servery, přijímá spojení, čte požadavek a vytváří odpověď (popřípadě předá řízení dynamické knihovně) a nakonec zapíše vrátí odpověď klientovi common.h všude používané konstanty message.h (c) struktura HTTP zprávy a poskytují funkce na práci s nimi (vyčitění, vypsání na obrazovku i do socketu) settings.h (c) struktura nastastavení a všechno okolo toho čtení parametrů příkazové řádky, konfiguračního souboru (včetně načítání dynamické knihovny) host_settings.h (c) struktura a funkce pro uchování nastavení jednotlivých virtuálních serverů extension.h (c) uchování nastavení dynamicky načítaných souborů funkce pro přidávání další dynamické knihovny a pro předání požadavku správné knihovně (té, které odpovídá URI) str.h (c) pomocné funkce pro práci s řetězci (úprava URI...) liblcgi.c dynamická knihovna pro práci s CGI skripty předá parametry požadavku do skriptu (GET jako proměnné prostředí, POST na standardní vstup), spustí jej a jeho výstup uloží do odpovědi. O zapsání odpovědi do socketu se už postará hlavní program. Makefile konfigurační skript pro make pro přeložení aplikace Jak server funguje Po spuštění se načte nastavení (nejdříve z příkazové řádky, následně ze souboru). Poté se vytvoří server na požadovaném portu (socket, který na něm naslouchá). Poté se program několikrát fork-ne, aby byl dostatečný počet procesů zpracovávajících příchozí spojení. Hlavní program se dále pouze o udržování tohoto počtu. Synovským procesům zůstane socket, na kterém se uspí v čekání na nové spojení od klienta. Poté, co ho obdrží, přečtou všechna data a pokusí se najít odpověď. Při hledání odpovědi může nastat několik možností: požadavek je neplatně formulován vytvoří se chybová hláška cílem požadavku je adresář o v adresáři není žádný indexový soubor vytvoří se seznam souborů a podadresářů a jako HTML se pošle klientovi o je nalezen indexový soubor první specifikovaný v konfiguračním souboru bude použit v dalším bodu cílem požadavku je soubor o zjistí se, zda URI odpovídá některému z regexpů pro dynamické knihovny, pokud ano, tak se řízení předá knihovně o pokud ne, tak je soubor načten do odpovědi pokud není daný soubor (adresář) nalezen (popřípadě k němu nemá server právo přístupu), poté se server pokusí načíst z disku chybovou stránku (pro ni platí stejná pravidla jako pro URI) o pokud nastala chyba i při hledání chybové stránky, tak se klientovi vrátí defaultní chybová hláška pro původní chybu před načítáním (a vracením) nedynamického objektu se ještě kontroluje (pokud přišlo v hlavičce požadavku If-Modified-Since), zda klient nemá nejaktuálnější verzi stránky, v tom případě se mu pouze pošle zpráva 304 Not Modified.
Po načtení celé odpovědi do paměti se vypočítají hodnoty dalších HTTP hlaviček a pak se celá odpověď zapíše do socketu. Pokud je spojení vytvořeno pomocí verze HTTP 1.0, pak je spojení ukončeno. V opačném případě server čeká na další požadavky od klienta. Vytváření vlastní dynamické knihovny Stačí vytvořit dynamickou knihovnu s void funkcí load_response, která jako parametr bere dvě struktury zprávy. V první je požadavek a do druhé má za úkol zapsat odpověď. Takže hlavička Vašeho programu by měla vypadat nějak takto: #include "message.h" void load_response(struct Message * msg, struct Message * response) { /* sem přijde kód */ } Ještě uvedu definici struktury zprávy i s popisem jednotlivých položek a pár zajímavých funkcí: struct Message { char * text, /* text požadavku */ * client_ip, /* IP adresa uživatele */ * client_host, /* jméno uživatelského PC */ server_port[6]; /* port serveru v textové podobě */ ssize_t len, /* délka textu ve znacích */ size, /* velikost bufferu text */ pos; /* pozice v textu políčko navíc pro pohyb */ int cnt, /* počet zpracovaných zpráv touto strukturou */ read; /* nakolikrát byl text načten ze socketu */ double size_avg; /* průměrná délka textu */ struct Header header; /* HTTP hlavičky */ /* hlavička */ struct Header { char uri[path_len], /* požadovaná URI znormalizovaná */ params[buf_len], /* parametry GET bez? */ headers[header_cnt][path_len]; /* další hlavičky index viz. dále */ int version, /* verze HTTP 0 nebo 1 */ method, /* způsob poslání: 0 - GET,1 POST */ result; /* číslo HTTP odpovědi: 200,404,... */ /* tyto konstanty indexují pole headers ve struktuře header. Znamenají vždy to, jak se jmenují */ enum HeaderTypes { HT_ContentEncoding = 0, HT_ContentLanguage = 1, HT_ContentLength = 2, HT_ContentType = 3, HT_Date = 4, HT_Host = 5, HT_IfModifiedSince = 6, HT_LastModified = 7, HT_Location = 8, HT_UserAgent = 9, HT_Server = 10 /* použitelné funkce */ int
message_fill_environ(struct Message * msg); /* vyplní proměnné prostředí vším, co je ve zprávě msg: * zapíše GET parametry, jméno skriptu, velikost POST dat (pokud jsou), způsob předání dat (GET, POST), jméno serveru, protokol a další informace * vrací 1, pokud je potřeba zapsat nějaká POST data, jinak vrací 0 */ Toto je asi vše, co se dá použít při spouštění dynamických knihoven. Úkol dynamické knihovny je načíst odpověď do proměnné response->text (včetně případných HTTP hlaviček) a uložit její délku do response->len. Při změně velikosti bufferu ještě uložit novou velikost do response->size. Druhým úkolem je vyplnit návratovou hodnotu HTTP Result Code do proměnné response->header.result. Pokud vyplníte 200, tak se zpráva, tak jak je, okopíruje na výstup. Při vyplnění jiného čísla se pošle správná chybová stránka.