Réseaux et télécommunications
Fermer ×

Le web

Le World Wide Web

Le World Wide Web (www ou w3), créé en 1990 par Tim Berners-Lee travaillant au CERN, est la principale structure d'échange de données sur internet.

Cette dénomination regroupe un protocole principal, des normes et des langages

Important : l'apprentissage de ces différents langages est accessible sur le site w3schools.

Le protocole HTTP, URL et type mime

URL

L'URL (Uniform Resource Locator) est une syntaxe qui définit la position d'un document sur un serveur du réseau. Envoyée par l'ordinateur client, elle se compose de différentes parties :

protocole://serveur/chemin/document

avec

Type mime

Le type mime permet de décrire le type de document transmis. Il est composé d'un type et sous-type avec la syntaxe :

type/sous-type

avec

Le type mime "text/html" correspond donc au document html.

HTTP

Le protocole HTTP (HyperText Transfer Protocol) est un protocole qui utilise un ensemble de commandes. Les commandes pour la demande (requête) de document dont les plus utilisées sont :

La syntaxe de demande de documents est, par exemple :

GET /chemin/fichier HTTP/1.1
host: serveur

Le serveur répond à cette requête en retournant le document précédé d'une entête qui contient au minimum le type mime du document envoyé. Cette entête est toujours suivie d'une ligne vide, puis du contenu du document.

Exemple d'échange

Requête :

 
GET /hello.txt HTTP/1.1
host: localhost.localdomain
localhost est le nom de l'ordinateur sur lequel est exécuté la requête, (adresse ip 127.0.0.1), et localdomain est le nom de domaine de la machine localhost. Cette commande demande le fichier hello.txt à la racine du serveur

Réponse du serveur :

Content-type: text/plain

Bonjour du serveur

Le serveur répond en envoyant le le type mime qui correspond au texte brut. Le début du contenu hello.txt est envoyé après la ligne vide.

Le langage HTML

Syntaxe générale du langage

Le langage HTML (Hypertext Markup Language) est un langage à balises dérivé du langage SGML (Standard Generalized Markup Language) dans ses premières versions. La dernière version XHTML (Extensible Hypertext Markup Language) est dérivée du langage XML (Extensible Markup Language) qui est plus rigoureuse dans l'utilisation et l'imbrication des balises.

En XML, le texte est encadré par une balise ouvrante de la forme <nom_balise> et une balise fermante </nom_balise>. Une balise peut inclure des attributs de la forme : <nom_balise nom="valeur">. Une balise peut être unique et de la forme : <nom_balise/>

Un document xhtml commence par la balise de description du type de document : <!DOCTYPE HTML>. Ensuite l'ensemble est encadré par les balises ouvrantes <html> et fermantes </html>. A l'intérieur de ces balises le document est séparé en deux parties :

Outre afficher des contenus de documents, le langage HTML intègre des liens hypertexte ou hyperliens qui font référence à d'autres documents locaux ou sur le réseau internet.

Contenu de l'entête

L'entête contient un ensemble de balises dont une pour le titre du document qui est affiché dans l'onglet du navigateur, et une pour le type d'encodage des caractères qui est souvent de type utf-8

L'exemple classique de la balise "meta" est <meta http-equiv="content-type" content="text/html;charset=utf-8"/> pour un document html codé en utf-8.

Mise en forme de texte

Titres

Il existe six niveaux de titres de h1 à h6 avec les balises : <hn> et </hn> avec n de 1 à 6 où 1 est le niveau le plus élevé de la hiérarchie.

Paragraphes

Un paragraphe de texte est encadré par les balises <p> et </p>. Il est possible de positionner la suite du texte en début de ligne suivante avec la balise <br/>.

Les listes à puces

Une liste est encadrée par les balises <ul> et </ul> ou bien par les balises <ol> et </ol> pour une liste numérotée. Chaque ligne de la liste est encadrée par les balises <li> et </li>

Les tableaux

Les tableaux sont composés de balises imbriquées qui correspondent au début et à la fin du mode tableau avec les balises <table> et </table>. Ensuite le tableau est décomposé en rangées avec les balises <tr> et </tr>. Chaque rangée est décomposée en colonnes avec les balises <td> et </td>. Ces balises peuvent être remplacées par les balises <th> et </th> pour les entêtes de colonnes et de rangées.

Lien hypertexte

Le lien hypertexte encadre un texte qui décrit la page html qui correspond à ce lien. La syntaxe est : <a href="url_page_web" >texte du lien</a>. "url_page_web" correspond à une url complète dans le cas d'un autre document sur internet ou relative à l'arborescence du serveur. Il existe un deuxième attribut "target" qui précise le mode d'ouverture de la page du lien "_blank" ouvre la page dans un nouvel onglet.

Insertion d'images

L'insertion d'image permet d'insérer une image située sur le même serveur ou bien disponible sur le réseau internet, la syntaxe est : <img src="url_image" alt=""/>. Comme pour le lien hypertexte, "url_image" correspond à une url complète ou bien une url relative au serveur. "alt" est un attribut qui contient le texte qui est affiché lorsque l'image n'est pas ou plus disponible. Cet attribut est obligatoire.

Exemple

Version html du tableau de calcul la page logiciels mathématiques.

Voir le contenu du fichier html

Fichier html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr">
<head>
	<title>Résultats des élections</title>
	<meta http-equiv="content-type" content="text/html;charset=utf-8" />
</head>
<body>
	<h1>Résultats des élections</h1>
	<h2>Deux candidats</h2>
	<ul>
		<li>Candidat 1 : 59,25% , élu à la majorité</li>
		<li>Candidat 2 : 49,75%</li>
		<li>Abstention : 57,98%</li>
	</ul>
	<h2>Résultats détaillés</h2>
	<table border="1">
		<tr><th>Dénomination</th><th>Résultats</th><th>Résultats institutionnels</th><th>Représentativité</th></tr>
		<tr><th>Inscrits</th><td>1190</td><td></td><td></td></tr>
		<tr><th>Votants</th><td>500</td><td></td><td>42,01%</td></tr>
		<tr><th>Blancs et nuls</th><td>100</td><td></td><td>8,40%</td></tr>
		<tr><th>Candidat 1</th><td>201</td><td>59,25%</td><td>16,89%</td></tr>
		<tr><th>Candidat 2</th><td>199</td><td>49,75%</td><td>16,72%</td></tr>
		<tr><th>Suffrages exprimés</th><td>400</td><td></td><td>33,61%</td></tr>
		<tr><th>Abstention</th><td>690</td><td>57,98%</td><td>57,98%</td></tr>
	</table>	
</body>
</html>
					

Affichage correspondant

Le style CSS

Description générale

L'exemple précédent montre la présentation brute HTML, il est bien sûr possible d'ajouter des attributs de mise en forme dans chaque balise. Une autre solution consiste à utiliser un fichier de style à la page HTML, c'est le rôle des styles CSS (Cascading Style Sheets).

Les styles peuvent être intégrés dans l'entête du fichier HTML en utilisant les balises <style> et </style> ou bien enregistrés dans un fichier indépendant .css, dans ce cas le fichier css est inclus avec la balise <link rel="stylesheet" href="chemin/fichier.css" />. L'avantage de cette dernière solution est qu'il est possible de définir un style commun à plusieurs pages html.

Utilisation dans un fichier HTML

Il est également possible de définir un style pour une balise précise, dans ce cas toutes les balises de ce même type auront le même style. Mais le plus souvent, les styles sont utilisés avec les balises <div> et <div> pour les blocs de textes et entre les balises <span> et <span> pour une partie du texte dans une ligne. Le style peut être défini par classe de balises avec l'attribut "class", par identifiant de chaque balise avec l'attribut "id". Une classe peut s'appliquer à plusieurs balises tandis qu'un identifiant est unique à chaque balise.

Syntaxe générale d'un style

balise {
	type: valeur;
}
				

Affecte le style à un type de balise.

#identifiant {
	type: valeur;
}
				

Affecte le style à une balise avec un identifiant (attribut id).

.classe {
	type: valeur;
}
				

Affecte le style à une classe de balises (attribut class).

balise.classe {
	type: valeur;
}
				

Affecte le style à un type de balise appartenant à une classe.

Formatage du texte

La couleur du texte est définie avec la syntaxe color: valeur; où "valeur" représente la couleur bien la valeur en hexadécimal "#RRGGBB"

L'épaisseur du texte est défini avec la syntaxe font-weight : valeur ; où valeur vaut "bold" pour du texte en gras.

Le soulignement du texte est défini avec la syntaxe text-decoration : valeur ; où valeur vaut "underline" pour le soulignement.

Formatage des paragraphes

La couleur de fond est définie avec la syntaxe background-color: valeur; où "valeur" représente la couleur ou bien la valeur en hexadécimal "#RRGGBB"

L'alignement horizontal du texte est définie avec la syntaxe text-align: valeur; où valeur représente le type d'alignement : left, center, right.

Le bord d'un paragraphe est définie avec la syntaxe border: valeur; où valeur représente le type de ligne, l'épaisseur de la ligne

Exemples

Version html+css du tableau de calcul la page logiciels mathématiques.

Voir le contenu des fichiers css et html

Fichier css

h1 { 
	background-color : #4b96e1;
	color : white;
	text-align : center;
}

h2 {
	text-decoration : underline ;
	text-decoration-thickness : 2pt ;
	color : #4b96e1;
	text-align : center;
}

ul.point {
	list-style-type: square;
}

table.cadre  {
	border : 1pt solid;
	width : 100% ;
}

th.ver {
	text-align : right ;
}

th.hor {
	text-align : center ;
}

td {
	text-align : center ;
}

table tr:nth-child(odd) {
	background-color : lightblue;
}

table tr:nth-child(even) {
	background-color : white;
}
					

text-decoration-thickness : valeur représente l'épaisseur du soulignement.

width : valeur représente la largeur du paragraphe en pixels (px) ou en pourcentage (%).

list-style-type: valeur représente le type de puce de la liste à puces.

Fichier html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr">
<head>
	<title>Résultats des élections</title>
	<meta http-equiv="content-type" content="text/html;charset=utf-8" />
	<link rel="stylesheet" href="presentation.css">
</head>
<body>
	<h1>Elections</h1>
	<h2>Deux candidats</h2>
	<ul>
		<li>Candidat 1 : 59,25% , élu à la majorité</li>
		<li>Candidat 2 : 49,75%</li>
		<li>Abstention : 57,98%</li>
	</ul>
	<h2>Résultats détaillés</h2>
    <table class="cadre">
		<tr><th class="hor"></th><th class="hor">Résultats bruts</th><th class="hor">Résultats institutionnels</th><th class="hor">Représentativité</th></tr>
		<tr><th class="ver">Inscrits</th><td>1190</td><td></td><td></td></tr>
		<tr><th class="ver">Votants</th><td>500</td><td></td><td>42,01%</td></tr>
		<tr><th class="ver">Blancs et nuls</th><td>100</td><td></td><td>8,40%</td></tr>
		<tr><th class="ver">Candidat 1</th><td>201</td><td>50,25%</td><td>16,89%</td></tr>
		<tr><th class="ver">Candidat 2</th><td>199</td><td>49,75%</td><td>16,72%</td></tr>
		<tr><th class="ver">Suffrages exprimés</th><td>400</td><td></td><td>33,61%</td></tr>
		<tr><th class="ver">Abstention</th><td>690</td><td>57,98%</td><td>57,98%</td></tr>
    </table>	
</body>
</html>
					

Affichage du résultat

Version html+css du tableau de calcul la page logiciels mathématiques avec une représentation graphique des résultats.

Voir le contenu du nouveau fichier html

Fichier html incluant le style graphique

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr">

<head>
	<title>Résultats des élections</title>
	<meta http-equiv="content-type" content="text/html;charset=utf-8" />
	<link rel="stylesheet" href="presentation.css">
	<style>			
	.camembert {
		background-color : white;
		margin-left : 10px ;
		margin-right : 10px ;
		display: block;
		width: 200px;
		height: 200px;
		border-radius: 50%;
		background-image: conic-gradient(
                orange 0deg,
                orange calc(0.58 * 360deg),
                purple  calc(0.58 * 360deg),
                purple calc(0.75 * 360deg),
                green calc(0.75 * 360deg),
                green calc(0.92 * 360deg),
                lightgray calc(0.92 * 360deg)
                );
	}

	.legende-candidat1 {
		background-color : purple ;
	}

	.legende-candidat2 {
		background-color : green ;
	}

	.legende-blanc-nul {
		background-color : lightgray ;
	}

	.legende-abstention {
		background-color : orange ;
	}

	.legende-element {
		text-align : left ;
		margin-left : 5px;
		margin-top : 5px;
		margin-bottom : 5px;
		margin-right : 5px;
	}

	.legende {
		margin-left : 10px;
		border : 1px solid ;
	}
	</style>
</head>
<body>
	<h1>Elections</h1>
	<h2>Résultats détaillés</h2>
    <table class="cadre">
		<tr><th class="hor"></th><th class="hor">Résultats bruts</th><th class="hor">Résultats institutionnels</th><th class="hor">Représentativité</th></tr>
		<tr><th class="ver">Inscrits</th><td>1190</td><td></td><td></td></tr>
		<tr><th class="ver">Votants</th><td>500</td><td></td><td>42,01%</td></tr>
		<tr><th class="ver">Blancs et nuls</th><td>100</td><td></td><td>8,40%</td></tr>
		<tr><th class="ver">Candidat 1</th><td>201</td><td>50,25%</td><td>16,89%</td></tr>
		<tr><th class="ver">Candidat 2</th><td>199</td><td>49,75%</td><td>16,72%</td></tr>
		<tr><th class="ver">Suffrages exprimés</th><td>400</td><td></td><td>33,61%</td></tr>
		<tr><th class="ver">Abstention</th><td>690</td><td>57,98%</td><td>57,98%</td></tr>
    </table>	
	<h2>Résultats</h2>
	<table align="center">
		<tr><th>Résultats officiels</th><th colspan="2">Représentativité</th></tr>
		<tr>
			<td style="text-align:left;">
				<ul class="point">
					<li><span class="elu">Candidat 1 : 50,25% , élu à la majorité</span></li>
					<li>Candidat 2 : 49,75%</li>
					<li>Abstention : 57,98%</li>
				</ul>
			</td>	
			<td>
				<div class="camembert"></div>
			</td>
			<td>
				<div class="legende">
					<div class="legende-element"><span class="legende-candidat1">   </span> Candidat 1</div>
					<div class="legende-element"><span class="legende-candidat2">   </span> Candidat 2</div>
					<div class="legende-element"><span class="legende-blanc-nul">   </span> Blancs et nuls</div>
					<div class="legende-element"><span class="legende-abstention">   </span> Abstention</div>
				</div>
			</td>
		</tr>
	</table>
</body>
</html>
					

Le fichier html utilise le même fichier "presentation.css" que celui de l'exemple précédent.

La figure utilise une image de fond du style conic-gradient inspirée du site de css du site w3schools. L'image de fond est circulaire grâce au style border-radius: 50%;

La taille de la figure est définie avec les styles width: 200px et height: 200px.

La division du graphique est réalisé à l'aide de couples couleur et position en degré. L'angle est calculé avec la fonction calc de css qui permet de calculer l'angle en fonction du pourcentage.

Le formatage de la légende utilise l'ajout de marge de chaque côté du bloc avec les styles :

  • margin-left : 5px; pour la marge gauche.
  • margin-top : 5px; pour la marge haute.
  • margin-bottom : 5px; pour la marge basse.
  • margin-right : 5px; pour la marge droite.

Affichage du résultat

Le système CGI

Application tiers

Le système CGI (Common Gateway Interface) est une application tiers qui est appelée par le serveur web. Cette application permet de créer des pages dynamiques, c'est à dire des pages qui contiennent des données qui évoluent automatiquement dans le temps. On utilise également ce type de système pour traiter des données de formulaire envoyées par le navigateur.

Un programme CGI utilise un des langages disponibles sur le serveur et peut être par exemple un script shell unix ou python.

Le transfert des données entre le serveur web et le programme CGI se fait en utilisant les variables d'environnement ainsi que l'entrée et la sortie standard du programme CGI. Ce qui fait que la sortie standard du programme CGI devient le navigateur du poste client. Un programme CGI écrit sur la sortie standard la page html qui sera envoyée au navigateur distant.

L'utilisation des scripts CGI n'est généralement pas activée dans le paramétrage des serveurs web. Avant d'utiliser les scripts CGI, il convient de se référer à la documentation du serveur afin d'utiliser les CGI en toute sécurité.

Variables d'environnement

Les informations de la connexion cliente sont enregistrées dans des variables d'environnement spécifiques qui peuvent être utilisées par le script CGI. Exemples de variables d'environnement

Exemples

Affichage d'une page HTML avec un script CGI

Voir le script CGI en shell

programme CGI

#!/bin/sh

cat << EOF
Content-type: text/html

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Hello</title>
</head>
<body>
<h1>Bienvenue</h1>
<p>Quelques variables d'environnement</p>
<ul>
<li>Protocole : ${SERVER_PROTOCOL}</li>
<li>Méthode : ${REQUEST_METHOD}</li>
<li>Script : ${SCRIPT_NAME}</li>
<li>IP client :  ${REMOTE_ADDR}</li>
<li>Langues acceptés : ${HTTP_ACCEPT_LANGUAGE}</li>
</ul>
</body>
</html>
EOF
					

Le script shell envoie la page html en incluant les variables d'environnement

Affichage du résultat dans le navigateur

Les informations affichées indiquent que le client a utilisé le protocole http avec la méthode GET. Le script qui a envoyé cette page est /cgi-bin/hello.cgi. Lé connexion a été faite en local (client et serveur sur le même ordinateur) car l'adresse IP du client est l'adresse IP locale.

Les langues acceptées sont dans l'ordre :

  • Français
  • Français francophone
  • Anglais
  • Anglais Etats-Unis

Affichage des paramètres transmis

Voir le script CGI shell

programme CGI en shell

#!/bin/bash


cat << EOF
Content-type: text/html

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Bienvenue</title>
</head>
<body>
<h1>bienvenue</h1>
EOF

if [ $REQUEST_METHOD = "GET" ]
 then
  CHAINE=$QUERY_STRING
 else
  read CHAINE
fi

echo "recu $CHAINE"

LISTE=${CHAINE//&/ }
for param in $LISTE 
 do
  echo "<h3>param $param</h3>"
  champ=${param%=*}
  valeur=${param#*=}
  valeur=${valeur//+/ }
  echo "<p>champ = $champ</br>"
  echo "valeur = ${valeur}</br></p>"
done

cat << EOF2
</body>
</html>
EOF2
					

Le script CGI commence par lire les paramètres transmis. Ce script fonctionne avec les deux méthodes "GET" et "POST". En effet il détecte la méthode utilisée, si c'est la méthode "GET", la chaine est copiée à partir de QUERY_STRING, sinon la chaîne est lue sur l'entrée standard.

Ensuite la chaîne de paramètres est transmises en liste en remplaçant les symboles & par des espaces. La boucle permet de traiter les paires parametre=valeur séparément. Ces deux valeurs sont séparés pour former "champ" et "valeur" qui sont ensuite affichés.

Affichage du résultat pour l'URL :

http://localhost/cgi-bin/programme.cgi?candidat1=201&candidat2=199

Programme CGI en Python

Le module python cgi contient l'ensemble des fonctions de traitement des données transmises au script CGI. Comme le dit la documentation, ce module devient obsolète dans les dernières versions de python.

La méthode cgi.FieldStorage() renvoie un dictionnaire qui contient les noms des paramètres ainsi que la valeur correspondante, et ce, quelque soit la méthode utilisée "GET" ou "POST".

Exemple

Voir le script CGI python

programme CGI en python

#!/usr/bin/python3

import cgi

def EcrireEntete(titre):
	print("Content-Type: text/html")
	print("")
	print("<htmL>\
<title>" + titre + "</title>\
<head>\
<meta content-type=\"text/html\" charset=\"utf-8\"/>\
</head>\
<body>")	
	
def EcrireFin():
	print("</body>\
</htmL>")	

EcrireEntete("Bienvenue")
form = cgi.FieldStorage()
print("<h1>Bienvenue</h1>")
nbparam=len(form)
print(f"{nbparam} paramètre(s)</p>")
if (nbparam > 0):
	for cle in form:
		valeur = form.getvalue(cle)
		print(f"<p>champ = {cle}<br/>valeur = {valeur}</p>")
EcrireFin()
					

Le script python doit être un fichier exécutable et commencer par la ligne :

#!/usr/bin/python3

si le programme python est la version python3, sinon il faut adapter cette ligne à la configuration python existante.

On affiche les éléments du dictionnaire form où la clé est le nom du champ et la valeur la valeur correspondante au champ.

Affichage du résultat pour l'URL :

http://localhost/cgi-bin/programme.cgi?candidat1=201&candidat2=199

Formulaire HTML et CGI

Formulaire HTML

Un formulaire HTML permet la saisie d'informations par l'utilisateur. Ces informations sont ensuite transmises au programme CGI. Le mode de transmission dépend de la méthode utilisée "GET" ou "POST".

L'ensemble du formulaire est encadré par les balises <form> et </form>. Il doit contenir au moins un bouton de soumission (envoi) des données, et éventuellement un bouton de réinitialisation des données. La balise <form> contient deux attributs qui sont action qui contient l'url du script CGI et method qui contient le type de requête "GET" (méthode par défaut) ou "POST".

Le bouton de soumission du formulaire respecte la syntaxe : <input type="submit" value="Envoyer" />.

Le bouton de réinitialisation respecte la syntaxe : <input type="reset" value="Effacer" />

Principaux types de saisies de la balise <input />

Méthode GET

Avec la méthode GET, les données du formulaire sont transmises avec l'URL sous la forme :

protocole://serveur/chemin/programmecgi?parametre1=valeur1&parametre2=valeur2&...

Après le nom du programme, un ? indique que les informations qui suivent sont des paramètres avec leurs valeurs. Un & sépare chaque paire paramètre/valeur.

La chaîne de caractère qui contient cette suite de paramètres/valeurs est enregistrée dans la variable d'environnement QUERY_STRING qui est accessible par le script CGI. La variable d'environnement CONTENT_LENGTH contient la taille totale des données transmises. Les caractères spéciaux (&,=,%,...) sont codés en hexadécimal précédés du symbole %. les espaces sont remplacés par le symbole "+".

Méthode POST

Avec la méthode GET, les données du formulaire sont transmises avec l'URL sous la forme :

protocole://serveur/chemin/programmecgi

Les paramètres sont transmis par un autre canal qui correspond à l'entrée standard du script CGI.

Exemple

Utilisation d'un formulaire qui permet de calculer le résultat d'une élection

Voir le formulaire HTML et le script CGI

Formulaire HTML

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr">

<head>
	<title>Résultats des élections</title>
	<meta http-equiv="content-type" content="text/html;charset=utf-8" />
	<link rel="stylesheet" href="presentation.css">
</head>
<body>
	<h1>Elections</h1>
	<h2>Saisir les valeurs</h2>
	<form action="/cgi-bin/elections.cgi" method="POST">
		<table style="border : 1pt solid;margin-left: auto;margin-right: auto;">
			<tr><td class="gauche">Nombre total d'inscrits</td><td><input type="text" name="inscrits" value="1190" size="8"/></td></tr>
			<tr><td class="gauche">Total des voix de Candidat 1</td><td><input type="text" name="candidat1" value="201" size="8" /></td></tr>
			<tr><td class="gauche">Total des voix de Candidat 2</td><td><input type="text" name="candidat2" value="199" size="8" /></td></tr>
			<tr><td class="gauche">Total des votes blancs et nuls</td><td><input type="text" name="blancsnuls" value="100" size="8" /></td></tr>
			<tr><td><input type="reset" name="reset" value="réinitialiser"/></td><td><input type="submit" name="valider" value="valider"/></td></tr>
		</table>
	</form>
</body>
</html>
					

Affichage du résultat

Script CGI

#!/bin/bash

inscrits=1190
blancsnuls=100
candidat1=201
candidat2=199

if [ $REQUEST_METHOD = "GET" ]
 then
  CHAINE=$QUERY_STRING
 else
  read CHAINE
fi

LISTE=${CHAINE//&/ }
for param in $LISTE 
 do
  champ=${param%=*}
  champ=${champ//+/ }
  valeur=${param#*=}
  valeur=${valeur//+/ }
  case $champ in
	candidat1)
				candidat1=$valeur
				;;
	candidat2)
				candidat2=$valeur
				;;
	blancsnuls)
				blancsnuls=$valeur
				;;
	inscrits)
				inscrits=$valeur
				;;
  esac
done

suffragesexprimes=$(( $candidat1 + $candidat2 ))
votants=$(( $blancsnuls + $suffragesexprimes ))
abstention=$(( $inscrits - $votants ))
pourcent1=$( echo "scale=2;100*${candidat1}/${suffragesexprimes}" | bc -l)
pourcent2=$( echo "scale=2;100*${candidat2}/${suffragesexprimes}" | bc -l)
pourcentabstention=$( echo "scale=2;100*${abstention}/${inscrits}" | bc -l)
pourcentvotants=$( echo "scale=2;100*${votants}/${inscrits}" | bc -l)
pourcentblancsnuls=$( echo "scale=2;100*${blancsnuls}/${inscrits}" | bc -l)
pourcentvrai1=$( echo "scale=2;100*${candidat1}/${inscrits}" | bc -l)
pourcentvrai2=$( echo "scale=2;100*${candidat2}/${inscrits}" | bc -l)
pourcentsuffragesexprimes=$( echo "scale=2;100*${suffragesexprimes}/${inscrits}" | bc -l)
majorite=$( echo "scale=0;1+${suffragesexprimes}/2" | bc -l)
valeurabstention=$( echo "scale=4;${abstention}/${inscrits}" | bc -l)
valeurcandidat1=$( echo "scale=4;${candidat1}/${inscrits} + ${valeurabstention}" | bc -l)
valeurcandidat2=$( echo "scale=4;${candidat2}/${inscrits} + ${valeurcandidat1}" | bc -l)

resultat1=""
resultat2=""
if [ ${candidat1} -ge ${majorite} ]
 then
	resultat1=", élu à la majorité"
fi
if [ ${candidat2} -ge ${majorite} ]
 then
	resultat2=", élu à la majorité"
fi

if [ $abstention -lt 0 ]
 then
	cat << EOR
Content-type: text/html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
	<title>Résultats des élections</title>
	<meta http-equiv="content-type" content="text/html;charset=utf-8" />
</head>
<body>
<h1>Erreur nombre de votants supérieur au nombre d'inscrits</h1>
</body>
</html>	
EOR
	exit 1
fi 
cat << EOF
Content-type: text/html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
	<title>Résultats des élections</title>
	<meta http-equiv="content-type" content="text/html;charset=utf-8" />
	<link rel="stylesheet" href="/~michel/elections/presentation.css">
</head>
<body>
	<h1>Résultats des élections</h1>
	<h2>deux candidats</h2>
	<ul class="point">
		<li>Candidat1 : ${pourcent1}% ${resultat1}</li>
		<li>Candidat2 : ${pourcent2}% ${resultat2}</li>
		<li>Abstention : ${pourcentabstention}%</li>
	</ul>
	<h2>Résultats détaillés</h2>
    <table class="cadre">
		<tr><th></th><th class="hor">Résultats</th><th class="hor">Résultats institutionnels</th><th class="hor">Représentativité</th></tr>
		<tr><th class="ver">Inscrits</th><td>${inscrits}</td><td></td><td></td></tr>
		<tr><th class="ver">Votants</th><td>${votants}</td><td></td><td>${pourcentvotants}%</td></tr>
		<tr><th class="ver">Blancs et nuls</th><td>${blancsnuls}</td><td></td><td>${pourcentblancsnuls}%</td></tr>
		<tr><th class="ver">Candidat 1</th><td>${candidat1}</td><td>${pourcent1}%</td><td>${pourcentvrai1}%</td></tr>
		<tr><th class="ver">Candidat 2</th><td>${candidat2}</td><td>${pourcent2}%</td><td>${pourcentvrai2}%</td></tr>
		<tr><th class="ver">Suffrages exprimés</th><td>${suffragesexprimes}</td><td></td><td>${pourcentsuffragesexprimes}%</td></tr>
		<tr><th class="ver">Abstention</th><td>${abstention}</td><td>${pourcentabstention}%</td><td>${pourcentabstention}%</td></tr>
    </table>
    <p>La majorité se situé à ${majorite} suffrages exprimés</p>
	<a href="/~michel/elections/">faire une nouvelle saisie</a>
</body>
</html>
EOF
					

Les contenus des variables canditat1, candidat2, inscrits, blancsnuls sont affectés avec les valeurs transmises en utilisant les noms des paramètres.

Les calculs des résultats sont effectués à partir de ces 4 valeurs :

  • suffragesexprimes = canditat1 + cansidat2
  • votants = blancnuls + suffragesexprimes
  • abstention = inscits - votants

Ensuite les valeurs en pourcentage sont calculées.

La majorité nécessaire est calculée à partir du nombre de suffrages exprimés divisé par deux + 1

Cette majorité permet de détecter quel candidat est élu.

Enfin on crée la page HTML en incluant ces valeurs.

Affichage du résultat

Version python

#!/usr/bin/python3

import cgi

def EcrireEntete(titre):
	print("Content-Type: text/html")
	print("")
	print("<htmL>\
<title>" + titre + "</title>\
<head>\
<meta content-type=\"text/html\" charset=\"utf-8\"/>\
<link rel=\"stylesheet\" href=\"/~michel/elections/presentation.css\">\
</head>\
<body>")	
	
def EcrireFin():
	print("</body>\
</htmL>")

inscrits = 1190
blancsnuls = 100
candidat1 = 201
candidat2 = 199
nbparam=0
form = cgi.FieldStorage()
nbparam=len(form)
if (nbparam > 0):
	for cle in form:
		champ = cle[0]
		valeur = form.getvalue(cle)
		resultat = valeur
		if (champ == "inscrits"):
			inscrits = valeur
		elif (champ == "blancsnuls"):
			blancsnuls = valeur
		elif (champ == "candidat1"):
			candidat1 = valeur 
		elif (champ == "candidat2"):
			candidat2 = valeur
suffragesexprimes = candidat1 + candidat2
votants = blancsnuls + suffragesexprimes 
abstention = inscrits - votants 
pourcent1=100 * candidat1 / suffragesexprimes
pourcent2=100 * candidat2 / suffragesexprimes
pourcentabstention=100 * abstention / inscrits
pourcentvotants=100*votants / inscrits
pourcentblancsnuls=100* blancsnuls / inscrits
pourcentvrai1=100*candidat1 / inscrits
pourcentvrai2=100*candidat2 / inscrits 
pourcentsuffragesexprimes=100*suffragesexprimes / inscrits
pourcentsuffragesexprimes=100*suffragesexprimes / inscrits
majorite = 1+suffragesexprimes//2
valeurabstention=abstention / inscrits
valeurcandidat1=candidat1 / inscrits + valeurabstention
valeurcandidat2=candidat2 / inscrits + valeurcandidat1
resultat1=""
resultat2=""
if (candidat1 >= majorite ):
	resultat1=", élu à la majorité"
if (candidat2 >= majorite):
	resultat2=", élu à la majorité"
EcrireEntete("Résultats des élections")
print("<h1>Résultats des élections</h1>")
print("<h2>Deux candidats</h2>")
print("<ul>")
print(f"		<li>Candidat1 : {pourcent1}% {resultat1}</li>")
print(f"		<li>Candidat2 : {pourcent2}% {resultat2}</li>")
print(f"		<li>Abstention : {pourcentabstention:.2f} %</li>")
print("</ul>")
print("<h2>Résultats détaillés</h2>")
print("	<table class=\"cadre\">")
print("		<tr><th></th><th class=\"hor\">Résultats</th><th class=\"hor\">Résultats institutionnels</th><th class=\"hor\">Représentativité</th></tr>")
print(f"		<tr><th class=\"ver\">Inscrits</th><td>{inscrits}</td><td></td><td></td></tr>")
print(f"		<tr><th class=\"ver\">Votants</th><td>{votants}</td><td></td><td>{pourcentvotants:.2f}%</td></tr>")
print(f"		<tr><th class=\"ver\">Blancs et nuls</th><td>{blancsnuls}</td><td></td><td>{pourcentblancsnuls:.2f}%</td></tr>")
print(f"		<tr><th class=\"ver\">Candidat 1</th><td>{candidat1}</td><td>{pourcent1:.2f}%</td><td>{pourcentvrai1:.2f}%</td></tr>")
print(f"		<tr><th class=\"ver\">Candidat 2</th><td>{candidat2}</td><td>{pourcent2:.2f}%</td><td>{pourcentvrai2:.2f}%</td></tr>")
print(f"		<tr><th class=\"ver\">Suffrages exprimés</th><td>{suffragesexprimes}</td><td></td><td>{pourcentsuffragesexprimes:.2f}%</td></tr>")
print(f"		<tr><th class=\"ver\">Abstention</th><td>{abstention}</td><td>{pourcentabstention:.2f}%</td><td>{pourcentabstention:.2f}%</td></tr>")
print("	</table>")
print(f"<p>La majorité se situé à {majorite} suffrages exprimés</p>")
print("<a href=\"/~michel/elections/\">faire une nouvelle saisie</a>")
EcrireFin()
					

Le système FastCGI

Description générale

Le système CGI est obsolète, mais reste encore utilisé pour les petites applications locales. Il est désormais remplacer par le système fastCGI

Contrairement à CGI qui exécute le programme CGI à chaque requête, le système FastCGI est composé d'un serveur CGI qui fonctionne en permanence. A chaque requête FastCGI, le serveur web transmets les données au serveur CGI qui effectue le traitement et retourne les résultats au serveur web qui les transmet au navigateur du poste client. C'est le serveur web qui lance des instances du serveur CGI.

La communication entre le serveur web et le serveur CGI utilise les sockets.

Comme pour le système CGI, FastCGI n'est généralement pas activé dans le paramétrage des serveurs web. Il convient de se référer à la documentation du serveur afin d'utiliser FastCGI en toute sécurité.

Le serveur CGI

On va utiliser la solution Python WSGI (Web Server Gateway Interface) du module flup. Ce serveur est démarré par le serveur web qu'il faut configurer, on peut s'inspirer de cette configuration des principaux serveurs Apache, Nginx et Lighttpd.

Exemples avec le serveur Lighttpd

Voir FastCGI Hello

Serveur FastCGI

#!/usr/bin/python3

from flup.server.fcgi import WSGIServer
from urllib.parse import parse_qs 

entetepagehtml="""<html>
<head>
<title>bienvenue</title>
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
</head>
<body>
<h1>Bienvenue avec fastcgi python</h1>
"""
finpagehtml="""</body>
</html>
"""

def affichage(environnement, entetereponse):
	entetereponse('200 OK', [('Content-Type', 'text/html')])
	pagehtml = entetepagehtml
	pagehtml += "<p>Requete type " +  environnement['REQUEST_METHOD'] + "</p>\n"
	pagehtml += "<p>données : " + environnement['QUERY_STRING']  + "</p>\n"
	listedonnees = parse_qs(environnement['QUERY_STRING'])
	pagehtml += "<ul>\n"
	for champ in listedonnees:
		donnee = listedonnees[champ][0]
		pagehtml += "<li>" + champ + " : " + donnee + "</li>\n"
	pagehtml += "</ul>\n"
	pagehtml += finpagehtml
	return [ pagehtml.encode('utf-8') ]

if __name__ == '__main__':
	WSGIServer(affichage).run()
					

En python la fonction parse_qs permet d'analyser une chaîne de paramètres pour donner un dictionnaire qui contient une liste de champs et valeurs.

Configuration Lighttpd

fastcgi.server = (
     "/pyhello" =>
    ((
         "socket" => "/var/run/lighttpd/fastcgipython.socket",
         "bin-path" => "/var/www/fastcgi/hello.py",
         "check-local" => "disable",
         "max-procs" => 1,
        )
    ))

					

L'emplacement du programme python, choisi ici, est arbitraire, il faut choisir un emplacement sécurisé qui dépend du serveur.

Affichage du résultat de l'URL :

http://localhost/pyhello
Voir FastCGI Hello avec la méthode POST

Serveur FastCGI

#!/usr/bin/python3

from flup.server.fcgi import WSGIServer
from urllib.parse import parse_qs 

entetepagehtml="""<html>
<head>
<title>bienvenue</title>
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
</head>
<body>
<h1>Bienvenue avec fastcgi python</h1>
"""
finpagehtml="""</body>
</html>
"""

def affichage(environnement, entetereponse):
	entetereponse('200 OK', [('Content-Type', 'text/html')])
	pagehtml = entetepagehtml
	pagehtml += "<p>Requete type " +  environnement['REQUEST_METHOD'] + "</p>\n"
	taille = environnement['CONTENT_LENGTH']
	pagehtml += "<p>taille : " + taille + "</p>\n"
	entree = environnement['wsgi.input']
	octets = entree.read(int(taille))
	chaine = octets.decode('utf-8')
	pagehtml += "<p>" + chaine +  "</p>\n"
	listedonnees = parse_qs(chaine)
	pagehtml += "<ul>\n"
	for champ in listedonnees:
		donnee = listedonnees[champ][0]
		pagehtml += "<li>" + champ + " : " + donnee +  "</li>\n"
	pagehtml += "</ul>\n"
	pagehtml += finpagehtml
	return [ pagehtml.encode('utf-8') ]

if __name__ == '__main__':
	WSGIServer(affichage).run()
					

Les données de type POST sont disponibles dans un flux de type InputStream accessible via la variable d'environnement "wsgi.input". La taille en octets des données disponibles est fournie avec la variable d'environnement "CONTENT_LENGTH". Ensuite, la lecture de ce flux d'entrée fournit l'ensemble des paramètres que l'on analyse avec parse_qs

Configuration Lighttpd

fastcgi.server = (
    "/pyhello" =>
    ((
         "socket" => "/var/run/lighttpd/serveurfcgipython.socket",
         "bin-path" => "/var/www/fastcgi/serveur.py",
         "check-local" => "disable",
         "max-procs" => 1,
        )
    ))
					

Affichage du résultat de l'URL :

http://localhost/pyhello
Voir FastCGI pour l'exécution d'un script transmis dans l'URL

Serveur FastCGI

#!/usr/bin/python3

import os
import subprocess
from flup.server.fcgi import WSGIServer
from urllib.parse import urlparse

def affichage(environnement, entetereponse):
	racine = environnement['DOCUMENT_ROOT']
	uri = environnement['REQUEST_URI']
	listeuri = urlparse(uri)
	chemin = listeuri.path
	programme = racine + chemin
	subenv = os.environ.copy()
	subenv['REQUEST_METHOD'] = environnement['REQUEST_METHOD']
	subenv['QUERY_STRING'] = listeuri.query
	resultat = subprocess.run(["python3" , programme] , capture_output=True, encoding="utf-8" , text=True , env=subenv )
	texte = resultat.stdout
	octets = texte.split("\n");
	premiereligne = octets.pop(0)
	octets.pop(0)
	pagehtml="\n".join(octets)
	typemime = premiereligne.split(": ")
	entetereponse("200 OK" , [ typemime ])
	return [ pagehtml.encode("utf-8") ]

if __name__ == '__main__':
	WSGIServer(affichage).run()
					

Cette fois-ci le serveur doit exécuter un script python transmis dans l'URL. Le chemin complet d'accès au script est transmis dans la variable d'environnement "REQUEST_URI" qui est analysée avec la fonction python urlparse qui donne le chemin d'accès au fichier ainsi que les paramètres transmis (méthode GET). Le chemin absolu est construit à l'aide la racine de l'arborescence du serveur contenue dans la variable d'environnement "DOCUMENT_ROOT" et le chemin fourni par la variable d'environnement "REQUEST_URI". Ensuite le programme python transmis est exécuté dans un process séparé avec la fonction subprocess. Cette fonction retourne le résultat de l'exécution du programme python. A partir du texte retourné, on extrait le type mime et le contenu avant d'envoyer l'ensemble au serveur.

Configuration Lighttpd

fastcgi.server = (
     ".py" =>
    ((
         "socket" => "/var/run/lighttpd/serveurappsfcgipython.socket",
         "bin-path" => "/var/www/fastcgi/serveur_apps.py",
         "check-local" => "disable",
         "max-procs" => 5,
        )
    ))    
					

La configuration fait que le serveur se connecte au serveur cgi pour chaque fichier d'extension py. La valeur 5 de "max-procs" indique au serveur d'exécuter plusieurs instances du programme serveur.

Affichage du résultat de l'URL :

http://localhost/essai.py

Les exemples précédents sont basiques, et ne prennent pas en compte les contraintes de sécurité. Il convient de bien lire l'ensemble des documentations afin d'obtenir un système plus sécurisé.