Comment détecter la fin d'une requête HTTP
Lorsqu'on implémente un serveur HTTP basique en Python, il est crucial de savoir quand une requête HTTP se termine pour éviter des connexions persistantes non désirées. Voici quelques exemples illutsrant différents cas où le client reste connecté indéfiniment :
from socket import socket, AF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR
reponse_http = 'HTTP/1.1 200 OK\r\n\r\n<html>Bonjour Monde !</html>'
serveur_socket = socket(AF_INET, SOCK_STREAM)
serveur_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
serveur_socket.bind(('localhost', 12000))
serveur_socket.listen(1)
connexion_client, adresse = serveur_socket.accept()
while True:
print('En attente de requête HTTP...')
requete = connexion_client.recv(1024)
print('Envoi de la réponse HTTP...')
connexion_client.sendall(reponse_http.encode('utf-8'))
Dans cet exemple, bien que "Bonjour Monde !" soit affiché dans le navigateur, la connexion reste ouverte car aucune directive explicite ne stipule sa fermeture.
Solutions possilbes
1. Utilisation de Content-Length
Indiquer explicitement la longueur du corps de la réponse permet au client de savoir combien de données lire avant de considérer la requête comme terminée.
from socket import socket, AF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR
reponse_http = 'HTTP/1.1 200 OK\r\nContent-Length: 23\r\n\r\n<html>Bonjour Monde !</html>'
serveur_socket = socket(AF_INET, SOCK_STREAM)
serveur_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
serveur_socket.bind(('localhost', 12000))
serveur_socket.listen(1)
connexion_client, adresse = serveur_socket.accept()
while True:
print('En attente de requête HTTP...')
requete = connexion_client.recv(1024)
print('Envoi de la réponse HTTP...')
connexion_client.send(reponse_http.encode('utf-8'))
2. Encodage par morceaux (Transfer-Encoding: chunked)
Le transfert par blocs divise les données en parties distinctes, avec chaque partie précédée de sa taille en hexadécimal.
from socket import socket, AF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR
entete_reponse = 'HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n'
serveur_socket = socket(AF_INET, SOCK_STREAM)
serveur_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
serveur_socket.bind(('localhost', 12000))
serveur_socket.listen(1)
connexion_client, adresse = serveur_socket.accept()
while True:
print('En attente de requête HTTP...')
requete = connexion_client.recv(1024)
print('Envoi de la réponse HTTP...')
connexion_client.send(entete_reponse.encode('utf-8'))
# Envoi du premier bloc
connexion_client.send('5\r\nSalut\r\n'.encode('utf-8'))
# Indication de fin
connexion_client.send('0\r\n\r\n'.encode('utf-8'))
3. Fermeture de la connexion après la réponse (Connection: close)
Une autre méthode consiste à fermer explicitement la connexion après avoir envoyé la réponse.
from socket import socket, AF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR
reponse_http = 'HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n<html>Bonjour Monde !</html>'
serveur_socket = socket(AF_INET, SOCK_STREAM)
serveur_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
serveur_socket.bind(('localhost', 12000))
serveur_socket.listen(1)
while True:
print('En attente de requête HTTP...')
connexion_client, adresse = serveur_socket.accept()
requete = connexion_client.recv(1024)
print('Envoi de la réponse HTTP...')
connexion_client.send(reponse_http.encode('utf-8'))
connexion_client.close()
Ces techniques permettent de gérer correctement la fin des requêtes HTTP et d'éviter des connexions ouvertes inutilement.