0%

Zelf HTTP serveren · oefening

HEAD beantwoorden zoals GET

In hoofdstuk 1 gaf je read_body na een HEAD-verzoek b"" terug zonder iets te lezen, omdat een antwoord op HEAD geen body heeft, ook als de kop een lengte aankondigt. Dat was de client die de server vertrouwde. Nu ben jij de server, en je handle beantwoordt HEAD met 405, omdat les 4 alleen GET toestond. Voer het scenario voor deze les uit, een GET /books en daarna een HEAD /books:

python pl_http.py head

De GET krijgt de . De HEAD krijgt 405 Method Not Allowed met allow: GET, en onder dat antwoord drukt pl_http nog één regel af, die de bytes telt die followed the reply en ze laat zien: je foutbody. pl_http leest, zoals elke client, geen body na HEAD, dus die bytes zijn extra; ze kwamen aan omdat niets tussen je handle en de een body weghaalt. De adapter schrijft wat je antwoord- ook bevat, en http.server verstuurt het, ook na HEAD. Geen body sturen na HEAD is jouw taak.

Eén regel aan het eind van handle

HEAD vraagt om precies wat GET zou krijgen, zonder de body: dezelfde status en dezelfde headers. Een client gebruikt het om te zien of iets bestaat, of hoe groot het is, zonder het te downloaden. HEAD /books is dus een 200 met de headers van de lijst, en HEAD /nowhere is een 404 met de headers van het foutantwoord.

De eenvoudigste manier om dat allemaal goed te krijgen, is één regel aan het eind van handle, na het routeren. Routeer HEAD precies zoals GET, bewaar het antwoord in een in plaats van vanuit elke tak terug te keren, en maak de body leeg vlak voor de ene :

    if method == "HEAD":
        response["body"] = b""
    return response

Elke tak daarboven, de lijst, de 404 en de 405, beantwoordt HEAD dan zoals hij GET beantwoordt, zonder de body. Een regel die alleen binnen de tak voor /books staat, zou na HEAD /nowhere nog steeds de body van de 404 versturen.

Waarom zet je de HEAD-regel na het routeren in plaats van binnen de tak voor /books?

De lengte beschrijft nog steeds GET

De regel maakt de body leeg en laat de headers met , dus content-length blijft wat het was: de lengte van de body die GET zou versturen. De definitie van HEAD in (RFC 9110, sectie 9.3.2) vraagt een server de headervelden te sturen die hij voor GET zou hebben gestuurd, en staat hem toe velden weg te laten waarvan hij de pas leert terwijl hij de body maakt. Een server mag content-length op een antwoord op HEAD dus sturen of weglaten, maar als hij er een stuurt, moet de waarde de lengte zijn die GET zou versturen (sectie 8.6). De regel van deze server, vastgelegd in SERVICE_CONTRACT.md, is hem te sturen, omdat een client zo de grootte leert zonder de download. Een lengte van 0 zou beweren dat de lijst leeg is.

De clientkant is al veilig. http.client leest geen body na HEAD, wat de lengte ook zegt, en je wire.py deed dat ook niet. Een body die toch wordt verstuurd, blijft ongelezen in de verbinding. Onder HTTP/1.0 sluit de server na één antwoord en verdwijnen de verdwaalde bytes met de verbinding. Vanaf hoofdstuk 9 vervoert één verbinding meerdere verzoeken, en die bytes zouden worden gelezen als het begin van het volgende antwoord.

GET /books verstuurt 132 bodybytes. Welke content-length heeft HEAD /books op deze server?

Je opdracht

  1. Zet bovenaan handle method = request["method"]. Routeer HEAD daarna zoals je GET routeert: op /books antwoordt method == "GET" or method == "HEAD" met de lijst.

  2. Bewaar het antwoord van elke tak in één variabele, response, en geef die één keer terug, aan het eind.

  3. Zet vlak voor die return response["body"] = b"" als de HEAD is.

  4. Verander de 405 in not_allowed("GET, HEAD"), omdat /books nu beide ondersteunt.

Voer python pl_http.py head opnieuw uit en vergelijk de twee regels server -> client. De statusregel en elke header zijn in beide hetzelfde, content-length: 132 inbegrepen. Het GET-antwoord gaat verder met de body; het HEAD-antwoord stopt bij de lege regel, en er volgt geen regel meer.

Druk daarna op Check my work. Je server kan de boeken opsommen en HEAD beantwoorden, maar hij kan nog geen enkel boek laten zien. De projectles voegt /books/{id} toe.

Opdracht

Breid handle in reading_server.py uit, zodat HEAD precies krijgt wat GET zou krijgen, zonder de body.

  • Routeer HEAD zoals GET: op /books antwoorden GET en HEAD allebei met de .

  • Bewaar het antwoord van elke tak in één en geef die één keer terug, aan het eind van handle. Zet vlak voor die de body op b"" als de HEAD is, zodat de regel elk pad dekt, de 404 en de 405 inbegrepen.

  • Laat de headers met : content-length op een antwoord op HEAD is de lengte van de body die GET zou versturen.

  • Beantwoord andere methoden op /books met not_allowed("GET, HEAD").

Voer python pl_http.py head uit in de en vergelijk de antwoorden op GET en HEAD.

Druk daarna op Check my work. Die roept handle aan met eigen states en vergelijkt elk HEAD-antwoord met het GET-antwoord voor hetzelfde doel: voor /books, voor een gecodeerd /books met een query, voor een lege lijst, en voor paden die met 404 antwoorden. Elk HEAD-antwoord moet dezelfde status en dezelfde headers hebben als het GET-antwoord, met content-length gelijk aan de lengte van de body van GET, en een lege body. GET-antwoorden moeten hun body houden, en DELETE, PUT en POST op /books moeten antwoorden met 405 en een header allow die zowel GET als HEAD noemt. Niets uit je terminal wordt beoordeeld, en er wordt geen server gestart.