requests en httpx, nu je het weet · oefening
Wat elke client ongevraagd meestuurt
In hoofdstuk 2 schreef je elke regel van een verzoek zelf, en wire.py stuurt nog steeds precies wat je hem opdraagt: de verzoekregel, een Host-regel uit host_header, de headers die de aanroeper meegeeft, en Content-Length als er een body is. Verder niets. De bibliotheken zijn royaler. Elk ervan voegt regels toe waar je nooit om vroeg, en nu je weet wat elke header doet, kun je ze lezen.
Vier clients, één echo
Om die regels te zien heb je een server nodig die meldt wat hij ontving. /v1/headers van practice-api meldt alleen X-Practice-Name, zoals je in les 2.4 zag, dus pl_http brengt zijn eigen mee: een kleine echo op 127.0.0.1 die GET /echo beantwoordt met elke
Open side_by_side.py en lees de laatste drie fetch_echoes(url) stuurt één GET naar de echo met elk van vier clients, wire.fetch, een kale http.client.HTTPConnection, een requests-sessie en een httpx-client, en elk vraagt om precies één header, X-Asked: yes. echo_headers maakt van elk antwoord een main(argv) neemt de fetch_echoes aan en drukt voor elke client de namen af die je unasked vindt. pl_http geeft de URL mee, omdat de echo alleen binnen zijn run bestaat:
python pl_http.py echo
Voordat je unasked schrijft, toont de run al de vier verzoeken zoals de echo ze ontving, en stopt daarna waar main je functie aanroept: raised NotImplementedError: not written yet: Lesson 5. pl_http schrijft PORT waar het
request 1 GET /echo (connection 1)
Host: 127.0.0.1:PORT
X-Asked: yes
request 2 GET /echo (connection 2)
Host: 127.0.0.1:PORT
Accept-Encoding: identity
X-Asked: yes
request 3 GET /echo (connection 3)
Host: 127.0.0.1:PORT
User-Agent: python-requests/VERSION
Accept-Encoding: gzip, deflate, zstd
Accept: */*
Connection: keep-alive
X-Asked: yes
request 4 GET /echo (connection 4)
Host: 127.0.0.1:PORT
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
User-Agent: python-httpx/VERSION
X-Asked: yes
VERSION staat voor de versie van de bibliotheek die hier geïnstalleerd is. Verzoek 1 is wire.py, verzoek 2 is http.client, verzoek 3 is requests en verzoek 4 is httpx. wire.py voegde één regel toe, http.client twee, en elke bibliotheek vijf, in een eigen volgorde.
Waar elke regel voor dient
Host. Elke client stuurt hem, omdat wire.py schrijft hem met host_header; http.client schrijft hem uit de host en poort die hij kreeg, en requests laat hem over aan de http.client eronder; httpx schrijft hem zelf.
User-Agent. De naam en versie van de client. Een server kan hem loggen, tellen welke clients hem aanroepen, of om een bekende bug in één versie heen werken. Je server negeert hem. Alleen de twee bibliotheken sturen er een; http.client en wire.py laten hem weg.
Waarom draagt User-Agent een versienummer?
Accept. Welke mediatypen de client aanneemt. */* betekent elk type. Je make_session uit les 1 verving hem door application/json voor de leeslijstclient.
Accept-Encoding. Een aanbod: “je mag de body met een van deze comprimeren, en ik maak het ongedaan”. gzip en deflate zijn veelgebruikte compressieformaten; zstd is een nieuwer formaat, dat requests op Python 3.14 aanbiedt omdat die Python het kan decoderen (op een oudere Python eindigt de deflate). Een server die het aanbod aanneemt, stuurt een gecomprimeerde body met een header content-encoding die het formaat noemt, en requests of httpx pakken hem uit voordat .content of .json() hem aan je geeft. http.client biedt identity aan, wat “geen compressie, alsjeblieft” betekent, omdat hij niet uitpakt. wire.py stuurt helemaal geen Accept-Encoding-regel. HTTP leest dat als “elke codering is goed”, dus een server zou nog steeds kunnen comprimeren, en wire.py zou je de gecomprimeerde bytes geven; servers comprimeren meestal alleen voor een client die het aanbood. Je server comprimeert nooit, dus niets hiervan verandert zijn antwoorden.
requests stuurt accept-encoding: gzip, deflate, zstd. Wat mag de server daardoor doen?
Connection. Of de client de verbinding wil bewaren voor nog een verzoek, de vraag uit hoofdstuk 9. Een requests-sessie en een httpx-client houden een pool bij, dus zeggen ze hardop keep-alive. http.client en wire.py zeggen niets, en onder HTTP/1.1 betekent niets zeggen hetzelfde: de verbinding mag open blijven. In deze run opent elke client een eigen verbinding, en de echo sluit elke verbinding na zijn antwoord, dus elk verzoek toont een nieuw verbindingsnummer.
http.client stuurt geen Connection-regel; requests stuurt Connection: keep-alive. Wat mag de server aannemen over de verbinding van het verzoek van http.client?
Headernamen negeren hoofdletters, dus Accept en accept zijn dezelfde header, en een client mag ze in elke volgorde sturen. Om te vergelijken wat elke client toevoegde, vergelijk je dus namen in één schrijfwijze en negeer je hun volgorde. Dat is de functie die je schrijft.
Je opdracht
Schrijf in side_by_side.py unasked(received, asked). received is iets wat bij echo_headers teruggeeft; asked is een lijst van de namen die je zelf stuurde. Geef een lijst terug van de namen in received die niet in asked staan, elk in kleine letters, in de volgorde waarin received ze geeft. Vergelijk in één schrijfwijze: zet elke naam in asked en elke naam in received om naar kleine letters voordat je vergelijkt. Een naam in asked die nooit aankwam, is niet iets wat de client toevoegde, dus die verschijnt nooit in het resultaat.
Een lus over asked die een lijst van namen in kleine letters bouwt, en daarna een lus over received die de namen houdt die daar niet in staan, is genoeg. Voer daarna opnieuw python pl_http.py echo uit. Na dezelfde vier verzoeken drukt main af:
standard output:
wire.py added: host
http.client added: host, accept-encoding
requests added: host, user-agent, accept-encoding, accept, connection
httpx added: host, accept, accept-encoding, connection, user-agent
Het resultaatblok eindigt met the echo answered 4 requests, one per connection, and is closed.
Klik op Check my work. Les 6 zet requests aan het werk in een echt programma: de leeslijstclient.
Opdracht
Vind de headernamen die een client uit zichzelf toevoegde.
Schrijf in side_by_side.py unasked(received, asked):
receivedgeeft headernamen als je eroverheen loopt, zoals dedie echo_headersteruggeeft.askedis eenvan de namen die je zelf stuurde. Geef een lijst terug van de namen in
receiveddie niet inaskedstaan, elk in kleine letters, in de volgorde waarinreceivedze geeft.Negeer hoofdletters aan beide kanten:
X-Traceinreceivedkomt overeen metx-TRACEinasked.Een naam in
askeddie niet inreceivedstaat, verschijnt nooit in het resultaat.
Geef de namen terug, niet de
In de python pl_http.py echo een kleine echo op 127.0.0.1 en draait je main() uit side_by_side.py met de main drukt af wat je unasked zegt dat elke client toevoegde. De run toont de vier verzoeken al voordat unasked bestaat.
Klik daarna op Check my work. Die roept unasked aan met header-dicts die je niet hebt gezien, met namen in gemengde hoofdletters aan beide kanten, een lege asked en een gevraagde naam die nooit aankwam. Terwijl hij draait, weigeren requests, httpx, http en wire van side_by_side iets te versturen, dus geen enkel verzoek verlaat de controle. Niets uit je terminal wordt beoordeeld.