0%

Hoofdstuk 8

Caching en voorwaardelijke verzoeken

Mag ik dit antwoord hergebruiken?

Hoofdstuk 7 liet de leeslijst beschermd achter: een sessie zegt wie er is ingelogd, en alleen het token van een schrijver mag een boek wijzigen. Elke client downloadt echter nog steeds de hele elke keer dat hij erom vraagt. Stel je een leeslijst-app voor die zijn scherm ververst door elke vijf seconden GET /books te sturen. De boeken veranderen een paar keer per dag, dus bijna elk antwoord herhaalt het vorige byte voor byte. De server bouwt dezelfde opnieuw, en de client ontvangt en parset bytes die hij al heeft.

Het antwoord van bestaat uit twee helften. De server kan zeggen hoe lang een antwoord mag worden hergebruikt, en de client kan vragen of de kopie die hij heeft nog actueel is. Deze les is de eerste helft: een header in het antwoord die zegt wat een client mag doen met een kopie die hij heeft bewaard. De rest van het hoofdstuk bouwt beide helften in je server en je client.

De vier directieven

Die header is Cache-Control. De is een lijst directieven gescheiden door komma’s, zoals cache-control: max-age=60, private. Een directief is een naam, soms gevolgd door = en een waarde. Vier ervan dekken het meeste van wat servers zeggen:

DirectiefWat een client mag doen met een kopie die hij heeft bewaard
max-age=Nhem zonder te vragen hergebruiken tot hij N seconden oud is; daarna is hij verlopen
no-cachehem bewaren, maar vóór elk hergebruik de server vragen
no-storehem helemaal niet bewaren
privateeen cache die één gebruiker bedient, mag hem bewaren; een gedeelde cache die veel gebruikers bedient, niet

Een cache is alles wat antwoorden bewaart om ze te hergebruiken. Je client krijgt er in les 5 een, en die bedient één gebruiker: wie hem ook draait. Een gedeelde cache zit tussen veel clients en een server, bijvoorbeeld een proxy van een bedrijf, en geeft één opgeslagen antwoord aan iedereen die om dezelfde vraagt. Daarom bestaat private: een antwoord met de gegevens van één persoon mag niet aan de volgende persoon worden gegeven. Deze cursus draait geen gedeelde cache; je hoeft het woord alleen te herkennen.

no-cache is het directief waarvan de naam misleidt. Het betekent niet “niet cachen”. Het betekent “je mag hem bewaren, maar vraag het mij voordat je hem gebruikt”. Navragen kost weinig, zoals les 4 laat zien, omdat de server “nog steeds hetzelfde” kan antwoorden zonder de body opnieuw te sturen. no-store is het directief dat “bewaar hem niet” betekent, en een server stuurt het mee met antwoorden die nooit in een cache horen te staan, zoals een antwoord dat noemt wie er is ingelogd. Les 6 zet het precies op die antwoorden.

Namen van directieven zijn niet hoofdlettergevoelig (RFC 9111, sectie 5.2), dus Max-Age=60 betekent hetzelfde als max-age=60. Een antwoord kan ook meer dan één regel cache-control dragen, en samen vormen die één lijst. In echte antwoorden kom je andere directieven tegen, zoals public, must-revalidate en s-maxage; deze cursus gebruikt ze niet.

De header lezen

Een Cache-Control-waarde lezen is splitsen bij komma’s, zoals hoofdstuk 1 bij een dubbele punt splitste. Voorspel wat het splitsen alleen oplevert:

Wat drukt dit af?

print("max-age=60, private".split(","))

Het tweede stuk begint met een spatie, omdat de spatie na de komma bij geen enkel directief hoort. Daarom wordt elk stuk gestript voordat het wordt vergeleken, en in kleine letters gezet, omdat de namen hoofdletters negeren. Voer dit uit:

Try it

Verander value in "no-store" of in "private,max-age=5" en voer het opnieuw uit. De stukken komen er hetzelfde uit, of er nu een spatie na de komma staat of niet.

Expires en de regel Date

Voordat Cache-Control bestond, zei een server met de header expires hoe lang een antwoord goed bleef. Die bevat een datum en een tijd, geen aantal seconden, in het HTTP-datumformaat: hetzelfde formaat als de regel Date die http.server op elk antwoord van je server zet. De email.utils van Python schrijft dat formaat:

import email.utils

print(email.utils.formatdate(0, usegmt=True))  # Thu, 01 Jan 1970 00:00:00 GMT

0 is de eerste seconde van -tijd, het aantal seconden sinds 1 januari 1970 in UTC dat time.time() teruggeeft. http.server schrijft zijn regel Date met dezelfde , met de huidige tijd.

Een datum heeft een zwakte die een aantal seconden niet heeft: hij werkt alleen als de klok van de client het eens is met die van de server. Als een antwoord beide draagt, wint dus max-age. RFC 9111 (sectie 5.3) zegt een cache expires te negeren zodra max-age aanwezig is. Je ziet expires nog steeds op echte antwoorden, meestal naast een max-age die voor nieuwere clients hetzelfde zegt. Je server stuurt alleen max-age.

Een antwoord draagt cache-control: no-cache. Mag de client het antwoord bewaren?

Wat staat cache-control: private toe?

Een antwoord heeft expires een uur vooruit en cache-control: max-age=60. Hoe lang is het vers?

Hergebruik is de helft van het verhaal. Als een opgeslagen kopie verloopt, moet de client nog steeds de hele body opnieuw downloaden, ook als er niets in veranderd is. De volgende lessen laten een client vragen “is het veranderd?” en een kort antwoord ontvangen, en die vraag heeft een naam nodig voor één versie van een body. Les 2 maakt die naam: een vingerafdruk berekend uit de bytes van de body.