Satimage Précédent | Suivant
Serving cgi and xmlrpc requests
Accueil Documentation Smile Server Serving cgi and xmlrpc requests  
Principes
Satimage-software distribue un programme cgi spécial permettant de gérer des appels cgi depuis un navigateur internet et des requêtes xmlrpc. Le programme cgi est fourni avec les exemples de Smile Server situés dans le dossier /Applications/Smile/Smile Server/.
Nous supposerons que votre machine agit en tant que serveur web (il faut pour cela activer le partage web dans les préférences système). Pour que Smile puisse gérer les requêtes reçues par le cgi, vous devez ouvrir un port dans Smile. Vous devez également configurer le cgi pour transmettre les requêtes à Smile.
La réponse renvoyée par Smile sera envoyée via le cgi au serveur Apache de votre machine, qui transmettra au navigateur internet client, ou bien à l'application effectuant la requête xmlrpc.

Toutes les informations dont vous avez besoin concernant les commandes mentionnées dans cette page sont dans la Suite Smile over IP du dictionnaire de Smile.

Configuration du cgi
Le programme cgi (cgismile) est livré avec un dossier nommé cgismiletemp. Vous pouvez renommer le programme cgi (par exemple en mycgi), à condition que vous renommiez également le dossier en conséquence (ici, mycgitemp). Il peut être utile de renommer le cgi pour pouvoir en installer plusieurs.
Copiez le programme cgi et son dossier associé dans /Library/WebServer/CGI-Executables. Puis réglez les autorisations du dossier associé à "Lecture et écriture" pour tous les utilisateurs (ou au moins pour l'utilisateur "www").
Le dossier contient un fichier config.plist. Editez ce fichier plist (double-cliquez dessus pour l'ouvrir dans Smile). Entrez l'adresse IP de la machine sur laquelle tourne Smile (ce peut être n'importe quelle machine sur le réseau local ou sur internet) dans le champ remote address. Vous devrez fournir un numéro de port correspondant au Smile Serveur : utilisez un nombre positif inférieur à 65535 ; les nombres inférieurs à 1024 ne sont pas recommandés. N'utilisez en aucun cas un numéro de port déjà ouvert sur votre machine. Entrez ce numéro de port du Smile serveur dans le champ remote port. Enregistrez le fichier config.plist.
Ouverture du port

Pour ouvrir un port sur la machine du Smile serveur, utilisez la commande install server port. En quittant, le Smile serveur fermera tous les ports qu'il a ouvert. Vous pouvez également fermer un port en utilisant la commande remove server port.

Pour que votre Smile Serveur puisse répondre aux appels du cgi, ouvrez le port ayant le numéro indiqué dans le fichier config.plist.

install server port 20000 subroutine "handle_cgi_request"
Le nom de la fonction ("handle_cgi_request" dans notre exemple) doit être le nom d'une fonction existant dans le contexte, c'est-à-dire d'une fonction compilée dans un terminal AppleScript ou appartenant à une bibliothèque de scripts située dans un dossier Context additions de Smile ou chargée à l'aide de la commande add library (cf Utiliser des bibliothèques AppleScript si vous n'êtes pas familier avec les bibliothèques AppleScript dans Smile). Cette fonction sera appelée par le cgi pour chaque requête.
Gestion de la requête par script : cas d'un formulaire HTML

Considérons tout d'abord la situation où le cgi est appelé d'un navigateur internet via un formulaire HTML. La fonction spécifiée lors de l'ouverture du port recevra deux paramètres : les entrées (input) du formulaire de l'utilisateur, sous la forme d'un texte Unicode formaté en plist, et d'autre part l'identifiant de la requête qui vous sera utile si vous souhaitez répondre de manière asynchrone.

Apache s'attend à recevoir une page html possédant un préfixe avec un en-tête HTTP valide. C'est cette page que doit retourner votre fonction, sous forme de texte.
Il existe d'autres alternatives, en particulier si vous voulez gérer des requêtes de téléchargement : cela dépasse le cadre de cette page, mais cela est documenté en de nombreux endroits - ailleurs que sur Satimage-software.

on handle_cgi_request(theplist [, theID])
    [...]
    set lf to ASCII character of 10
    set theheader to "Content-type: text/html" & lf & lf    
    set thehtml to "
<html>
    <head>
        [...]
    </head>
    <body>
        [...]
    </body>
</html>"
    return theheader & thehtml
end handle_cgi_request
Le premier paramètre d'entrée, theplist, est une plist sous forme de texte Unicode, correspondant à un dictionnaire contenant les clés suivantes :
  • CGI-env : lui-même un dictionnaire, contient les variables d'environnement du cgi (adresse du client IP; etc).
  • HTTP_COOKIE : lui-même un dictionnaire, contient la liste des cookies (si cela fait sens).
  • Les données entrées par l'utilisateur, par paires (clé, valeur), où les clés sont les noms ("name") des champs ("input") du formulaire html et les valeurs sont les quantités entrées par l'utilisateur ("value").
    Si le champ correspond à un fichier, la valeur est elle-même un dictionnaire avec les deux clés suivantes : filename, le nom du fichier d'origine du client, et tfilename, le chemin POSIX local du fichier. Lorsque le cgi reçoit la réponse, il effacera le fichier tfilename : il est de votre responsabilité de manipuler le fichier ou de le recopier dans l'intervalle.
Par exemple, l'élément de formulaire html suivant :
<input name="user_age" type="text" value="26"/>
donnera dans la plist :
<key>user_age</key>
<string>26</string>
Remarquez que les valeurs sont du texte : vous devrez les coercer si nécessaire.
set p to PlistOpen theplist
set theAge to (PlistGet (PlistChild p key "user_age")) as integer
ou (plus lisible si les noms des données sont assez courts) :
set p to PlistOpen theplist
set r to PlistGet p
set theAge to r's user_age as integer

Pour manipuler efficacement la plist, utilisez les commandes de XMLLib.osax, et n'oubliez pas d'appeler PlistClose aux plists que vous avez éventuellement créées : vos fonctions ne disposeront pas automatiquement des plists que vous avez créées.

Gestion de la requête par script : cas d'une requête XMLRPC
Si le cgi a été appelé avec une requête XMLRPC, la plist contient le XML d'entrée sous forme de texte Unicode, dans la clé xml_rpc. Vous pouvez utiliser la fonction suivante comme exemple de gestion d'une requête xmlrpc :
on handle_cgi_request(theplist, theID)
    set p to PlistOpen theplist
    set s to PlistGet (PlistChild p key "xml_rpc")
    PlistClose p
    set theinputs to XMLOpen s
    set thehandler to XMLGetText (item 1 of (XMLXPath theinputs with "//methodName"))
    set params to XMLGetText ((XMLXPath theinputs with "//param/value/*")) -- a list
    XMLClose theinputs
    callhandler params subroutine thehandler -- appel d'une fonction via son nom
    set lf to ASCII character of 10
    set theheader to "Content-type: text/xml" & lf & lf    
    set thexml to "
<?xml version='1.0'?>
<methodResponse>
<params>
<param>
<value><string>reply here</string></value>
</param>
</params>
</methodResponse>"
    return theheader & thexml
end handle_cgi_request
Il y a différentes manières d'envoyer une requête xmlrpc. Depuis AppleScript, vous pouvez utiliser la syntaxe suivante, mais vous ne devez pas envoyer la requête depuis le Smile qui la recevra, car l'appel est bloquant.
tell application "http://127.0.0.1/cgi-bin/cgismile"
    set theresult to call xmlrpc {method name:"msg", parameters:{"hello world"}}
end tell
Spécifier des fonctions n'appartenant pas au contexte global
Dans les exemples précédents, la fonction appelée est disponible dans le contexte global du Smile serveur. Dans certains cas vous voudrez ne pas surcharger ainsi le contexte global et vous préfèrerez appeler une routine appartenant à un script. Cette routine doit alors appartenir au script d'un objet : le script objet d'un objet (script of theObject) ou le class script d'un objet (class script of theObject). (Mais pas un script objet). Pour spécifier une telle fonction, définissez le paramètre subroutine de la commande install server port avec un record de la façon suivante :
install server port 20000 subroutine {name:"handle_cgi_request", script:script of window "answering window" as integer}
Filtrages pouvant être effectués sur les ports
Pour des questions de sécurité ou pour d'autres raisons, vous pouvez préférer limiter l'appel à votre Smile serveur seulement pour un certain nombre de machines spécifiques. Pour cela, fournissez une liste d'IP (sous forme de textes) via le paramètre optionnel for de la commande install server port.
install server port 20000 subroutine "handle_cgi_request" for {"82.231.243.102", "17.254.3.183"}
Gestion asynchrone de requêtes
Dans certains cas, le Smile serveur n'est pas censé renvoyer une réponse immédiatement. Un exemple serait le cas d'une tâche longue à effectuer mais ne devant pas bloquer le Smile serveur : vous voudrez alors rediriger cette tâche à effectuer vers un autre Smile de façon à ce que le Smile serveur puisse répondre à d'autres requêtes pendant que la tâche est en cours d'exécution.
Pour cela, la fonction appelée doit renvoyer une erreur error number -1718, qui indique au cgi que le contenu de la réponse n'est pas encore arrivé et qu'il arrivera plus tard. Cela quittera la fonction appelée et débloquera le Smile serveur.
Pour envoyer effectivement la réponse le moment venu, le Smile serveur doit appeler la commande resume server task, qui enverra la réponse au cgi. Pour indiquer à quelle requête il faut répondre, resume server task doit connaître l'identifiant de la requête. Le Smile serveur reçoit l'identifiant de la requête en second paramètre de la fonction appelée par la requête.
La fonction appelée pourrait être la suivante :
on handle_cgi_request(theRequestData, theRequestID)
    set my requestData to theRequestData -- stocke la requête dans une variable persistante
    set my replyID to theRequestID -- stocke l'identifiant dans une variable persistante
    error number -1718 -- code d'erreur spécial, spécifiant que la réponse est différée
end handle_cgi_request
Pour répondre effectivement à la requête, la commande resume server task doit être appelée :
resume server task (get my replyID) with data theData

Dans un vrai programme, vous ne devriez pas utiliser de variable globale (my requestData et my replyID) pour stocker l'identifiant de la requête, en particulier si vous souhaitez transmettre (en asynchrone) une requête longue à exécuter vers un autre Smile serveur.

Problèmes d'accessibilité des ports
Le fait qu'un port puisse ou non être appelé depuis l'extérieur dépend de votre configuration. Si les deux machines sont sur un même réseau local, vous devez simplement vérifier que les firewalls de MacOSX vous permettent d'utiliser ce port. Pour l'utiliser à travers internet, vous devez régler spécifiquement votre configuration. Par exemple si vous utilisez une borne Airport, vous devrez la configurer dans l'utilitaire d'administration d'Airport et régler les redirections de ports de façon à rediriger les requêtes sur votre machine.
English version
Copyright ©2009 Paris, Satimage