Le protocole HTTP et WSGI¶§
Rappels sur le Web¶§
Web ≠ Internet¶§
Internet est ensemble de réseaux informatiques locaux utilisant les mêmes protocoles de bas niveau standards (TCP/IP) et formant un réseau global.
Note
Le Web est l’une des applications d’internet, et la plus populaire, d’où l’amalgamme souvent fait entre les deux.
Qu’est-ce que le Web ?¶§
Un espace documentaire décentralisé, interconnecté et interopérable.
- décentralisé → HTTP
- interconnecté → URL
- interopérable → HTML
Note
Ces technologies peuvent bien sûr évoluer (elle l’ont déjà fait), voire être à terme remplacées par d’autres. Ce ne sont pas elles qui définissent le Web, mais les 3 propriétés qu’elles lui confèrent.
Architecture du Web¶§
Architecture Client-Serveur¶§
- Ressource: toute unité d’information (document, image, vidéo, données...) accessible sur le Web, identifiée par une URL
- Serveur: un ordinateur « contenant » des ressources, toujours connecté à Internet.
- Client: un ordinateur/smartphone/tablette... utilisé pour exploiter des ressources.
Note
- Les termes “client” et “serveur” identifient en fait des rôles.
- Le même ordinateur peut jouer le rôle de client dans certaines situations, et de serveur dans d’autres situations.
- Il est même possible d’avoir, sur un même ordinateur, un logiciel client et un logiciel serveur (c’est ce que nous ferons en TP).
Identification des ressources¶§
┌──────────────────── protocole │ ┌───────────── serveur │ │ ┌─ nom local │ │ │ ─┴── ─┴───────── ─┴────────────── http://champin.net/enseignement/web
Le nom local a souvent une structure hiérarchique (similaire à la structure des fichiers).
Comme pour les fichiers, on peut spécifier une URL relativement à une autre :
exemple :
../algorithmique
Avertissement
Malgré cette similitude, les ressources ne correspondent pas toujours à des fichiers.
- Plus généralement, le nom local est composé de plusieurs parties, ayant des structures différentes. Son interprétation est du ressort du serveur.
┌──────────────────── protocole │ ┌───────────── serveur │ │ ┌─ nom local │ │ │ ─┴── ─┴───────── ─┴───────────╌╌ http://champin.net/a/b/c?d=1&e=2#f ───┬─ ─────┬─ ┬ │ │ │ partie hiérarchique │ │ │ (chemin) ─┘ │ │ partie associative │ │ (paramètres) ─────────┘ │ partie interne │ (fragment) ────────────┘
- Chaque URL désigne une ressource différente (même lorsqu’elle ne diffère que par les paramètres, par exemple)
Rôles du serveur¶§
- Est garant de l’état des ressources,
- pour en fournir une représentation aux clients qui le demandent (GET),
- mais aussi pour les modifier en réponse à certaines requêtes (POST),
par exemple :
- commande sur un site marchand,
- message posté sur un réseau social,
- etc...
Important
L’état d’une ressource n’est pas forcément stocké dans un fichier. Souvent, il est stocké dans une base de données ; la représentation HTML (ou autre) de la ressource est calculée par le serveur.
Message HTTP¶§
Principe¶§
HTTP est basé sur l’échange de messages :
- le client envoie un message requête,
- le serveur retourne un message réponse.
Chaque échange est indépendant des autres (messages auto-suffisants).
Structure d’une requête¶§
- Première ligne
- verbe
- identifiant local de la ressource
- version du protocole HTTP
- En-têtes (suivis d’une ligne vide)
- Contenu facultatif (selon le verbe)
Exemples de requêtes¶§
GET /france/lyon HTTP/1.1
Host: meteo.example.org
User-Agent: Mozilla/5.0 (X11; Linux x86_64;
rv:58.0) Gecko/20100101 Firefox/58.0
Accept: text/html,application/xhtml+xml,
application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Connection: keep-alive
Keep-Alive: 300
(pas de contenu)
GET /france?ville=lyon HTTP/1.1
Host: meteo.example.org
User-Agent: Mozilla/5.0 (X11; Linux x86_64;
rv:58.0) Gecko/20100101 Firefox/58.0
Accept: text/html,application/xhtml+xml,
application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Connection: keep-alive
Keep-Alive: 300
(pas de contenu)
POST /passer-commande HTTP/1.1
Host: marchand.example.org
User-Agent: Mozilla/5.0 (X11; Linux x86_64;
rv:58.0) Gecko/20100101 Firefox/58.0
Accept: text/html,application/xhtml+xml,
application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Connection: keep-alive
Keep-Alive: 300
Content-Type: application/x-www-form-urlencoded
Content-Length: 12345
nom=PA+Champin&addresse=12+rue+Turing&articles=...
Note
- On retrouve dans la requête la structure de l’URL (nom du serveur, nom local de la ressource)
- Notez que les deux premières requêtes sont identiques, à l’exception du nom local.
Structure d’une réponse¶§
- Première ligne
- version du protocole HTTP
- code de statut
- libellé textuel
- En-têtes (suivis d’une ligne vide)
- Contenu facultatif (selon le code de statut)
Exemples de réponse¶§
HTTP/1.1 200 OK
Date: Mon, 02 Jan 2016 22:46:26 GMT
Server: Apache/2
Accept-Ranges: bytes
Content-Type: text/html; charset=utf-8
Content-Length: 29794
Etag: "7462-477341dcfb940;89-3f26bd17a2f00"
Last-Modified: Mon, 02 Jan 2016 12:00:00 GMT
Content-Location: Home.html
Vary: negotiate,accept
Cache-Control: max-age=600
Expires: Mon, 02 Nov 2009 22:56:26 GMT
Connection: close
<!DOCTYPE html>
<html><head><title>Météo de Lyon</title>
...
HTTP/1.1 303 See also
Date: Mon, 02 Jan 2016 22:46:26 GMT
Server: Apache/2
Accept-Ranges: bytes
Location: /commande/12345
Connection: close
(pas de contenu)
HTTP/1.1 404 Not Found
Date: Mon, 02 Jan 2016 22:46:26 GMT
Server: Apache/2
Content-Type: text/html; charset=utf-8
Content-Length: 2979http://rdflib.readthedocs.io/
Connection: close
<!DOCTYPE html>
<html><head><title>Cette ressource n'existe pas</title>
...
Codes de statut¶§
HTTP définit 40 codes de statut, répartis en cinq catégories :
Catégories | Exemples |
---|---|
1xx : Information | 100 Continue |
2xx : Succès | 200 OK |
3xx : Redirection | 301 Moved Permanently |
4xx : Erreur client | 404 Not Found, 401 Unauthorized |
5xx : Erreur serveur | 500 Internal Server Error |
En-têtes de requête¶§
Note
HTTP spécifie un très grand nombre d’en-têtes ; nous décrirons au fur et à mesure du cours ceux dont nous avons besoin.
accept
: la liste des types de contenu (au format MIME) préférés le client.accept-language
: la liste des langues (au format RFC 4646) préférés par le client.
Ces en-têtes servent à la négociation de contenu (conneg). Le serveur peut utiliser ces informations pour adapter le contenu de la réponse, mais il peut aussi les ignorer (lorsque la ressources est un fichier statique).
cache-control
: permet d’influer sur le comportement des caches intermédiaires, notamment pour les inhiber (optionno-cache
).
En-têtes de réponse¶§
content-type
: le type de contenu de la réponse (au format MIME). Exemple:text/html
,image/png
,application/pdf
.content-length
: la taille du message en octets.cache-control
: permet d’influer sur le comportement des caches, notamment pour indiquer la durée de validité maximale (optionmax-age=<seconds>
).
Note
les en-têtes content-type
et content-length
sont en fait utilisés pour tout message ayant un contenu,
y compris certaines requêtes (e.g. POST).
WSGI¶§
Serveur et application¶§
Note
- Certaines ressources sont stockées directement dans des fichiers statiques (dont le nom est en général corellé avec le chemin de l’URL).
- Certaines ressources sont gérées par un script PHP.
- Certaines ressources sont gérées par un programme,
répondant à certains standards, comme
- CGI,
- WSGI, que nous allons décrire dans ce cours,
- ...
WSGI¶§
WSGI (Web Server Gateway Interface) est un standard spécifiant comment un serveur Web peut interagir avec une application Python.
Hello world WSGI¶§
def application(environ, start_response):
message = b"Hello world\n"
status = "200 Ok"
headers = [
("content-type", "text/html"),
("content-length", str(len(message))),
]
start_response(status, headers)
return [message]
Explications¶§
- La fonction
application
sera appelée à chaque requête. environ
est un dictionnare, qui contient notamment toute l’information décrivant la requête.start_response
est une fonction servant à générer les méta-données de la réponse (statut et en-têtes).- La fonction doit retourner une liste de chaînes d’octets.
Note
Plus précisément, la fonction doit retourner un itérable de chaînes d’octets. Cela permet dans certains cas au serveur d’envoyer les premiers octers avant que la fonction n’ait terminé son exécution.
Chargement du module¶§
- Le module peut rester chargé entre deux requêtes, ce qui permet de mutualiser certains traitemens (e.g. connexion à une base de données).
- Cela dit, le serveur peut à tout moment décharger le module, donc l’application ne doit pas s’appuyer sur des données conservées en mémoire.
n = 0
def application(environ, start_response):
global n
n = n+1
message = ("Compteur: %s\n" % n).encode("utf-8")
status = "200 Ok"
headers = [ ("content-type", "text/html"),
("content-length", str(len(message))), ]
start_response(status, headers)
return [message]
Note
Dans l’exemple ci-dessus, plusieurs requêtes successives vont faire augmenter le compteur. Cependant, à tout moment, le module peut être déchargé par le serveur, et rechargé lors d’une nouvelle requête, ce qui remettra le compteur à zéro. Si on voulait persister la valeur du compteur, il faudrait la sauvegarder dans un fichier ou une base de données.
Le dictionnaire environ
¶§
Il contient, entre autre, les clé suivantes :
PATH_INFO
: la partie hiérarchique du nom de la ressourceQUERY_STRING
: la partie associative du nom de la ressourceREQUEST_METHOD
: la méthode HTTP de la requêtewsgi.input
: un file-like donnant accès au contenu de la requêteCONTENT_TYPE
,CONTENT_LENGTH
: la valeur des en-têtes correspondantsHTTP_xxx
: la valeur le l’en-têtexxx
Pour en savoir plus, consultez la spécification WSGI.
Serveur de développement¶§
- Pour tester notre application, il ne serait pas très pratique d’avoir à configurer et déployer un serveur Web complet.
- Heureusement, les bibliothèques standards de Python nous fournissent un mini-serveur, utile pour la phase de développement.
- Pour l’utiliser, il suffit de créer un script contenant les lignes suivantes (en adaptant la première ligne) :
from mon_projet import application
from wsgiref.simple_server import make_server
srv = make_server("localhost", 12345, application)
srv.serve_forever()