Récupération et expiration en HTTP1.1
Posted on sam. 19 novembre 2016 in web
Quand je me suis attaqué à JARR, ma première motivation était d’écrire un crawler qui ne soit pas non seulement rapide mais qui respecte aussi tout un panel de bonnes pratiques et autres RFCs.
Le crawler doit en l’occurence :
- dans le but de limiter le traffic réseau et soulager en temps CPU les serveurs distants : ne récupérer une resource que si elle a expiré
- dans le but de limiter la consomation de resources de mon crawler : vérifier qu’une resource a changé avant de la traiter
Exposé ainsi, je pense que ça peut s’appliquer à en fait n’importe quel crawler et pas seulement un qui récupère des flux RSS, et du coup plus largement n’importe quel client web. Pour expliquer comment réaliser ces deux fonctionnalités je vais m’appuyer sur différents mécanismes de HTTP que presque tout le monde implémente plus ou moins bien. Comme base de travail je vais bien sûr utiliser la RFC2616 qui traitre de HTTP1.1.
Expiration d’une ressource
Il y bien sûr la possibilité de simplement récupérer une ressource en boucle à intervalle de temps régulier. Ça marche plutôt bien et ça reste un fonctionnement par défaut très utile quand une ressource ne dispose d’aucun mécanisme d’expiration.
Comme beaucoup de choses en HTTP on va passer par des entêtes.
Cache-Control
L’entête Cache-Control
, si il est placé dans la réponse, peut préciser différentes choses concernant le contrôle de cache sur un proxy ou par un client. La directive max-age
) est celle qui nous intéresse et sert à préciser le délais après laquelle une ressource est considérée comme périmée.
Par exemple le header suivant Cache-Control: max-age=600
signifie que la resource expirera dix minutes après avoir reçu la requête (en gros
).
Expires
On peut aussi utiliser l’entête Expires
. Celui-ci, beaucoup plus simplement, précise directement la date à laquelle la ressource sera considéré comme périmée. La date doit être précisée dans le format spécifié par la RFC1123.
Exemple assez explicite : Expires: Sat, 19 Nov 2016 14:32:47 GMT
.
Les deux pouvant être présent en même temps la RFC précise à la fin de cette section que la directive max-age
de l’entête Cache-Control
doit prévaloir.
Remarques
Si la date d’expiration de la ressource est inférieure à votre délais de rafraichissement, ce la vous aidera à garder la ressources plus à jours. Si elle est supérieure, cela vous évitera de la récupérer probablement inutilement.
Mais ce qui est important c’est de ne pas oublier que ces valeurs ne sont que des recommandations du serveur. Il n’est pas rare par exemple de voir des valeurs pour Expires
plusieurs années dans le passés ou le futur ! C’est parfois dû à la cohabitation de max-age
et de Expires
mais la conclusion est la même : le serveur peut mentir ou se tromper. Je suggère par conséquent de borner la valeur que vous allez utiliser en y appliquant une limite basse et haute.
Maintenant que vous avez la date d’expiration, il ne vous reste plus qu’à ne planifier la récupération qu’après cette date.
Ensuite, ce n’est pas parce qu’elle a expirée qu’elle a changé.
Vérifier qu’une ressource a changée
Pour ce faire on peut tout simplement comparer la réponse obtenue avec le résultat en cache. Mais cette comparaison implique souvent un coût (disque, réseau, base de donnée, etc) qu’on peut éviter avec, encore une fois l’usage de deux entêtes. Si le serveur reconnaît ces entêtes, il répondra avec un code 304 qui en plus d’être vide (et donc d’économiser de la bande passante) indique qu’elle ne nécessite aucun traitement supplémentaire.
Last-Modified / If-Modified-Since
Vous pouvez placer l’entête If-Modified-Since
avec comme valeur la date (toujours au format de la RFC1123) de la dernière fois que la ressource a été modifié.
Vous pouvez aussi tirer la valeur de n’importe quelle réponse du serveur contenant l’entête Last-Modified
.
ETag / If-None-Match
Autrement vous pouvez aussi récupérer la valeur de l’entête ETag
de n’importe quelle réponse qui le contient et le passer dans vos prochaines requêtes avec l’entête If-None-Match
.
Remarques
Il se peut qu’une réponse avec un code autre que 304
qui contienne un ETag
correspondant à celui transmit dans la requête. Je n’ai pas trouvé de RFC spécifiant le comportement à adopter dans ce cas. Pour ce qui est de JARR j’ai pour ma part décidé que cette réponse n’invalidait pas la ressource et ne procède donc à aucune action après cette réponse.
Si possible, il faut préciser If-None-Match
et If-Modified-Since
. Le serveur suivra différentes règles pour savoir comment traiter les deux entêtes.