Autor: Dalibor Šrámek, email: dalibor.sramek@insula.cz, Poslední úprava: 8. února 1998.
Ú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í:
(Příklady na Kumbále jsou již bohužel mimo provoz. Pokusím se zprovoznit
nějaké nové.)
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 "<h2>Aktuální datum a cas</h2>" 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 těchto stránkách.
<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: