Syntaxe des pages CSP et fonctionnement du préprocesseur raiipp


Raiipp a à sa charge la conversion des pages CSP en fichiers C++ directement compilables par g++.

Le décodage des pages s'effectue via trois tampons. Le premier pour les déclarations, le deuxième pour les attributs
et le troisième pour le code à exécuter lors de l'appel par mod_raii de la méthode service(request, respsonse).

Le texte contenu entre les digraphs <% et %> fait l'objet d'un traitement particulier tandis que le texte hors  de ces balises est directement poussé vers la réponse http via le tampon de code.

Une fois ces trois tampons obtenus de la page CSP, le fichier source C++ est généré de la façon suivante :

#include <raii.H>

using namespace raii;

[CONTENU DU TAMPON DE DÉCLARATION]

class SERVLET(RaiiServlet) : public raii::HttpServlet {

        [CONTENU DU TAMPON D'ATTRIBUTS]

        void service(HttpServletRequest& request, HttpServletResponse& response) {

[CONTENU DU TAMPON DE CODE]

        }
};

exportServlet(RaiiServlet);

La macro SERVLET() est utilisée pour créer un nom unique de classe (entre chaque page et à chaque compilation) afin d'éviter les collisions lors du chargement des pages par le linker. Elle doit être systématiquement utilisée.

La macro exportServlet() permet de créer un point d'entrée compatible avec les fonctions dl* qui ne connaissent que la sémantique d'appel du C.

Section de déclaration <%#

Vous pouvez placer plusieurs de ces sections dans votre page. Elles seront ajoutées au tampon de déclaration dans l'ordre d'apparition. En revanche, comme ces sections seront jointes en début de fichier source, il n'y a pas d'intérêt (et ce serait même contre-intuitif) à en utiliser plusieurs.

Dans cette section vous pouvez :

<%#
#include <Magick++.H>
int usage_count;
using namespace Magick;
template<typename T> T add(const T& a, const T& b) { return a+b; }
%>

Section des attributs <%!

Vous pouvez placer plusieurs de ces sections dans votre page. Elles seront ajoutées au tampon des attributs dans l'ordre d'apparition. Toujours aucun intérêt à en mettre plusieurs.

Dans cette section vous pouvez :

Concernant les variables que vous pouvez définir, il est important de garder à l'esprit que vous devez les réinitialiser à chaque requête HTTP à l'aide de la méthode preService. Néanmoins, vous avez la garantie qu'elle ne sera pas modifiée entre l'appel de preService et de service ni  pendant tout le temps que dure l'exécution de votre page CSP.

<%!
// permettre un accès direct à la structure session et au paramètre web relatif à l'action
ptr<HttpSession> session;
String action;

// initialise les variables à la bonne valeur avant l'appel de la méthode service
void preService(HttpServletRequest& request, HttpServletResponse& response) {
session = request.getSession();
action = request.getParameter("action");
}

// purge des variables après la méthode service, sinon session risque de
void postService(HttpServletRequest& request, HttpServletResponse& response) {
session = NULL;
action = "";
}
String plop() {
return "plop";
}

public: //le constructeur doit être public
SERVLET(RaiiServlet)() : raii::HttpServlet(), session(NULL), action("") {
}

%>

Section de code

Le corps de votre page sera une alternance de ces balises et de zones de texte (certainement de l'html). Ce flux sera poussé dans le tampon de code strictement dans l'ordre d'apparition.

Texte hors des balises <%...%>

Le texte en dehors des balises CSP sera ajouté au tampon de code sous forme d'injection dans la variable response :

<h1>plop</h1>

sera transformé en

response << "<h1>plop</h1>\n";

Zone de code <% %>

Une zone de code interrompt le flux de texte et insère "verbatim" le texte  dans le tampon de code. Ainsi le texte suivant :

<h1>plop</h1>
<% if ( admin ) { %>
<h2>admin</h2>
...
<% } %> 

sera inséré dans le tampon de code sous la forme

response << "<h1>plop</h1>\n"
""; if ( admin ) { response << "\n"
"<h2>admin</h2>\n"
"...\n"
""; } response << "\n";

Une fois le digraph %> passé, le flux textuel est de nouveau ajouté au tampon sous forme d'injection dans la variable response.

Evaluation <%= expresion %>

La balise d'évaluation permet d'insérer dans le flux de texte la valeur d'une variable.

Le type de cette variable peut être :

Notez qu'il n'y a pas de support pour unsigned long long int ni long long int. C'est intentionnel et cela permet au programmeur de définir sa représentation de l'entier sur 64bit. Pour ma part, je m'en sers comme valeur financière.

String ntostring(long long int n) {

long long int pe=n/100,
pd=n%100;
StringStream ss;
ss << pe << ".";
if ( pd == 0 )
ss << "00";
else if ( pd < 10)
ss << "0" << pd;
else
ss << pd;
return ss.str();
}

const HttpServletResponse& operator<<(const HttpServletResponse& resp,const long long int n) {

if ( n >= 0.01 ) {
if ( ap_rputs(ntostring(n).c_str(),apacheRequest) < 0 )
                throw ResponseException("Connexion closed");
        }
        else {
//n'affiche rien si 0.00
        }
        return resp;
}

Le flux de texte est interrompu, et du code permettant l'injection de la variable dans la réponse est inséré dans le tampon de code.

<h1><%=admin?title_admin:title_guest%></h1>

devient :

response << "<h1>"; response << ( admin?title_admin:title_guest ); response << "</h1>\n";

Tag personnalisés <%: %> <%/ %>

Ces balises permettent l'instanciation de classes définies par vos soins et l'exécution de code auquel vous pouvez passer des paramètres selon le contexte d'appel.

Ces tags personnalisés peuvent être utilisés de deux façon.

La première en utilisant une balise unique de la forme <%:classe  [param1=expression;] [param2=expression;]%>.

<%:img src="/image/plop.jpg";%>

Les paramètres sont passés à l'instance en transformant param=expression; en instance.setParam(expression);. Notez la transformation du nom en setNom.

Lors de la rencontre de ce tag dans la page CSP, la classe indiquée est instanciée via la variable self, les paramètres sont transmis à l'instance et la méthode self.doStart est appelée. La classe img pourrait être définie de la façon suivante :

class img {
String src;
img() : src("") {}
public:
void setSrc(const String& url) { src=url; }
void doStart(HttpServletRequest& request, HttpServletResponse& response) {
response << "<img src=\"" << src.escapeAttribute() << "\"/>";
}
};

La seconde en utilisant une balise d'ouverture et une balise de fermeture.

<%:classe  [param1=expression;] [param2=expression;] [cond=condition_entrée;]%>

<%/classe [condition_répétition] %>

Concernant l'ouverture, la différence avec l'utilisation d'une balise unique est l'utilisation du paramètre cond. Celui-ci permet de rendre l'entrée dans le bloc encadré par <%:...%> et <%/...%> conditionnelle.

La balise <%/...%> permet de marquer la fin du bloc, une condition de répétition peut être ajoutée afin de paramétrer le nombre de répétitions du bloc encadré. Une fois cette condition testée, la méthode doStop est appelée.

Un exemple simple serait l'implémentation de la balise A

<%:a href="/page.html";%>lien<%/a%>

Celle-ci serait programmée de la façon suivante :

class a {
String href;
public:
a() : href("") {}
void setHref(const String& url) { href=url; }
void doStart(HttpServletRequest& request, HttpServletResponse& response) {
response << "<a href=\"" << href.escapeAttribute() << "\">";
}
void doStop(HttpServletRequest& request, HttpServletResponse& response) {
response << "</a>";
}
};

 

Considérations importantes lorsque vous utilisez des tag personnalisés :

#define ALIAS(variable,alias) typeof(variable)& alias = variable;

Exemples complexes

Tag de création de tableaux

Tag de création de menu déroulant

Commentaires

<%-- Ceci est un commentaire --%>