Scripturi CGI
Pagini Web generate dinamic
Autor :Péter Csaba
Calea cea mai directa catre programarea Web o reprezinta scripturile
CGI. Însa pentru a putea scrie astfel de programe sunt necesare
câteva cunostinte de baza privind protocolul HTTP si modul în
care serverul Web ruleaza scripturi CGI.
Cu aproape trei ani în urma, am prezentat - ca programator Perl
- ideile de baza privind modul în care se scriu în Perl scripturi
CGI care sa fie sigure din punct de vedere al securitatii serverului pe
care ruleaza (vezi "Byte România", Iulie 1997, http://www.byte.ro/
byte97-07/perl.htm). Mai târziu, am prezentat si câteva scripturi
CGI, fiind convins ca majoritatea cititorilor stiu cum functioneaza si
ce sunt de fapt scripturile CGI.
În ultima vreme, politica a devenit un subiect destul de discutat,
asa ca am pus un sondaj de opinie într-una dintre paginile întretinute
de mine, sub titlul "Cine va câstiga alegerile din 2000?".
Sondajul a avut succes - pâna în momentul de fata peste 6000
de persoane si-au exprimat optiunea politica. Dupa putin timp, am primit
mai multe scrisori în care persoanele respective mi-au cerut sa
le dau programul care sta în spatele acestui sondaj, pentru ca si
ei au pagina pe Web si vor sa introduca un sondaj asemanator. Le-am trimis
programul, un script gratuit si extrem de usor de configurat.
Au urmat mai multe întrebari de la cei carora le-am trimis programul.
Ma întrebau ce trebuie sa faca, pentru ca scriptul nu functioneaza
iar în navigator apare sursa programului în loc de ceva inteligibil.
Dupa alte câteva scrisori, mi-am dat seama ca putini sunt cei care
stiu ce este un script CGI si cum functioneaza acesta. Acest articol îsi
propune sa clarifice subiectul în discutie.
Foarte pe scurt
Am început sa învat limbajul Perl pentru ca am vrut sa creez
pagini interactive pentru Web. Programele care fac ca Web-ul sa fie dinamic
sunt adesea numite "scripturi CGI" pentru ca folosesc o interfata
standard, despre care se presupune ca este suportata de toate serverele
Web, interfata numita CGI - Common Gateway Interface. (Am spus "se
presupune" pentru ca înca nu am întâlnit un server
Web care sa nu aiba suport pentru CGI dar, pe de alta parte, nu pot pretinde
ca am verificat toate serverele Web existente la ora actuala.) CGI defineste
un mediu în care orice proces este rulat de catre serverul de Web.
Un proces rulat de server poate fi scris în orice limbaj de programare.
Vorbim despre "scripturi CGI" pentru ca de obicei ne este mai
usor sa le scriem în limbaje interpretate, numite "limbaje
de scripting". Interfata CGI redirecteaza datele de intrare catre
un proces rulat de server si intercepteaza datele de iesire ale acestuia.
Acest articol va examina interfata CGI si modul cum se integreaza aceste
scripturi în sistem.
Protocolul HTTP
Înainte de a urmari modul cum functioneaza un script CGI, trebuie
sa ne oprim putin asupra protocolului HTTP (Hypertext Transfer Protocol),
care este metoda fundamentala de comunicatie prin Web. Sarcina unui server
Web este sa raspunda la o cerere HTTP primita de la client. Daca cererea
este pentru un fisier - continând poate cod HTML, o imagine, un
film sau sunet - atunci serverul va localiza fisierul si îl va trimite
catre client.
HTTP este un protocol text, mesajele fiind alcatuite dintr-o linie initiala
- care poate fi o cerere initiata de un navigator (browser) sau o linie
de raspuns de la server - urmata de mai multe date. Prin "mai multe
date" se poate întelege un fisier, iar tipul informatiei din
fisier este specificat folosind conventia adoptata pentru MIME (Multimedia
Internet Mail Extension).
MIME a aparut din nevoia de a trimite alte informatii
decât text simplu într-o scrisoare electronica. Acest protocol
a fost preluat de catre HTTP ca o tehnologie convenabila pentru a trata
diferite tipuri de fisiere. Spre deosebire de multe alte protocoale folosite
în Internet, HTTP este bidirectional. De exemplu, în marea
majoritate a cazurilor, mesajul contine un fisier care va fi trimis clientului
de catre server si MIME este folosit de programul client pentru a afla
cum sa afiseze fisierul. Desigur, si clientul poate sa trimita un fisier
catre server folosind acelasi protocol.
Servere Web
Multe servere Web sunt configurate în asa fel încât
atunci când o cerere HTTP de la navigator solicita un fisier cu
extensia cgi, fisierul nu va fi trimis. În schimb, serverul va verifica
daca programul respectiv este executabil si, în caz afirmativ, va
rula programul, despre care se poate presupune ca are sarcina sa genereze
date de iesire corespunzatoare. Serverul Web poate fi configurat si în
asa fel încât o cerere catre un fisier aflat într-un
director anume sa se execute ca un script. Acest director special este
numit de obicei cgi-bin.
Serverele Web au adoptat aceste "directoare speciale" din motive
de securitate. Este periculos sa permitem utilizatorului Mitica sa lanseze
programe pe masina noastra - chiar daca este vorba de propriile noastre
programe - deci majoritatea serverelor limiteaza accesul la scripturile
CGI. Este un obicei bun sa ascundem scripturile în asa fel încât
acestea sa fie executabile, dar sa nu poata fi descarcate. Eu de obicei
pun scripturile în afara directoarelor din care se pot descarca
fisiere.
Desigur, ascunderea scripturilor nu ne asigura o securitate maxima, deoarece
prietenul apropiat al lui Mitica, George Distrugatorul, poate sa analizeze
scripturile si sa gaseasca gauri de securitate în acestea.
Schimbul de informatii
Când scriem scripturi CGI, cream de fapt un program care va rula
ca un subproces al serverului Web. Trebuie sa gasim o modalitate pentru
ca scriptul nostru sa primeasca informatii de la server. Exista câteva
metode care sunt folosite pentru a transmite informatiile catre scriptul
care ruleaza. Prima modalitate la care ma gândesc este folosirea
parametrilor. De fapt, scripturile CGI nu folosesc parametri asa cum sunt
folositi în alte programe, poate pentru ca aceasta modalitate a
fost considerata "prea UNIX", sau pentru ca standardele pentru
transmiterea de parametri catre programe sunt prea restrictive.
A doua metoda de a transmite date catre procesul fiu este prin "variabile
de mediu": o serie de perechi de forma nume=valoare sunt mostenite
de procesul fiu de la parinte în momentul în care procesul
fiu este creat. UNIX foloseste de obicei variabile de mediu pentru a pastra
informatiile utile în context global, punându-le astfel la
dispozitia proceselor.
Structura nume=valoare a variabilelor de mediu se potriveste foarte bine
cu datele care vin din formularul (form) pe care utilizatorul l-a încarcat
în navigator. Fiecarui câmp de intrare de pe formular poarta
un nume si va primi si o valoare furnizata de catre utilizator. Valoarea
trebuie sa fie legata de numele câmpului si perechea sa fie trimisa
serverului Web.
În fine, o alta metoda de a trimite date catre procesul fiu este
sa scriem informatiile într-un fisier si sa specificam procesului
fiu sa citeasca datele din acesta. Aceasta tehnica este frecvent utilizata
în redirectarea de tip shell. De exemplu, comanda:
$ ls-l /bin | more
va determina shell-ul sa lanseze comanda ls si sa redirecteze rezultatul
comenzii catre o intrare standard a comenzii more. Comanda more primeste
de fapt un descriptor al unui fisier deschis, din care sa citeasca datele.
Variabile de mediu
Când un client trimite serverului Web o cerere care are drept rezultat
executia unui script, serverul va preîncarca mai multe variabile
de mediu, pe care scriptul CGI le va mosteni si pe care le va putea consulta.
Anumite variabile de mediu sunt considerate parti "standard "
ale protocolului CGI. Serverul poate sa defineasca si alte variabile de
mediu. Eu folosesc des un script CGI de tip shell simplu, care contine
comanda set pentru a vedea variabilele de mediu care sunt returnate de
server, de obicei pentru a verifica daca stiu denumirea corecta a unei
anumite variabile (vezi mai jos).
Nu voi enumera variabilele aici, dar voi vorbi despre ele în general.
Aceste variabile pot fi împartite în trei categorii.
În primul rând, sunt variabile care comunica scriptului date
despre serverul pe care ruleaza. De exemplu, scriptul poate afla numarul
portului pe care îl foloseste.
În al doilea rând, sunt si câteva variabile care furnizeaza
scriptului informatii de baza despre programul client (de obicei un browser
Web) si despre sistemul care a formulat cererea pentru date. Astfel, de
exemplu, scriptul poate sa identifice adresa IP a clientului sau tipul
navigatorului. Dar pe de alta parte, nu poate identifica nici persoana,
nici adresa ei de e-mail.
În al treilea rând, sunt variabile care contin informatii
despre tranzactia curenta. Precum am mai spus, HTTP foloseste protocolul
MIME pentru a schimba informatii. Tipul MIME este pus la dispozitia scriptului
în variabila CONTENT_TYPE.
GET si POST
Exista doua variante de a trimite catre serverul Web informatia culeasa
într-un formular care apeleaza un script CGI: GET si POST. Parametrul
METHOD specifica alegerea facuta la implementarea formularului în
pagina Web. Fiecare metoda foloseste o cale diferita pentru a trimite
informatiile din formular la server, în momentul în care butonul
submit (trimite) este apasat.
Denumirile GET si POST se refera la comenzi din protocolul HTTP. Când
navigatorul cere o pagina obisnuita, trimite o cerere de genul:
GET /main.html HTTP/1.1
Cererea de mai sus îi solicita serverului pagina /main.html si
îi indica faptul ca este capabil sa faca fata versiunii 1.1 a protocolului
HTTP. Daca totul este ok, atunci serverul va raspunde cu un mesaj care
contine datele cerute.
Când navigatorului i se spune sa trimita datele din formular folosind
o cerere de tip GET, acesta va trimite cererea adaugând la sfârsitul
URL-ului un semn de întrebare si continutul formularului. Se creeaza
un URL, extins continând adresa paginii si parametrii care trebuie
trimisi scriptului (de exemplu http://www.domeniu.dom/cgi-bin/script.pl?name=John&
tel=1274). Daca sunt foarte multe perechi nume = valoare în formular
sau daca valorile sunt mari, atunci aceasta cerere initiala catre server
poate fi prea mare iar serverul nu îi va putea face fata. La fel,
când URL-ul este procesat de server, orice sir de caracter urmat
de un semn de întrebare ajunge într-o variabila de mediu (QUERY_STRING),
care este pusa la dispozitia scriptului CGI.
Este de dorit sa se limiteze spatiul ocupat de variabilele de mediu.
Astfel, pentru formularele mai mari este de preferat metoda POST. În
acest caz, datele din formular nu sunt trimise ca parte a URL-ului, ci
sub forma unui fisier codat cu standardul MIME înglobat în
corpul mesajului trimis catre server. Datorita faptului ca datele sunt
trimise codat MIME, serverul stie marimea si tipul datelor si va trece
aceste valori în script folosind variabila de mediu CONTENT_LENGTH
si CONTENT_TYPE. În plus, scriptul asteapta sa se citeasca setul
de parametrii trimis din formular de pe canalul sau standard de intrare.
Este de datoria serverului sa asigure ca datele trimise de catre navigator
sa fie puse la dispozitia scriptului, gata de a fi importate si scanate
pentru perechile nume=valoare.
Exista însa o complicatie atât pentru GET cât si pentru
POST. Am amintit ca HTTP este un protocol de tip text si ca folosirea
textului pentru a transporta informatii are probleme.
Se întâmpla des ca într-o aplicatie de text sa trebuiasca
sa "furam" caractere pentru a le folosi în aplicatia însasi.
Gânditi-va la fisierul de parole din UNIX: foloseste caracterul
doua puncte (:) pentru a separa diferitele informatii despre utilizator,
cum ar fi parola, numele, iar acest lucru înseamna ca nu putem avea
un nume de utilizator care sa contina caracterul doua puncte. Se spune
ca acesta (caracterul ":") este un caracter "furat".
Pentru a evita acest lucru, protocolul HTTP foloseste o metoda de decodare
ca sa asigure faptul ca informatia ramâne intacta în urma
transmisiei. Pentru a codifica datele folosite în URL-uri, caracterele
spatiu sunt înlocuite cu semnul plus (+) si toate celelalte caractere
care nu sunt alfanumerice sunt înlocuite cu semnul procent (%) urmat
de codul hexazecimal al caracterului.
Într-un mesaj POST sau GET, datele din formular sunt trimise folosind
aceasta metoda de codificare. Numele câmpurilor si valorile sunt
codate si fiecare pereche este transformat într-un sir de caractere
nume=valoare, aceste siruri de caractere fiind apoi concatenate într-un
singur sir, în care perechile sunt separata de caracterul &.
De exemplu, un formular cu metoda POST care contine doua variabile NAME
si PHONE îsi va trimite data astfel:
NAME=Joe&PHONE=1274
Metoda GET va adauga acest sir de caractere la URL, dupa semnul de întrebare,
în timp ce metoda POST va transmite datele ca o parte a protocolului
HTTP, urmând ca scriptul sa le citeasca de pe canalul sau standard
de intrare.
POST /cgi-bin/script.pl HTTP/1.1
Accept: images/gif, image/x-xbitmap,
image/jpeg, image/pjepeg, */*
Accept-Language: en-us,ro;q=0.5
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 4.0;
Windows 95)
Host: www.domain.com
Content-Length: 41280
Connection: Keep-Alive
Pragma: no-cache
Name=John&tel=1274
Raspunsul dat de script
Datoria serverului Web este de a trimite raspunsuri la cererile clientilor.
Scripturile CGI sunt utilizate de regula pentru a colecta date din diverse
surse (de obicei baze de date, fisiere locale etc.), a le grupa, a le
formata si, în fine, a le trimite clientului care le-a solicitat.
Serverul Web aranjeaza lucrurile în asa fel încât iesirea
standard a scriptului sa fie redirectionata catre navigatorul clientului.
Totusi, datele trimise de catre script trebuie sa fie codificate prin
standardul MIME, astfel încât un script trebuie sa porneasca
prin trimiterea unui antet (header) MIME care specifica tipul datelor
care vor urma, antet care este separat de informatia codata printr-un
rând liber. Daca veti examina un script scris în PERL care
returneaza o pagina HTML-ul, sunt sanse mari ca acesta sa înceapa
o linie de forma:
Print "Content-type:text/html\n\n";
Prin aceasta se specifica faptul ca datele care urmeaza reprezinta cod
HTML. Observati ca apare un rând liber în plus dupa aceasta
afirmatie, pentru a separa antetul de corpul mesajului. Antetul fiind
trimis, scriptul poate sa transmita linistit date de tip HTML.
Un exemplu
Am vorbit mai devreme despre scrierea unui script CGI care sa afiseze
toate variabilele de mediu setate de server. Iata cum ar putea arata,
ca script CGI shell:
#!/bin/sh
echo 'Content-type: text/plain'
echo
set
Trebuie sa plasati acest script în cgi-bin-ul dumneavoastra si
sa îl denumiti - sa zicem - shset. Scriptul este executat de shell.
Folosim perechea standard de caractere magice la începutul fisierului
( #!) pentru a comunica sistemului ca acesta este un fisier executabil.
Pentru a rula, scriptului trebuie sa-i setam drepturile de executie.
chmod +x shset
Scriptul afiseaza antetul MIME, un rând liber si apoi foloseste
comanda standard shell set pentru a afisa valorile mediului. Acum puteti
sa-l apelati dintr-un browser Web, prin introducerea unui URL de genul
urmator:
http://www.domain.com/cgi-bin/shset
Veti obtine un ecran plin cu informatii care va arata diferite valori
care sunt stabilite de serverul dumneavoastra
Deoarece scriptul CGI returneaza o parte din informatiile trimise prin
protocolul HTTP catre browser, este usor sa-l determinam sa foloseasca
capacitati speciale ale acestuia. De exemplu, se pot crea pagini în
care sa spunem: "Multumesc pentru introducerea datelor" si sa
instruim navigatorul sa astepte 10 secunde si apoi sa se conecteze la
o alta adresa, de exemplu la pagina de intrare a serverului (sau, eventual,
la pagina pe care utilizatorul o vizitase înainte sa înceapa
sa completeze formularul). Pentru aceasta e suficient adaugati la antetul
HTTP un rând ca cel care urmeaza:
Refresh: 10; URL=http://www.domain.com/index.html
Observatie: URL-ul trebuie sa fie o cale absoluta.
Decodarea argumentelor CGI
Spatiul tipografic nu ne permite sa tratam aici detaliile decodarii argumentelor
URL. În materialul bibliografic specificat la sfârsitul acestui
articol puteti gasi mai multe exemple de scripturi care va vor ajuta la
întelegerea acestora. Perl-ul are acum un modul standard (CGI.pm)
care usureaza extrem de mult rezolvarea acestor sarcini (vezi Listing
1).
Utilizarea bibliotecilor standard are avantaje si dezavantaje. Pentru
programe foarte simple, se întâmpla sa nu folosesc aceste
biblioteci, desi bibliotecile rezolva problemele mai usor si ascund partile
neplacute ale problemei respective. Astfel, când folositi o biblioteca,
probabil nu trebuie sa stiti ce probleme încearca sa rezolve. Daca
functioneaza, este perfect. Dar daca nu, gasirea problemei este mai dificila,
pentru ca va trebui sa cititi si sa întelegeti codul scris de altcineva.
Mai mult, acea persoana a carui cod trebuie sa-l descifrati poate fi un
programator experimentat care foloseste structuri Perl care nu sunt asa
de evidente.
În sfârsit, daca folositi o biblioteca trebuie sa va supuneti
modului în care autorul crede ca trebuie rezolvata problema respectiva.
În cazul modulului CGI.pm, acest lucru înseamna ca programul
scris de noi va contine codul care genereaza aspectul formularului, codul
care parseaza datele din formular, cel care verifica veridicitatea acestora,
cât si codul care contine actiunea principala a procesarii formularului.
În acest fel, scriptul va contine functii nefolosite (ceea ce-i
va creste dimensiunea) iar codul pentru generarea aspectului paginii nu
este separat destul de clar de codul pentru actiunea propriu-zisa - de
obicei selectarea informatiilor ce trebuie afisate.
De fapt, pentru a citi valorile din formular trimisi trimise prin metoda
POST aveti nevoie de vreo 13 linii sursa Perl (vezi Listing 2) si puteti
gasi variante ale acestui cod în diverse carti.
În prima faza (linia 3) scriptul citeste câtiva octeti de
pe canalul lui standard de intrare, unde serverul Web plaseaza valorile
metodei POST. Rutina stie câti octeti trebuie sa citeasca deoarece
poate sa examineze variabila de mediu CONTENT_LENGTH. Folosind limbajul
Perl, toate variabilele de mediu pot fi interogate folosind vectorul asociativ
ENV.
Datele citite sunt stocate într-un sir, care apoi este separat
într-un vector folosind ca separator caracterul & (linia 4).
Avem acum câteva siruri de caractere de forma nume=valoare de care
avem nevoie în continuare. Separam (6) fiecare dintre sirurile de
caractere în doua variabile (folosind ca separator caracterul =),
dupa care decodificam caracterele modificate (7-8) din valoarea corespunzatoare
fiecarui câmp. În cele din urma se depoziteaza (linia 10)
argumentele într-un vector asociativ. În final (liniile 12-14),
se afiseaza perechile nume = valoare receptionate din formular prin metoda
POST.
Pentru cei care nu cunosc Perl: vectorii asociativi sunt structuri de
stocare echivalente cu ceea ce în alte limbaje se cheama "tablouri
asociative" sau "dictionare". În principiu, sunt
structuri similare cu tablourile obisnuite, cu diferenta ca accesarea
elementelor se poate face nu doar printr-un index numeric, ci si printr-un
alt tip de valoare (de exemplu, sir de caractere), numita "cheie".
Astfel, expresia $FORM{"nume"} va furniza valoarea care este
asociata sirului "nume" în vectorul asociativ FORM.
Recomandari
- World Wide Web Consortium (W3C) - http://www.w3.org/. .
- The CGI Resource Index - http://cgi.resourceindex.com/.
Péter Csaba lucreaza la firma NetSoft si poate fi contactat prin
e-mail la cpeter@netsoft.ro
(Multumiri speciale pentru Nagy Nóra
pentru ajutorul acordat în realizarea acestui articol)
Acest articol a aparut in revista PC Report Nr 94 / Iulie 2000
webmaster@pcreport.ro
Nota
Dreptul de autor pentru acest articol apartine in totalitate si in mod exclusiv autorului acestuia .
|