Portail / Sujets autour de la programmation / Programmation C/C++ sous Linux / Serveur d'application avec Qt 6 / Documentation technique(Sommaire)

Processus de chargement du serveur PAS.

Mise à jour du 08/11/2024.

PAS pour PiApplications Application Server est un serveur d'application développé en C++ sur la base de l'infrastructure Qt et plus particulièrement de sa classe QAbstractHttpServer. Le chargement de ce serveur est un processsus qui contient de nombreuses étapes. Cette multiplication peut rendre difficile la compréhension de ce dernier par un développeur chargé de la maintenance d'une application web. Le serveur PAS constitue une infrastructure visant à dégager le développeur des tâches de bas niveau comme la gestion des communications réseau, le suivi de session, la sécurité, etc. L'idée est de permettre au développeur d'encapsuler son application dans une librairie dynamique chargée puis exécutée par le serveur. Pour cela, il doit s'appuyer sur un concept mis en oeuvre par un certain nombre de classes qui jouent le rôle d'interfaces avec cette infrastructure. La question est de savoir à partir de quel moment le développeur prend en charge la requête HTTP.

Le chargement du serveur PAS.

Le chargement du serveur d'application PAS comporte les étapes nécessaires à :

  1. son lancement ;
  2. le chargement d'un agent de sécurité chargé de veiller au respect des URL d'appel ;
  3. la mise en place éventuelle d'un suivi des échanges entre clients et serveur ;
  4. le chargement des éléments de sécurité éventuels nécessaires au transfert chiffré des échanges ;
  5. la création éventuelle d'un gestionnaire de connexions PostgreSQL commun à plusieurs applications web ;
  6. le chargement d'une ou plusieurs applications web ;
  7. la mise en place d'une connexion d'extrémité chargée de l'écoute des demandes de connexion.

Ce fonctionnement, et en particulier les étapes "éventuelles", sont pilotées par un fichier de configuration "maître" appelé fichier de configuration du serveur.

Lancement du serveur.

Le diagramme ci-dessous schématise le lancement du serveur :

Lancement du serveur

Chargement de l'agent de sécurité.

Le diagramme ci-dessous schématise le chargement de l'agent de sécurité :

Chargement de l'agent de sécurité

Initialisation du serveur.

Le diagramme ci-dessous schématise les étapes d'initialisation du serveur :

Initialisation du serveur

Démarrage de l'écoute par le serveur.

Le diagramme ci-dessous schématise les étapes du démarrage de l'écoute des clients par le serveur :

Démarrage de l'écoute

Du point de vue du développeur ces étapes peuvent être ignorées. Tout se passe comme si la requête du client était transformée en un objet de classe QHttpServerRequest traitée par la méthode WebServer::handleRequest(). Observons le traitement effectué par cette méthode avant de nous intéresser au point d'intervention du développeur.

Chargement des applications web.

Le diagramme ci-dessous schématise les étapes du de chargement des applications web par le serveur. Les applications sont déclarées dans le fichier de configuration du serveur de 1 à N selon une qéquence de nombres au pas de 1 (passer de i à i+2 fera cesser la boucle de chargement à i). Le diagramme ci-dessous représente uniquement les étapes à l'intéroeur de chque boucle pour la configuration de l'application d'indice {N} :

Chargement d'une application web

Si la

Si on observe attentivement le schéma, on peut se demander comment les classes instanciables de l'application web (en particulier la fabrique de sa propre classe est connue du singleton qui gère la collection des fabriques de classe). Ce petit "miracle" se produit lors du chargement de la librarie. Cette dernière comporte une fonction d'initialisation automatiquement invoquée au chargement de la librairie et une fonction de finalisation automatiquement invoquée lors de son chargement. La fonction de chargement ajoute à la collection des fabriques des classes celles de la librairie tandis que la fonction de déchargement supprime ces fabriques de classe de la collection.

La méthode clef : handleRequest().

Notre classe WebServer hérite directement de la classe QAbstractHttpServer et non de QHttpServer. Cela a pour conséquence que le mécanisme de routage décrit dans l'article dédié à la mise en oeuvre de ce dernier ne peut pas être utilisé ici. En fait la classe QHttpServer hérite également de la classe QAbstractHttpServer. Cette dernière dispose d'une méthode virtuelle pure de prototype bool QAbstractHttpServer::handleRequest(const QHttpServerRequest &request, QHttpServerResponder &responder). Cette méthode est automatiquement invoquée par l'infrastructure dès qu'une requête HTTP est reçue. Toute classe qui hérite de QAbstractHttpServer peut donc surcharger cette méthode avec l'assurance que cette surcharge sera invoquée lors de la réception de chaque requête. C'est ce que fait notre classe WebServer. Voici comment notre classe WebServer traite l'arrivée d'une requête.

Déroulement de la méthode WebApp::handleRequest()

Traitement par défaut de la requête HTTP

Le schéma ci-dessus montre que si la requête HTTP est conforme, la méthode process()de la classe WebApp est invoquée. C'est au sein de cette méthode que le traitement applicatif est effectivement effectué.

"Conforme" signifie seulement que le chemin de commutation de l'URL vers une des applications web supportées par le serveur est reconnu. Cela ne présume rien quand à la présence de l'identifiant de session ou des paramètres HTTP transmis.

Le fonctionnement de la méthode process() de la classe WebApp est schématisée ci-dessous. Certaines applications peuvent ne pas souhaiter utiliser les conventions et facilités du serveur comme son mécanisme de suivi de session ou sa geston des services via le paramètre HTTP srv. De telles applications devront redéfinir et coder la méthode process(). C'est pour cela que cette méthode est virtuelle ce qui permet au polymorphisme de jouer est donc d'invoquer la méthode de la classe dérivée et non celle de la classe ancêtre WebApp. C'est pas exemple ce que fait la classe WebSite (application de gestion de sites statiques) qui n'a pas besoin d'un suivi de session ni de la notion de "service".

Déroulement de la méthode WebApp::process()

Conclusion.

Tout cela peut sembler un peu compliqué mais les schémas ci-dessus montrent qu'écrire une application web revient à :

  1. Décider du chemin de l'URL depuis la racine qui jouera le rôle de commutateur vers l'application ;
  2. Surcharger la méthode WebApp::process() ou modéliser les appels des premiers services de cette application avec description exhaustive, pour chaque service, des paramètres HTTP "acceptables" (en particlier le paramètre srv qui identifie chaque service). Notez que la modélisation elle-même n'a pas besoin d'être exhaustive. Il est possible d'enrichier l'application de nouveaux services à n'importe quel moment ;
  3. Créer le projet Qt d'une librairie dynamique qui sert de conteneur à l'application web ;
  4. Ajouter les fonctions d'initialisation et de finalisation de cette librairie pour gérer l'ajout des fabriques de classes instanciables de l'application au singleton des fabriques de classe.
    Attention : chaque librairie doit disposer de ses propres noms de fonction d'initialisation et de finalisation sinon ces fonctions risquent de na pas être invoquées. En particulier, la fabrique de la classe de l'application dérivée de Webappest obligatoire ;
  5. Créer le modèle (données de session) de l'application au sein duquel chaque service trouvera les données à traiter.
  6. Créer les services hérités de la classe IService qui ont été modélisés supra.
  7. Créer le fichier de configuration de l'application puis déclarer ce dernier dans le fichier du serveur.

Cela simplifie considérablement le travail du développeur en lui permettant de se concentrer sur la logique de son application plutôt que sur les détails techniques de réception des requêtes ou d'émission des réponses.

Rédaction par Jean-Marie Piatte (1983-2021)