OpenScheme : Script CGI en Scheme (suite)
Par :
Guilhem de Wailly (gdw@erian-concept.com)
et :
Fernand Boéri (boeri@unice.fr)
Résumé
Notre connaissance du langage Scheme nous permet maintenant d'entrevoir toute la puissance de ce langage en concevant des applications plus complexes. Cela est rendu possible en exploitant les possibilités d'OpenScheme couvrant un large spectre de fonctionnalités.
Ce mois-ci, nous continuons à examiner les fonctionnalités d'OpenScheme pour écrire des scripts CGI. Nous verrons comment écrire des pages HTML avec du code Scheme dedans de telle manière quelles soient considérées par le serveur WEB comme des scripts CGI. Nous verrons comment rafraîchir les pages périodiquement, comment récupérer les valeurs des champs des formulaires, comment télécharger des fichiers sur le serveur, comment gérer les cookies. Nous verrons aussi comment OpenScheme facilite grandement toutes ces gestions en proposant un environnement puissant et facile d'accès.
L'environnement OpenScheme est disponible sur le CDROM de Linux Magazine. Il est aussi disponible sur www.open-scheme.com. Cet environnement existe en version libre ou commerciale.
Description
Dans les deux derniers articles, nous avons mis en place le serveur HTTP de manière qui soit capable d'exécuter des scripts CGI que nous avons testé avec un petit script CGI en shell. Puis nous avons réalisé un script CGI en Scheme en utilisant le remplacement de mots clefs qui est une technique très souple proposée par OpenScheme pour réutiliser des pages WEB existantes en les ``agrémentant'' de certaines parties dynamiques.
Ce mois-ci, nous allons examiner la seconde méthode pour écrire des scripts CGI. Dans cette méthode, le code Scheme est écrit directement dans la page WEB qui devient toute entière le script CGI. Pour utiliser cette fonctionnalité, OpenScheme est invoqué avec des arguments particuliers sur sa ligne de commande. Une fois reconnue cette option, on se trouve dans un mode de fonctionnement adapté aux scripts CGI et facilitant grandement leur conception. Pour simplifier, on appellera ce mode OHTML.
Cette méthode est très souple et assez efficace. C'est notamment celle que nous avons utilisée pour réaliser le site WEB de ForceSud, une association de réinsertion de Nice, partenaire de Carrefour. Son site est www.force-sud.com.
Cette association propose une galerie marchande organisée en rayons. Le site utilise les cadres (frames) gérés par des programmes en OpenScheme, en mode OHTML. Le moteur base de donnée utilisé est celui d'OpenScheme, qui est extrêmement rapide. Le site propose à ses administrateurs un module de gestion élémentaire qui sera étendu dans les prochains mois.
Ce module d'administration permet de modifier le thème de campagne qui est composé d'un nom, d'un texte et/ou d'une photo. Il est aussi possible de gérer les promotions liées au thème de campagne ; les promotions sont affichées de manière cyclique dans une zone prévue à cet effet dans le magasin vituel, est dans un rayon dont le nom est celui du thème de campagne.
Les utilisateurs remplissent un caddie et peuvent passer commande. La commande est acheminée par courrier électronique chez ForceSud est elle est ensuite traitée (Nous avons proposé un module de paiement sécurisé, mais il n'a pas encore été accepté, le marché étant jugé encore peu réceptif). Le caddie est sauvegardé par un cookie sur le site du client qui le retrouvera à l'identique lors de sa prochaine connexion. Un module permettant aux utilisateurs de gérer plusieurs caddies sera prochainement réalisé ; ce sera bien pratique pour faire des courses types.
Nous étudierons ici les briques nécessaires pour réaliser une telle application.
Code inclus dans la page
La possibilité d'inclure du code Scheme dans une page OHTML repose sur plusieurs caractéristiques :
ï‚· Rendre sous Unix n'importe quel fichier exécutable, en changeant ses droits d'accès (chmod a+x fichier);
ï‚· Spécifier dans un fichier quelle est la ligne de commande à invoquer pour interpréter ce fichier, en la plaçant au début du fichier après la balise #! ;
ï‚· Démarrer OpenScheme dans un mode spécial où il considère que son fichier d'entrée et une page WEB dans laquelle il est susceptible de trouver du code Scheme entre les balises <osm ... osm>. C'est le mode OHTML.
Pour lancer OpenScheme dans le mode OHTML, il est nécessaire de placer sur sa ligne de commande les options --call --cgi. C'est une option générale d'OpenScheme qui permet d'invoquer n'importe quelle fonction au démarrage ; ici, la fonction est --cgi, définie dans le plugin NET de l'environnement.
Nous pouvons même exécuter des pages OHTML en dehors du serveur WEB, mais attention, les résultats seront de l'HTML !
Essayons en écrivant le fichier /home/httpd/cgi-bin/test.cgi suivant :
#! /usr/local/bin/osm-cgi
--banner=off --call --cgi
<html>
<body>
Bonjour
tout le monde !
</body>
</html>
Rendons cette page exécutable en tapant la commande Unix :
$ chmod a+x /home/httpd/cgi-bin/test.cgi
et exécutons la ; nous obtenons l'affichage :
$
/home/httpd/cgi-bin/test.cgi
Content-type:
text/html
<!"Content-type:
text/html">
<html>
<body>
Bonjour, tout
le monde!
</body>
</html>
<! OpenScheme
- 1.3.5 [2000.10.03:08h37]
Copyright (C) 1993 to 2000 by
Erian
Concept
http://www.open-scheme.com
mail:osm.support@erian-concept.com
>
Maintenant, on peut voir le résultat avec le navigateur WEB netscape ou autre en tapant :
$ netscape http://localhost/cgi-bin/test.cgi
On devrait avoir un bonjour !
Nous allons maintenant ajouter au programme précédent du code Scheme directement dans le script :
#! /usr/local/bin/osm-cgi
--banner=off --call --cgi
<html>
<body>
Bonjour
tout le monde !
<! balise reconnue par OpenScheme, et
replacée par les >
<! informations affichées
par le fragment de programme >
<! en utilisant les
fonctions d'affichage standard >
<osm
(let
([aujourdhui (date->vector (date))])
;
affichage du jour
(format
#t
"<br>Ajourdhui,
on est le ~a/~a/~a"
(vector-ref
aujourdhui 2)
(vector-ref
aujourdhui 1)
(vector-ref
aujourdhui 0))
; affichage de
l'heure
(display "<br>il
est ")
(format #t "~a
heures, " (vector-ref aujourdhui
3))
(format #t "~a minutes,
" (vector-ref aujourdhui 4))
(format
#t "~a secondes, " (vector-ref aujourdhui
5)))
osm>
</body>
</html>
Nous devrions avoir une page WEB qui affiche le jour et l'heure ! Tient, pendant que nous y sommes, y-a-t-il un moyen que cette page soit mise à jour périodiquement, sans aucune intervention de l'utilisateur ? Pour cela, on peut placer dans l'entête une balise HTML spéciale pour automatiser le rafraîchissement :
#! /usr/local/bin/osm-cgi
--banner=off --call --cgi
<html>
<head>
<!
spécifie un rafraîchissement automatique >
<
de la page toutes les trois secondes >
<meta
http-equiv="refresh"
content="3;url=/cgi-bin/test.cgi">
</head>
<body>
Bonjour tout le monde
!
<osm
(let
([aujourdhui (date->vector (date))])
(format
#t
"<br>Aujourd'hui,
on est le ~a/~a/~a"
(vector-ref
aujourdhui 2)
(vector-ref
aujourdhui 1)
(vector-ref
aujourdhui 0))
(display "<br>il
est ")
(format #t "~a
heures, " (vector-ref aujourdhui
3))
(format #t "~a minutes,
" (vector-ref aujourdhui 4))
(format
#t "~a secondes, " (vector-ref aujourdhui
5)))
osm>
</body>
</html>
Le nombre 3 dans ``...content=''3;...`` indique que le rafraîchissement doit avoir lieu toutes les 3 secondes. Nous avons donc maintenant une belle horloge WEB ! (xclock pourra aussi être utilisé :-)
Variables prédéfinies
L'environnement OHTML récupère des variables d'environnement pour les transformer en variables Scheme. Parmi ces variables, on trouve auth-type, content-length, content-type, gateway-interface, path-info, path-translated, query-string, remote-addr, remote-host, remote-ident, remote-user, request-method, request-uri, script-name, server-name, server-port, server-protocol et server-software.
Le lecteur pourra se référer à toutes les sources d'informations données en annexes sur le protocole HTTP pour connaître précisément la signification de ces variables.
Arguments des formulaires
Le mois dernier, nous avons vu que les valeurs des formulaires étaient retournées par la fonction (net:cgi:pase-input) sous la forme d'une liste de couples variable-valeur. Dans le mode de fonctionnement OHTML que nous sommes en train d'examiner, les arguments des formulaires sont placés dans des variables Scheme dont le nom est celui donné aux champs dans le formulaire HTML.
Considérons par exemple le formulaire /home/httpd/html/form.html suivant :
<html>
<body>
<!
Début do formulaire.
Le script
spécifié par action sera
invoqué
lorsque l'on pressera le bouton.>
<form
action=/cgi-bin/test1.cgi
method=post>
<! Zone
d'édition.>
<br>nom:
<input
type=text
name=nom
size=10>
<br>Prénom:
<input
type=text
name=prenom
size=10>
<br>Age:
<input
type=text
name=age
size=10>
<! Bouton
OK.>
<br>
<input
type=submit
value=ok>
</form>
</body>
</html>
Le script /home/httpd/cgi-bin/test1.cgi est invoqué lorsque l'on presse sur le bouton OK :
#! /usr/local/bin/osm-cgi
--banner=off --call --cgi
<html>
<body>
Réponse
au formulaire !
<osm
(format
#t "<br>nom =
~s" nom)
(format #t
"<br>prénom = ~s" prenom)
(format
#t "<br>age =
~s" age)
osm>
</body>
</html>
Nous voyons que le système a su récupérer automatiquement les valeurs des champs et les placer dans les variables Scheme correspondantes. Cependant, cela peut parfois causer un problème d'existence de variables ; en effet, en Scheme, référencer une variable qui n'existe pas est une erreur. OpenScheme propose dont la nouvelle forme spéciale (defined? variable) qui retourne vrai si la variable est définie et faux sinon. Notez que cette nouvelle forme spéciale n'est définie que dans l'environnement OHTML.
Affichage des erreurs
Il arrive assez rarement que les programmeurs commettent des erreurs :-) Cependant, parfois, il est nécessaire de pouvoir afficher de manière correcte ces messages. Par défaut, OpenScheme écrit sur le flux standard d'erreur tous les messages d'erreur. Le serveur WEB Apache collecte ce flux et le redirige vers le fichier de logs associé au serveur. Le nom de ce fichier est par défaut /var/log/httpd/error_log. Ceci peut être modifié avec la variable ErrorLog du fichier de configuration de Apache, situé dans /etc/httpd/conf/httpd.conf.
OpenScheme permet aussi d'installer des filtres et une continuation pour les messages d'erreur. Les filtres sont rassemblés dans une liste de fonctions Scheme destinées soit à traiter le message, soit à restaurer un environnement particulier avant l'affichage du message. Les filtres sont des fonctions ayant un nombre de paramètres variable dont le premier est une chaîne de format et les autres sont les arguments de la chaîne de format (voir la fonction format).
La continuation est une fonction Scheme appelée lorsque tous les filtres ont été préalablement invoqués. En général, il s'agit d'une continuation Scheme replaçant le programme à un endroit connu. La continuation est une fonction ayant un argument entier, le code de retour de l'erreur.
Les filtres se situent dans la liste *error:hooks* et la continuation dans la variable *error:continuation*. Il est possible d'insérer un filtre ``à la main'' en écrivant :
; ajout d'un filtre à
la main
(set! *error:hooks*
(cons
; le nouveau filtre
(lambda
(chaine-de-format . args)
;
affichage du message sur le port
;
d'erreur standard
(vformat
*current-error-port*
chaine-de-format
args))
*error:hooks*))
Il est aussi possible d'utiliser les fonctions (add-error-hook function) pour ajouter un filtre dans la liste des filtres et (del-error-hook function) pour supprimer un filtre de cette liste.
Si nous souhaitons afficher les erreurs directement dans la page WEB, nous pouvons écrire :
; ajouter le
filtre
(add-error-hook
(lambda
(chaine-de-format . args)
; créer le
message sous la forme d'une
; chaîne de
caractères
(let ([message
(vformat
#f
chaine-de-format
args)])
;
affichage du message au format HTML
(format
#t
"<br><font
color=red>
Erreur:
~a<font>"
message))))
Dans l'environnement de traitement automatique des CGI OHTML, un filtre standard est déjà mis en place. On peut s'en convaincre en écrivant dans le fichier /home/httpd/cgi-bin/test2.cgi un programme qui commet une erreur :
#! /usr/local/bin/osm-cgi
--banner=off --call --cgi
<html>
<body>
Cette
page contient une erreur !
<! la ligne suivante sera un
message >
<! d'erreur en rouge >
<osm (+ #\a
1) osm>
</body>
</html>
En affichant la page dans le navigateur, nous obtenons un message d'erreur indiquant une erreur sur l'addition.
Téléchargement
Les pages WEB permettent, bien sûr, d'obtenir des informations à partir d'un serveur. Mais il existe aussi la possibilité de déposer des fichiers dans un serveur. Cette possibilité est connue sous le doux nom d'upload. Cette caractéristique est très utile pour mettre à jour un site.
Le protocole HTML utilisé est quelque peu compliqué, et utilise des documents composites (Multiparts documents). Dans l'environnement OHTML, ceci est géré automatiquement par OpenScheme qui cache à l'utilisateur toute la complexité de l'opération.
Avant de commencer, le téléchargement de fichiers est contrôlé par un formulaire (balise <form> ... </form>) contenant une entrée de type file, comme par exemple <input type=file name=nom>. L'endroit où sera déposé le fichier téléchargé et son nom dépendent de la valeur du champ name de la balise. En fonction du nom donné, nous avons :
ï‚· chemin/ : le nom de fichier entré dans le champ de saisie est conservé, et le fichier est placé dans le répertoire chemin/ dans le serveur. La variable chemin/ est définie dans l'environnement OHTML du script attaché au formulaire et vaut la chaîne de caractères représentant le nom de fichier original.
ï‚· chemin/nom : le fichier téléchargé est placé dans le répertoire chemin/ et nommé nom ; la variable chemin/nom est définie dans l'environnement OHTML avec le nom original du fichier.
ï‚· nom : le fichier téléchargé garde son nom original, et la variable nom vaut le nom original du fichier téléchargé.
Par exemple, concevons un petit outil pour visionner d'images en HTML :
#! /usr/local/bin/osm-cgi
--banner=off --call --cgi
<html>
<body>
<!
Début do formulaire.
Le script
spécifié par action sera
invoqué
lorsque l'on pressera le bouton.
Enctype
détermine le type d'encodage,
ici,
multipart>
<form
enctype='multipart/form-data'
action=/cgi-bin/test3.cgi
method=post>
<! Image
précédente.>
<br>
<img
src=/image>
<! Champ de saisie du
nouveau fichier.>
<input
type=file
name=../html/image
size=10>
<! Bouton
OK.>
<br>
<input
type=submit
value=ok>
</form>
</body>
</html>
Le nom choisi pour le champs de saisie du fichier est ../html/image : dans ce cas, le fichier téléchargé perd son nom original, et il est appelé image dans le répertoire ../html. Attention, ce répertoire est donné par rapport au répertoire d`exécution des scripts CGI.
Les cookies
Les cookies sont des informations libres qui sont déposées dans votre système par votre navigateur. Ces informations sont composées d'un nom de serveur, d'un texte libre et d'une date d'expiration. Certains serveurs s'en servent pour déposer des informations permettant, lors de votre prochaine connexion, de retrouver un certain contexte.
Les cookies ont été très fortement réprouvés par les utilisateurs car ils étaient perçus comme une tentative de violation de leur vie privée. Il faut quand même savoir que les cookies sont des informations construites à partir du serveur, en fonction des pages que vous visualisez. Il n'est pas possible, avec les cookies, d'espionner votre ordinateur et d'y voler des informations.
Dans le site de ForceSud, pas exemple, nous nous servons des cookies pour la gestion du caddie : le caddie est en permanence sauvegardé sur le poste client, avec l'avantage de permettre de retrouver le caddie que l'on avait lors de la dernière connexion.
Le contenu du cookie correspondant à votre serveur est placé par le serveur WEB dans la variable d'environnement HTTP_COOKIE. Pour récupérer la valeur du cookie, il suffit donc d'utiliser la fonction (os:getenv HTTP_COOKIE).
Pour écrire le cookie correspondant à votre serveur, il faut placé sous l'entête :
Content-type: text/html
la ligne :
Set-Cookie: nom=valeur; path=/; expires=Mon, 01-Jan-2001 00:00:00 GMT
Pour modifier l'entête HTML, nous devons positionner la variable *net:cgi:header* de l'environnement OHTML. Voici maintenant un petit programme qui récapitule tout cela :
#! /usr/local/bin/osm-cgi
--banner=off --call --cgi
<osm
;
récupération du cookie
(define
cookie (os:getenv "HTTP_COOKIE"))
osm>
<osm
;
fabrication du nouveau cookie
(define nouveau
(date->string (date)))
osm>
<osm
;
modification de l'entete de la page pour
; positionner le
cookie
(set! *net:cgi:header*
(format
#f
"~a\"~a\"~a"
"Content-type: text/htm\nSet-Cookie:
"
nouveau
";
path=/; expires=Mon, 01-Jan-2001 00:00:00
GMT"))
osm>
<html>
<body>
<br>
l'ancien
cookie était :
<osm (display
cookie) osm>
<br>
le
nouveau est :
<osm (display nouveau)
osm>
</body>
</html>
La lecture du cookie se fait au travers de la variable d'environnement HTTP_COOKIE, et son écriture, en modifiant l'entête du résultat. Dans une prochaine version d'OpenScheme, le cookie sera automatiquement placé dans la variable Scheme http-cookie. Pour modifier le cookie, il suffira de modifier cette variable avec une nouvelle chaîne de caractère, en utilisant l'opérateur set!.
La première fois qu'on lance le script au travers du navigateur, on devrait y lire :
l'ancien cookie était :
#f
le nouveau est : "Tue Dec 5 16:16:41 2000"
En effet, il y a peu de chance pour que vous ayez déjà un cookie pour ce serveur, ce qui explique la valeur fausse de l'ancien cookie. A la seconde exécution, on devrait lire :
l'ancien cookie était :
"\"Tue Dec 5 16:16:41 2000\""
le nouveau est
: "Tue Dec 5 16:17:14 2000"
Maintenant, si on examine le fichier ~/.netscape/cookies, on devrait y trouver la ligne suivante :
localhost FALSE / FALSE 978307201 "Tue Dec 5 16:16:41 2000"
Si vous utilisez un autre navigateur, vous devriez trouver le cookie dans un autre fichier. Vous vous apercevez aussi, sans doute, que le fichier contient un nombre de lignes impressionnant, retraçant l'histoire de vos balades sur le réseau. Un bon coup de ménage, et vous serez un nouveau-né !
Nous avons maintenant à notre disposition une panoplie d'outils puissants et simples d'utilisation pour écrire des scripts CGI. Ils sont indépendants du système d'exploitation, car OpenScheme l'est aussi.
Nous les utiliserons prochainement pour écrire une petite application WEB complète.
Les auteurs
Guilhem de Wailly, directeur de la société Erian Concept : support, formations, configurations, administration, développements Linux. Environnement OpenScheme.
http://www.erian-concept.com
Pr Fernand Boéri, Professeur à l'Université de Nice - Sophia-Antipolis, membre de l'unité SPORTS du laboratoire I3S, spécialiste en modélisation, évaluation de performances et architectures parallèles.
http://www.i3s.unice.fr
Références
WEB
 TCP/IP, Architecture,
protocoles, applications
Douglas Comer
InterEdition
ï‚· W3C
http://www.w3c.org
ï‚· CGI
http://www.jmarshall.com/easy/cgi/
ï‚· CGI
http://hoohoo.ncsa.uiuc.edu/cgi/
ï‚· CGI
http://www.tsden.org/ryutaroh/fileupload-e.shtml
ï‚· CGI en Français
http://www.scripts-fr.com/
ï‚· CGI+Scheme
http://www.lh.com/~oleg/ftp/Scheme/web.html
ï‚· FastCGI
http://www.fastcgi.com/
ï‚· HTTP en
Français
http://webbo.enst-bretagne.fr/ActiveWebFr/eg-uk-tut.book_29.fr.html
ï‚· HTTP 1.0
http://www.ietf.org/rfc/rfc1945.txt
ï‚· HTTP 1.1
http://www.ietf.org/rfc/rfc2616.txt
Scheme
 Structure et Interprétation
des programmes informatiques
H. Abelson, GJ.
Sussman
InterEdition
 The Scheme Programming
Languages - Ansi Scheme
R. Kent Dybvig
Prentice Hall
 Les langages Lisps -
Christian Queinnec
InterEdition
 Programmer avec Scheme
- J. Chazarin
Thomson Publishing
 Revised4 Report on the
Algorithmic Language Scheme
W. Clinger, J.
Rees
ftp://ftp.nj.nec.com/pub/kelsey
Environnements Scheme Free
 Bigloo -
Manuel Serrano
http://kaolin.unice.fr
Environnement
de programmation Scheme.
 DrScheme -
Rice University
http://www.cs.rice.edu/CS/PLT/
Environnement
Scheme libre très avancé.
 PCScheme - Texas
Instrument
ftp://cui.unige.ch/public/pcs/pcscheme.exe
Un
très bon environnement de programmation Scheme pour DOS, avec
éditeur intégré.
 Scm - A.
Jaffer
http://www-swiss.ai.mit.edu/~jaffer
La
référence des interprètes Scheme. Très
petit, rapide, pour beaucoup de plates-formes, extensible.
 Stk -
Erik Gallesio
http://kaolin.unice.fr
Interprète
Scheme avec la bibliothèque TK.
D'autres liens sont visibles sur le site www.schemers.org.
Environnements Scheme commerciaux
 ChezScheme -
Cadence, Inc
http://www.scheme.com/
Environnement
Scheme très performant.
 EdScheme, 3Dscheme -
Scheme, Inc
http://www.schemers.com/
Environnement
de programmation Scheme pour Windows.
 Inlab Scheme - Inlab
Software GmbH
http://www.munich.net/inlab/scheme/
Environnement
commercial
 OpenScheme -
Erian Concept
http://www.open-scheme.com
Environnement
professionnel de programmation Scheme comprenant un interprète,
un compilateur et un débogueur symbolique.
Existe en
version libre et commerciale, pour Linux, FreeBSD, Solaris, Windows
et BeOS, sur systèmes Intel.