Autor: Ing. Dalibor Šrámek, email: dali@kumbal.vse.cz
Úvodem
Tento text vznikl, jelikož mě několik přátel požádalo o radu
při tvorbě CGI skriptů a nechtělo se mi to každému zvlášť opakovat. Pro jeho
uspěšné strávení se předpokládají obecné znalosti operačního systému a
programování. Konkrétní příklady jsou z prostředí Unixu. Obecné informace lze
většinou využít i na jiných platformách.
Ručení omezené, zmíněné v podtitulu, se vztahuje zejména na tři skutečnosti:
Klasickým příkladem jsou počítadla přístupu na WWW stránky, gatewaye k různým slovníkům a databázím nebo třeba obměňující se reklamy na WWW. Následující odkazy vedou k ukázkám CGI skriptů na serveru Kumbál, které dokumentují některé ze způsobů využití:
Je také třeba zajistit, aby CGI skript byl spustitelný pro WWW server. V Unixovém prostředí běží WWW server obvykle s minimálnimi právy (např. uživatel nobody), proto musí mít skript nastavená práva pro spouštění jakýmkoliv uživatelem.
Abyste se vyhnuli zbytečnému hledání chyby ve skriptu, když ve skutečnosti je špatně nakonfigurovaný server, doporučuji nejdříve zkusit zcela jednoduché skripty - ideálně například níže uvedený výpis proměnných prostředí.
Určitou možností, jak spouštět skripty na serveru je také SSI.
Výstup z CGI skriptu
Jak již bylo uvedeno, je CGI skript v podstatě
normální program. V unixovém prostředí to může být třeba běžný skript shellu,
program v jazyce Perl nebo program zkompilovaný v nějakém z klasických jazyků
(C, Pascal...).
Chcete-li, aby CGI skript měl nějaký výstup, musí jej zapisovat na zařízení STDOUT (standardní výstup - čili obvykle obrazovka). Následující příklad ukazuje velmi jednoduchý skript, který vypisuje datum (příklady jsou psány pro Bourne shell a jemu podobné).
#!/bin/sh echo Content-type: text/plain echo datePrvní řádek určuje, jak se bude CGI skript interpretovat - konkrétně říká, že skript se má spustit pomocí shellu /bin/sh. Pokud je CGI skript napsaný v interpretovaném jazyce (shell, Perl), je nutné tento řádek uvádět. Pro kompilované programy pochopitelně nemá smysl.
Nazvete-li tento skript např. date.cgi a budete se na něj odkazovat linkem http://mujserver/mojecesta/date.cgi, server skript vykoná a výsledek v podobě aktuálního data zašle klintovi.
Drobnou změnou hlavičky docílíte možnosti formátovat výstup pomocí HTML.
#!/bin/sh echo Content-type: text/html echo echo "<HTML>" echo "<BODY>" echo "<H3>Aktuální datum a cas</H3>" echo "<B>" date echo "</B>" echo "</BODY>" echo "</HTML>"HTML tagy se vypisují také na standardní výstup (povšimnete si, že v příkladu jsou uzavřeny do uvozovek, aby se zabránilo interpretaci speciálních znaků shellem).
Ještě poznámka o standardních I/O zařízeních pro nepříliš zkušené
programátory. Se zařízením STDIN a STDOUT se obvykle pracuje těmi nejjednodušími
rutinami vstupu a výstupu. V jazyve C jsou to například printf a
puts, v Pascalu pak writeln.
Hlavička CGI skriptu
Jak jsme viděli, musí CGI skript vypisovat na
STDOUT hlavičku pro klienta, která sestává minimálně z jednoho řádku a je
ukončena prázdným řádkem. Podívejme se, co více se dá ještě s hlavičkou
dosáhnout.
#!/bin/sh echo Content-type: text/html echo Pragma: no-cache echoTato hlavička (přesněji pridaný druhý řádek) říká klientovi, že dokument nemá uchovávat ve své paměti cache, což je u proměnlivých výstupů z CGI skriptů velmi důležité.
Jinou možností je vrátit klientovi stavový kód:
#!/bin/sh echo Content-type: text/html echo Status: 200 OK echoPoužitelné kódy jsou zejména:
Samozřejmě, že ne všechny prohlížeče tyto kódy správně interpretují, ale v dnešní době jich už bude většina.
To co se vypisuje v uvedených případech nestačí jako kompletní hlavička HTML
dokumentu. Server vždy doplní další hodnoty (nezadáte-li stavový kód, server jej
vloží). Některé servery umožňují nakonfigurovat tak, že výstupy zadaných skriptů
již nejsou kontrolovány (Non Parsed Headers), což může vést ke zvýšení rychlosti
odezvy. Musíte však ve skriptu pečlivě generovat kompletní hlavičku.
Jakým jazykem psát
Ačkoliv příklady v tomto textu jsou psány jako
skripty pro unixovský shell, není takový postup obecně vhodný. Důvodem je
nepříliš velká efektivita, která znamená vyšší zátež serveru při spouštění (ve
výse zmíněném skriptu, který vrací aktuální stav serveru, se například spouští
více než 50 procesů).
Pro méně často spouštěné CGI skripty se doporučuje používat (v unixovém prostředí) jazyk PERL, který obsahuje mimo jiné velmi mocné nástroje pro práci s textem. Často spouštěné skripty je optimální vytvořit v nějakém kompilovaném jazyce. C a C++ sice neposkytují při programování skriptu tak silné prostředky, to je však vyváženo efektivitou provádění.
Při troše snahy lze dnes na Internetu nalézt slušné množství knihoven pro C a
C++, které umožňují pohodlné použití těchto jazyků pro psaní CGI skriptů.
Prostředí CGI skriptu
Při spuštění CGI skriptu jsou nastaveny různé
proměnné prostředí, ze kterých lze zjistit zajímavé informace. Vyzkoušejte
následující příklad.
#!/bin/sh echo Content-type: text/plain echo echo "Ukázka některých proměnných:" echo "REMOTE_HOST = $REMOTE_HOST" echo "REMOTE_USER = $REMOTE_USER" echo "QUERY_STRING = $QUERY_STRING"Předřazení znaku "$" oznámí shellu, že se jedná o proměnnou prostředí. Zavoláte-li tento skript, měli byste obdržet aktuální hodnoty adresy klienta a jméno uživatele (pokud máte unixového klienta).
#!/bin/sh echo Content-type: text/plain echo echo SERVER_SOFTWARE = $SERVER_SOFTWARE echo SERVER_NAME = $SERVER_NAME echo GATEWAY_INTERFACE = $GATEWAY_INTERFACE echo SERVER_PROTOCOL = $SERVER_PROTOCOL echo SERVER_PORT = $SERVER_PORT echo REQUEST_METHOD = $REQUEST_METHOD echo HTTP_ACCEPT = "$HTTP_ACCEPT" echo PATH_INFO = "$PATH_INFO" echo PATH_TRANSLATED = "$PATH_TRANSLATED" echo SCRIPT_NAME = "$SCRIPT_NAME" echo QUERY_STRING = "$QUERY_STRING" echo REMOTE_HOST = $REMOTE_HOST echo REMOTE_ADDR = $REMOTE_ADDR echo REMOTE_USER = $REMOTE_USER echo AUTH_TYPE = $AUTH_TYPE echo CONTENT_TYPE = $CONTENT_TYPE echo CONTENT_LENGTH = $CONTENT_LENGTHA abychom okusili také z jiného soudku, tak teď funkčně podobný skript v PERLu.
#!/usr/bin/perl print "Content-type: text/html\n\n"; while (($key, $val) = each %ENV) { print "$key = $val \n"; }Jak poznají i ti, kteří PERL nikdy neviděli, vypisuje tento skript v cyklu všechny proměnné prostředí, které jsou definovány. Funkční variantu si můžete vyzkoušet na serveru Kumbál.
<HTML> <BODY> <FORM ACTION="/mojecesta/mujskript.cgi" METHOD="GET"> Zadejte prosím jméno: <INPUT TYPE="text" NAME="jméno"> <BR> Zadejte prosím příjmení: <INPUT TYPE="text" NAME="prijmeni"> <BR> <INPUT TYPE="submit" VALUE="odeslat"> </FORM> </BODY> </HTML>Hodnota ACTION u tagu formuláře určuje skript, který se spustí po odeslání formuláře. Hodnota METHOD určuje způsob, jakým budou data z formuláře skriptu předána. V našem případě (metoda GET) bude daty naplněna proměnná QUERY_STRING. Formát proměnné bude následující: jmeno=zadanejmeno&prijmeni=zadaneprijmeni
Aby to bylo složitější, jsou speciální znaky vyskytující se v hodnotách proměnných zakódovány následujícím způsobem:
Metoda GET má jak je zvykem své výhody a nevýhody. Výhodou je, že klient zasílá data pro CGI skript v URL. Pro výše uvedený formulář se bude volat URL: /mojecesta/mujskript.cgi?jmeno=zadanejmeno&prijmeni=zadaneprijmeni. Vidíme, že takový skript lze volat i ručně bez vyplňování formuláře. Řekněme, že bychom chtěli vytvořit skript, který by pracoval jako anglicko-český slovník. Vstup bychom mohli zabezpečit například následujícím formulářem.
<HTML> <BODY> <FORM ACTION="/mojecesta/slovnik.cgi" METHOD="GET"> Zadejte prosím hledané slovo: <INPUT TYPE="text" NAME="slovo"> <BR> <INPUT TYPE="submit" VALUE="odeslat"> </FORM> </BODY> </HTML>Stejně dobře by ale tento slovník šel použít přímo zápisem: http://muj.server.cz/mojecesta/slovnik.cgi?slovo=meslovo.
To, že se data předávají jako součást URL je však zároveň nevýhodou metody
GET. Prohlížeče totiž neumožňují neomezenou délku URL a tak se metoda GET nehodí
pro větší objemy dat. Nezanedbatelné není ani to, že tyto řetězce se obvykle
uschovávají v logu například na proxy serveru, pokud jej používáte, což znamená
snížení již tak malého soukromí.
Vstup metodou POST
Omezení množství dat vstupujících do CGI skriptu
odstraňuje metoda POST. CGI skriptu jsou v případě použití metody POST data
směřována na standardní vstup. Skript může využít proměnnou prostředí
CONTENT_LENGTH, která obsahuje celkovou délku dat v bytech. Postup je pak
takový, že se zjistí z proměnné počet bytů k načtení a čte se ze standardního
vstupu.
Dlouho jsem na tomto místě sliboval příklad - teď tady konečně je a abych zabil více much jednou ranou, tak je v jazyce C. Ve formě, kterou vidíte, by jej mělo jít bez problémů zkompilovat na Unixu. V jiných OS budou možná malinko jiné potřebné hlavičkové soubory a nahrazení funkce strcasecmp, která porovnává řetězce bez ohledu na velikost písmen.
Funkce příkladu:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char *method; char *data; /* vypis hlavicky */ puts("Content-type: text/plain\n"); /* zjisteni zpusobu zaslani dat */ method=getenv("REQUEST_METHOD"); /* kontrola definice promenne */ if (method==NULL) { puts("Chyba: neni definovana promenna REQUEST_METHOD!"); exit(1); } /* zkopirovani QUERY_STRINGu, je-li metoda GET */ if (!strcasecmp(method,"GET")) { data=strdup(getenv("QUERY_STRING")); } /* nacteni dat ze STDIN, je-li metoda POST */ if (!strcasecmp(method,"POST")) { int length; length=atoi(getenv("CONTENT_LENGTH")); data=malloc(length+1); /* cteni daneho poctu bytu */ fread(data,length,1,stdin); data[length]=0; } /* pokusny vypis datoveho retezce */ puts(data); /* uvolneni pameti a konec */ free(data); return(0); }
Další prvky formuláře si uvedeme na příkladu:
Zdrojový kód formuláře si můžete prohlédnout, pokud zvolíte v klientovi zobrazení zdrojového kódu stránky. Kód CGI skriptu pro výpis proměnných je velmi jednoduchý:#!/bin/sh echo "Content-type: text/plain" echo echo "Výpis proměnných formuláře:" echo echo "$QUERY_STRING" | tr "&" "\n"Vidíme, že pro tento skript je nutné zadávat data metodou GET. Filtr tr pak nahradí všechny znaky ampersand znakem konce řádky. Zaslaná data můžete také vypsat pomocí skriptu uvedeného jako příklad u metody POST.
Vraťme se ještě jednou k políčkům fomuláře v příkladu:
SSI umožňují například vložit do stránky datum její poslední úpravy nebo
výstup zadaného příkazu nebo CGI skriptu. Obecný formát vsuvky je
následující:
<!--#příkaz parametr1=hodnota parametr2=hodnota ...
-->
Použitelné příkazy a jejich parametry: