Programmation CGI

Jean Vaucher
Version 1.2 (14 nov. 2003)

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 et CGI

Le plus souvent, les CGI sont invoque a partir de FORMS (formulaire) dans des pages HTML. On peut voir un FORM comme un super-LIEN qui comprend non-seulement le URL d'un CGI mais qui permet aussi de definir une interface ou l'usager peut specifier les parametres de l'appel. Quand il appuie sur le bouton d'expedition, ceci provoque l'expedition d'une requete GET qui comprend le contenu des champs du formulaire. Voici une exemple simple qui se trouve dans mon repertoire: www-perso.iro.umontreal.ca/~vaucher/SCRIPTS/form.html
    <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.

Requetes POST

Notre discussion s'est concentree sur la commande GET. Il existe un deuxieme format de requete, le POST, qui est utilisee quand le volume de donnees a passer au CGI depasse la centaine d'octets. La commande POST comprend deux parties separees par une ligne blanche: un HEADER et un corps comme pour les reponses d'un serveur HTTP. Les parametres sont passes dans le corps au lieu d'etre concatenes au URL et on specifie dans le HEADER la longueur exacte des donnees. On peut essayer ce format avec TELNET:
	% telnet www.iro.umontreal.ca 80

	   POST /~vaucher/echo.cgi HTTP/1.0
	   Content-Length: 5
	   
	   a=123456789
Avec 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:


References