Initialement, le WEB ne servait qu'a acceder a des fichiers existants en
utilisant le protocole HTTP. Selon HTTP, un client indique le fichier
qu'il cherche en expediant au serveur une commande GET: une ligne avec
le format suivant:
GET <path vers le fichier> HTTP/1.0 <CRLF><CRLF>
Le protocole "CGI"("Common Gateway Interface"), est un ajout minime a
HTTP qui permet l'implantation d'une relation client/serveur plus
elaboree ou les requetes comprennent des parametres et les fichiers
retournes sont generees dynamiquement suite a l'execution de programmes.
Avant de voir comment fonctionne l'appel de programmes CGI, voyons
d'abord les etapes d'une requete normale faite a travers un "browser".
Dans le Web, les fichiers sont specifie par des URLs comme:
http://www.iro.umontreal.ca/~vaucher/test.html
qui indiquent:
1) La machine sur laquelle se trouve le fichier: www.iro.umontreal.ca
2) le "path" ou chemin pour atteindre le fichier: ~vaucher/test.html
3) le "protocole": http
En fait le "protocole" revient a indiquer le # de port sur lequel (par
convention) le programme serveur attend des demandes de communication.
Pour HTTP, le port=80.
La recherche de notre fichier "test.html" est declanchee soit en
entrant le URL directement dans la zone de recherche d'un browser ou
en appuyant sur un LINK dans une page HTML. Par exemple:
<a href="http://www.iro.umontreal.ca/~vaucher/test.html">Fichier</a>
Suite a ceci, le browser va ouvrir un socket TCP vers le serveur dont
l'adresse est donnee par:
host : www.iro.umontreal.ca
port : 80
et ecrire la requete sur le socket. Dans le cas le plus simple, c'est
la commande indiquee plus haut:
GET <path vers le fichier> HTTP/1.0 <CRLF><CRLF>
Ensuite, le browser va atendre la reponse du serveur. La reponse
comprendra deux parties: un HEADER separe par une ligne blanche des
donnees du fichier proprement dit. Le HEADER comprend une ligne avec
code d'erreur (ou reussite) suivi de diverses informations (1 ligne par
information) dont les plus importants sont le "Content-Type" des donnees
retournees et la longueur du fichier. On peut utiliser TELNET pour voir
exactement ce qui est retourne:
Exemple telnet
% telnet www.iro.umontreal.ca 80
GET /~vaucher/test.html HTTP/1.0
HTTP/1.1 200 OK
Date: Wed, 12 Nov 2003 19:20:36 GMT
Server: Apache/2.0.40 (Red Hat Linux)
Last-Modified: Mon, 22 Nov 1999 19:42:59 GMT
Content-Length: 380
Content-Type: text/html; charset=ISO-8859-1
<HTML>
<HEAD>
<TITLE>Test 6815</TITLE>
..... etc.....
La maniere dont le browser va presenter le contenu du fichier retourne
depend du "Content-Type". Notez que le HEADER n'est jamais affiche.
Pour du HTML avec "Content-type: text/html" comme dans notre exemple, le
fichier devra etre formatte. Pour du texte pur (text/html), le fichier
sera affiche tel quel. Pour des images ou du son, le browser devra
faire appels a divers PLUG-IN specialises chacun dans un type de fichier.
Le serveur
Un serveur HTTP est un simple programme qui ouvre un "Server Socket"
sur le port 80 et attend des connexions de la part des clients. A
chaque ouverture de socket, un serveur normalement cree un Thread
distinct qui lit la commande et en extrait le 'path' pour trouver le
nom du fichier a retourner. Dans le cas normal (fichier existant et
accessible en lecture), le serveur repond en ecrivant sur le socket le
HEADER suivi du contenu du fichier; et finalement, il ferme le socket.
Le "Content-Type" retourne depend d'un fichier de parametrisation du
serveur qui specifie un "Content-Type" pour chaque extension du nom de
fichier.
CGI
Avec le protocole CGI, on peut indiquer que le fichier specifie dans la
requete GET ne doit pas etre retourne tel quel; mais plutot que c'est
un PROGRAMME qui doit etre execute et que c'est son OUTPUT qui doit
etre retourne. On peut aussi donner des parametres ou des donnees qui
seront passes au programme.
Les conventions pour signaller qu'un fichier doit etre execute plutot
qu'expedie sont assez simples. Le plus souvent, il faut que le nom du
fichier se termine avec ".cgi". Parfois une installation exigera que
tous les programmes CGI soient dans un repertoire particulier, p.e.
"/cgi-bin/..." et tout path qui debute par ce repertoire sera interprete
comme un programme CGI a executer. Au DIRO, les CGI doivent etre
termines par ".cgi" et aussi se trouver sur une machine distincte
"www-perso.iro.umontreal.ca" ou "www-etud.iro.umontreal.ca".
Maintenant, comment specifier des parametres. Par convention, les
parametres sont specifies a la suite du "path" identifiant le programme
par des doublets NOM=VALEUR. Le caractere "?" separe les parametres
du "path" et le caractere "&" separe les parametres. Pour tester, ces
idees, vous pouvez faire appel a un CGI dans mon compte qui s'appelle
"echo.cgi" et qui retourne une liste des parametres fournis. Pour ce
faire, utilisez le URL qui suit dans un browser.
http://www-perso.iro.umontreal.ca/~vaucher/SCRIPTS/echo.cgi?a=1&b=tutu
Ou utilisez telnet comme avant avec la commande:
GET /~vaucher/echo.cgi?a=1&b=tutu HTTP/1.0
Note: comme beaucoup de vieux CGI, le programme "echo" est en
fait un SCRIPT ecrit en PERL (il pourrait aussi bien etre ecrit en
CShell). Pour voir son contenu, essayez le lien "echo.perl". De nos
jours, PHP est en train de remplacer PERL.
Serveur pour CGI
Dans le cas d'un appel a un CGI, le travail du serveur HTTP est un peu
plus complique. Il doit faire executer le programme, lui passer des
parametres et recuperer son output pour l'expedier au client.
1) D'abord, l'execution se fait en combinant un FORK avec un EXEC.
2) Pour les PARAMETRES d'un GET, la convention est qu'ils sont passes au
programme dans une variable d'environnement du nom de "QUERY_STRING" et
la section 5.10.5 du livre vert vous montre comment ajouter de telles
variables a l'environnement d'un programme lance avec EXEC.
3) Pour recuperer l'OUTPUT, il faut rediriger l'output DU CGI dans un
PIPE qui sera lu par le serveur ou redirige directement vers le socket
du client. Le livre vert decrit comment faire au chapitre 7 et on
traitera de ce sujet dans la prochaine demo.
4) Il faut que le CGI et le serveur HTTP collaborent afin que le HEADER
retourne au client soit formatte correctement. Le serveur ecrit le
debut du HEADER mais c'est le programme CGI qui doit fournir la derniere
ligne du header - qui specifie le "Content-Type" - ainsi que les donnees
proprement dites (et la ligne blanche qui les separe).
Exemple
Dans mon repertoire CGI, il y a deux scripts CShell que vous pouvez
consulter:
http://www-perso.iro.umontreal.ca/~vaucher/SCRIPTS/a1
http://www-perso.iro.umontreal.ca/~vaucher/SCRIPTS/a2
Il y a aussi des liens symboliques avec l'extention ".cgi" afin que vous
puissiez aussi les invoquer: "a1.cgi" et "a2.cgi". On utilise "echo"
pour generer de l'output. La premiere ligne defini le Content-Type:
"text/plain" pour a1 et "text/html" pour le second. Le reste est
identique. Dans le premier, les etiquettes HTML comme <h1>
seront imprimees telle quelle tandis que dans le fichier "html" elles
occasioneront le formattage habituel. Notez qu'il est important
d'ecrire la ligne blanche qui delimite le HEADER et que dans aucun cas
le HEADER n'est imprime.
Pour invoquer, utilisez dans un browser (ou telnet) une commande comme:
http://www-perso.iro.umontreal.ca/~vaucher/SCRIPTS/a1.cgi?fred=123
Voici le code de "a1.cgi".... Simple n'est-ce pas!
#!/bin/csh -f
echo "Content-Type: text/html"
echo
echo "<h1>Titre</h1>"
echo "request_method: $REQUEST_METHOD<br>"
echo "query_string : '${QUERY_STRING}'<hr> "
Attention il faut donner la permission d'executer: "chmod a+x a1.cgi"
<HTML>
<pre>
<FORM action="http://www-perso.iro.umontreal.ca/~vaucher/SCRIPTS/echo.cgi"
method="GET">
Nom: <input type="text" name="name">
Prof: <INPUT TYPE="checkbox" NAME="prof" VALUE="oui">
<SELECT NAME="couleur">
<OPTION>Bleu
<OPTION>Rouge
<OPTION>Vert
</SELECT> <br>
<input type="submit" value="Invoquez !">
</FORM>
</pre>
</HTML>
Dans ce cas, le CGI invoque est "echo.cgi" qui imprime QUERY_STRING et
montre donc le codage des parametres du FORM. Il est interessant de
voir les conventions adoptees pour coder des caracteres "dangereux" que
des usagers pourrait specifier comme parametres. [ Essayez par exemple
de mettre des blancs et des caracteres comme "?", "/" ou "&" dans le
champ NOM ]. Voir CGI Programming 101 pour plus de details.
% telnet www.iro.umontreal.ca 80 POST /~vaucher/echo.cgi HTTP/1.0 Content-Length: 5 a=123456789Avec GET,la commande aurait ete: "GET /~vaucher/echo.cgi?a=1234 HTTP/1.0". Si vous regardez attentivement la sortie vous verrez que seul les 5 premiers caracteres du paametre on ete lus !!!
La deuxieme difference importante avec les POST est dans la methode utilisee par le SERVEUR pour passer les donnees au CGI. Au lieu de les mettre dans une variable d'environnement, le SERVEUR fait une redirection du socket et le CGI accede aux parametres en lisant les donnees directement sur STDIN. Les variables d'environnement restent utiles: 1) pour specifier que la methode d'appel (POST ou GET) et 2) donner la longueur des donnees a lire sur INPUT.
Du cote des FORMs, il n'y qu'a signaller "method=POST" pour changer le mode d'appel. Voir ci-dessous; on utilise "echo2.cgi" version un peu plus elaboree de "echo":
<HTML> <pre> <FORM action="echo2.cgi" method="POST"> Nom: <input type="text" name="name"> <input type="submit" value="Invoquez !"> </FORM> </pre> </HTML>
Essayez le: form_post.html. Remarquez que QUERY_STRING est vide mais CONTENT_LENGTH a une valeur; tandis que c'est le contraire avec form.html.
Pour des exemples plus elabores d'applications de FORM et de CGI/PHP, les interesses (de percussion) pourront regarder: