Eén verbinding, veel verzoeken · eindproject
Eindproject: Project: elke body lezen
Je server houdt verbindingen open, en je read_chunked decodeert chunks, maar je fetch leest nog steeds maar twee soorten body: geen na HEAD, en precies content-length bytes. Al het andere laat hem fetch leest elke body volgens de regel uit les 2, en een nieuwe fetch_many, stuurt meerdere verzoeken over één verbinding, zoals je server ze nu verwacht.
Wat de client moet doen
SERVICE_CONTRACT.md noemt de regels onder “The client in wire.py”. De twee nieuwe:
fetchleest een body viacontent-length, via chunks of tot het sluiten, en leest geen body naHEADof bij een 1xx-, 204- of 304-status. Hij weigerttransfer-encodingsamen metcontent-length, en elke transfer coding behalvechunked.fetch_manystuurt één verzoek tegelijk over één verbinding, vraagt bij het laatste om te sluiten, en werptConnectionErrorop als de server de verbinding te vroeg beëindigt.
Mijlpalen
Eerst read_until_close(reader, *, limit=MAX_BODY). Die geeft de bytes terug die al in de buffer staan en elke byte daarna, totdat recv b"" teruggeeft, en laat de buffer leeg achter. Ook een antwoord dat bij het sluiten eindigt, krijgt een limiet: werp ValueError op zodra de bytes limit overschrijden, voordat je om meer vraagt. Ongeveer zes regels.
Dan read_body(reader, method, headers, *, status=200). Herschrijf hem volgens de regel uit les 2, in die volgorde:
Geen body voor
HEAD, voor een status onder 200, of voor 204 of 304: geefb""terug zonder te lezen.Met
transfer-encoding: alscontent-lengther ook is, werp danValueErrorop. De header moet precies éénbevatten, chunkedin willekeurige hoofd- en kleine letters en met spaties eromheen toegestaan; al het andere, zoalsgzip, chunked, werptValueErrorop, omdat deze client geen andere codering decodeert. Geef daarna terug watread_chunkedteruggeeft.Zonder
content-length: geef terug watread_until_closeteruggeeft.Lees anders via
content-length, zoals eerder.
De regel uit hoofdstuk 1 die een body zonder lengte weigerde, is verdwenen. De tests die bij dit hoofdstuk in tests/test_wire.py zijn meegekomen, verwachten hem niet meer, en de docstring bovenaan dat bestand zegt dat.
Dan fetch. Die geeft de status van het antwoord door: read_body(reader, method, response["headers"], status=response["status"]). Zonder die status zou een 304 met een content-length fetch laten wachten op bytes die nooit komen.
Voer de helper uit:
python pl_http.py bodies
Je fetch vraagt de kleine server uit les 3 om /length, /chunked en /close, elk over een eigen verbinding. Vóór je wijzigingen eindigt het chunked antwoord in raised ValueError: this client cannot measure a body sent with Transfer-Encoding yet. Erna eindigt elk van de drie rapporten in een body:
body: b'{}' (2 bytes)
body: b'{"books": ["Kindred", "Caf\xc3\xa9"]}' (31 bytes)
body: b'{"ends": "at the close"}' (24 bytes)
Als laatste fetch_many(base_url, targets, *, connect=socket.create_connection, timeout=TIMEOUT). Die stuurt GET base_url + target voor elk doel, op volgorde, over één verbinding, en geeft een fetch teruggeeft:
Open de verbinding één keer,
with connect((host, port), timeout=timeout) as sock:, met de host envan base_url(poort 80 als die er geen heeft), en maak één reader voor de hele verbinding. Bytes van het volgende antwoord kunnen in dezelfderecvaankomen als het einde van dit antwoord, en die wachten in de buffer van die reader.Bouw elk verzoek met
build_request("GET", base_url + target, headers). Het laatste bevat de headerConnection: close, zodat de server sluit zodra elk antwoord is aangekomen; de andere hebben geenConnection-header nodig.Stuur een verzoek, lees de kop en de body ervan, en stuur pas dan het volgende. Ze allemaal versturen voordat je leest, pipelining genoemd, laat deze cursus met
. Als een antwoord vóór het laatste doel
connection: closezegt, leest de server niets meer op deze verbinding. Werp dan meteenConnectionErrorop, met een melding die beide getallen bevat, hoeveel doelen er zijn beantwoord en hoeveel het er waren, zoalsthe server closed the connection after 1 of 3 requests.
Ongeveer zestien regels. Voer het daarna uit tegen je eigen server:
python pl_http.py many
statuses: [200, 200, 200]
/me: b'{"user": null}' (14 bytes)
serve() returned after 1 connection; the server is closed
pl_http drukt de drie verzoeken af als één client -> server-regel, omdat het alles wat een verbinding droeg samen toont. Het log van je server bevat drie verzoeken, en serve telde één verbinding.
Je tests
Je kunt tests toevoegen aan tests/test_wire.py, zoals in hoofdstuk 6. fakes.scripted_socket staat voor een zwijgende server die de verbinding open houdt als het script None bevat, en voor een gesloten server als het script gewoon ophoudt. Een antwoord zonder lengte wordt gelezen tot het sluiten, dus een test van zo’n antwoord eindigt zijn script zonder None:
def test_a_body_that_ends_at_the_close():
sock = scripted_socket([b"HTTP/1.1 200 OK\r\n\r\nab", b"c"])
response = wire.fetch("GET", ITEM_URL, connect=fake_connect(sock, []))
assert response["body"] == b"abc"
Deze keer voert Check my work je tests één keer uit, zoals python -m pytest -x dat zou doen. Geen enkele mag falen, en geen enkele mag een echte verbinding openen. Voer ze eerst zelf uit:
python -m pytest -q
Wat er bewezen wordt, en hoe
Check my work koppelt je client aan je server zonder connect die hij aan fetch_many geeft, geeft een vervangende socket terug: wat je client verstuurt, gaat in dit proces door je eigen recv. Zodra elk antwoord is gelezen en de verbinding nog open is, werpt recv TimeoutError op, en dat betrapt een client die tot het sluiten leest en een serverantwoord zonder lengte. De runs bodies en many zijn er om naar te kijken en worden niet beoordeeld.
Druk daarna op Check my work. De framing, keep-alive en het afhandelen van het sluiten die requests op een verbinding voor je doet, heb je nu met de hand geschreven. Les 5 zet dezelfde bytes in
Opdracht
Leer wire.py elke body te lezen en meerdere verzoeken over één verbinding te sturen.
read_until_close(reader, *, limit=MAX_BODY): geef de bytes inreader["buffer"]terug, gevolgd door elke byte totdatrecvb""teruggeeft, en laat de buffer leeg achter. Werpop zodra de bytes limitoverschrijden, voordat je om meer vraagt.read_body(reader, method, headers, *, status=200), volgens de regel uit les 2:b""zonder te lezen voorHEADen voor een status onder 200, 204 of 304; mettransfer-encodingValueErroralscontent-lengther ook is of als de header iets anders is dan éénchunked(hoofd- en kleine letters maken niet uit, spaties toegestaan), andersread_chunked; zondercontent-lengthread_until_close; anderscontent-lengthzoals eerder.fetchgeeftstatus=response["status"]door aanread_body.fetch_many(base_url, targets, *, connect=socket.create_connection, timeout=TIMEOUT): éénconnect-aanroep met de host envan base_url(80 als die er geen heeft) entimeout, één reader, en voor elk doel op volgorde eenGETgebouwd metbuild_request("GET", base_url + target, headers), volledig gelezen voordat het volgende wordt verstuurd. Alleen het laatste verzoek bevatConnection: close. Geef de records terug, één per doel, elk deparse_head-met "body"erbij, zoalsfetchze teruggeeft. Als een antwoord vóór het laatste doelconnection: closezegt, werp danConnectionErrorop met een melding die beide getallen bevat, hoeveel doelen er zijn beantwoord en hoeveel het er waren, zoalsthe server closed the connection after 1 of 3 requests.
Je server moet nog steeds read_body weigert (les 1). Je tests in tests/ moeten slagen: voer python -m pytest -q uit. In de python pl_http.py bodies en python pl_http.py many je client op een echte verbinding.
Druk daarna op Check my work. Die roept read_body en read_until_close aan met readers rond een vervangende HEAD, 204, 304 en 101 zonder iets om te lezen; chunked bodies met chunked in verschillende schrijfwijzen; lengtes; bodies die bij het sluiten eindigen en al deels in de buffer staan; een body boven limit; en transfer-encoding met content-length, gzip, chunked, twee waarden en identity. Waar een verbinding open blijft, werpt de vervangende socket TimeoutError op als er meer van wordt gevraagd dan de server stuurde. Hij voert fetch uit op een 304, een 204, een chunked antwoord en een antwoord dat bij het sluiten eindigt. Hij voert fetch_many uit tegen de HTTP/1.1-handlerklasse van je eigen server, in dit proces, met boeken die je niet hebt gezien, en controleert één verbinding, de verzoekregels, Connection: close alleen op het laatste verzoek, de time-out en elke body; daarna tegen antwoorden die te vroeg connection: close zeggen. Hij stuurt je server drie verzoeken over één verbinding en een geweigerde chunked body. Als laatste voert hij je tests één keer uit, en stopt bij de eerste fout, zonder netwerk en met een limiet van 3 seconden. Niets uit je terminal wordt beoordeeld.