Zelf HTTP serveren · oefening
Een paar verbindingen bedienen en dan stoppen
handle geeft de juiste make_listener bond een serve accepteerde clients een voor een, met een time-out, totdat het er max_connections had bediend. http.server.HTTPServer is die listener en die accept-stap in één http_adapter.make_handler_class(handle, state) teruggeeft. Deze les schrijft twee kleine make_server, die de server bouwt, en serve, die hem een vast aantal verbindingen laat beantwoorden en hem dan sluit.
Binden zoals Networks het leerde
HTTPServer krijgt een adres en een handlerklasse:
handler_class = http_adapter.make_handler_class(handle, state)
server = http.server.HTTPServer(("127.0.0.1", 0), handler_class)
Het adres volgt de regels die je voor listeners leerde. 127.0.0.1 is het 0.0.0.0, dat verbindingen accepteert van elk netwerk waarop de machine zit.
make_server(handle, state, *, timeout=1.0) krijgt handle als handle welke functie er ook werd meegegeven, meestal je eigen, zodat een test een andere kan meegeven.
Nog één regel stelt server.timeout in. Dat attribuut begrenst hoe lang de server wacht tot er een client aankomt voordat hij dat wachten opgeeft. Het is een andere time-out dan de SOCKET_TIMEOUT van de adapter, die elke leesactie op een verbinding begrenst zodra er een client is.
Eén aanroep, één verbinding
server.handle_request() wacht op één client, hoogstens server.timeout seconden. Maakt er op tijd een client verbinding, dan beantwoordt het die verbinding, request handler en al, en keert het terug. Komt er niemand, dan keert het stilletjes terug als de time-out afloopt, zonder een serve is dus een for-range(max_connections) met één aanroep van handle_request() erin:
max_connections=3, three clients come
handle_request() waits, client 1 connects, answered
handle_request() waits, client 2 connects, answered
handle_request() waits, client 3 connects, answered
server closed
max_connections=3, server.timeout=1.0, nobody comes
handle_request() waits 1 second, returns
handle_request() waits 1 second, returns
handle_request() waits 1 second, returns
server closed about 3 seconds in all
In beide uitvoeringen doet serve precies drie aanroepen en stopt dan; een aanroep met een time-out telt ook. Dat maakt de uitvoering begrensd: ze eindigt na hoogstens max_connections keer wachten, wat clients ook doen.
HTTPServer heeft ook een serve_forever() heet. Die doet wat de naam zegt en keert pas terug als een ander deel van het programma zegt dat hij moet stoppen. Je server gebruikt hem nooit.
serve(server, max_connections=3) draait met server.timeout = 1.0, en er maakt nooit een client verbinding. Wat gebeurt er?
Sluit hem
make_server geeft de server open terug, en serve sluit hem zodra de lus klaar is, zodat de luisterende socket de uitvoering nooit overleeft. Een server werkt met with zoals je sockets dat deden: het verlaten van een blok with server: roept server.server_close() aan, of het blok nu afliep of een exception opwierp. server.server_close() aanroepen na de lus werkt ook.
pl_http.py
Je hebt één pl_http.py, in je werkruimte, is een hulpprogramma dat voor deze cursus is geschreven en de client speelt. Het roept je make_server aan met je handle en de boeken uit sample_state(), voert je serve op de achtergrond uit, verstuurt de verzoeken van het scenario dat je noemt, en drukt de bytes af die elke kant op gingen. Jij voert het uit; je bewerkt het nooit. Het scenario voor deze les is books, en de client ervan is wire.fetch, de functie die je in hoofdstuk 2 afmaakte. De wire.py in je werkruimte is de afgewerkte versie uit hoofdstuk 3, om te lezen en uit te voeren, niet om te bewerken:
python pl_http.py books
Als beide functies geschreven zijn, zie je dit, met drie regels die hier zijn aangepast: het antwoord en de regel headers zijn ingekort met ..., en de tijd in de logregel is vervangen door woorden.
pl_http: books, your server on 127.0.0.1 (a port the system chose)
--- requests and replies ---
client -> server b'GET /books HTTP/1.1\r\nHost: books.test\r\n\r\n' (41 bytes)
server -> client b'HTTP/1.0 200 OK\r\nServer: ...
--- wire.fetch returned ---
version: 'HTTP/1.0'
status: 200
reason: 'OK'
headers: {'server': [...], 'date': [...], 'content-type': ['application/json'], 'content-length': ['132']}
body: b'{"books": [{"id": 1, "title": "Kindred", "author": "Octavia E. Butler"}, {"id": 2, "title": "Caf\xc3\xa9 Society", "author": "Ana Ruiz"}]}' (132 bytes)
--- your server's log ---
127.0.0.1 - - [the date and time] "GET /books HTTP/1.1" 200 -
--- result ---
serve() returned after 1 connection; the server is closed
pl_http: every thread has stopped
Lees het van boven af. De eerste regel noemt het adres dat je server bond; pl_http vindt de poort zelf en drukt hem nooit af. client -> server is het verzoek dat fetch schreef. server -> client is het antwoord, afgekapt na de eerste 200 bytes: de statusregel HTTP/1.0 200 OK, dan Server, met BaseHTTP en de Python-versie, en Date, allebei toegevoegd door http.server, dan je twee headers, de lege regel en het begin van je body. wire.fetch returned is het record dat je client uit hoofdstuk 1 uit die bytes bouwde. De log is de regel die http.server voor elk verzoek naar standard error schrijft. Het resultaat zegt of serve terugkeerde en of de server werd gesloten.
Als er iets mis is, zegt hetzelfde verslag dat. Als make_server of serve niet geschreven is, zegt het not written yet: Lesson 3. Als serve terugkeert zonder de server te sluiten, zegt het resultaat the server is still open, en eindigt pl_http met status 1. Als serve nooit terugkeert, stopt pl_http met wachten ongeveer 2 seconden nadat de client klaar is, zegt het dat serve() nog draaide, en eindigt het met status 3; er blijft geen server achter. Als binden mislukt, noemt het dat een probleem van de site, maar controleer eerst het adres in je make_server: een adres dat de machine niet heeft, mislukt op dezelfde manier.
wire.fetch verstuurde HTTP/1.1, maar het antwoord zegt HTTP/1.0. Waarom?
Je opdracht
Schrijf
make_server(handle, state, *, timeout=1.0): bouw de handlerklasse methttp_adapter.make_handler_class(handle, state), maak de server methttp.server.HTTPServer(("127.0.0.1", 0), handler_class), stelserver.timeout = timeoutin, en geef de server terug.Schrijf
serve(server, *, max_connections): roep binnenwith server:server.handle_request()max_connectionskeer aan met eenfor-lus.Voer
python pl_http.py booksuit en lees het transcript.
Houd import http.server bovenaan het bestand en schrijf http.server.HTTPServer voluit, zoals hierboven.
Druk daarna op Check my work. Je server beantwoordt nu elk verzoek met de hele /nowhere inbegrepen. De volgende les laat hem nee zeggen.
Opdracht
Schrijf twee reading_server.py.
make_server(handle, state, *, timeout=1.0)bouwt de handlerklasse methttp_adapter.make_handler_class(handle, state), met zijn eigenhandleenstate, maakt de server methttp.server.HTTPServer(("127.0.0.1", 0), handler_class), steltserver.timeout = timeoutin, en geeft de server terug, nog open.serve(server, *, max_connections)roeptserver.handle_request()preciesmax_connectionskeer aan en sluit daarna de server, metwith server:rond deof server.server_close()erna.
Houd de regel import http.server en schrijf http.server.HTTPServer(...) voluit; gebruik geen regel from http.server import. Roep serve_forever() niet aan.
Voer python pl_http.py books uit in de wire.fetch te zien beantwoorden.
Druk daarna op Check my work. Die zoekt in reading_server.py naar een regel from http.server import en een aanroep van serve_forever. Ze voert make_server uit met http.server via de HTTPServer was op ("127.0.0.1", 0), dat server.timeout de meegegeven time-out is (1.0 als er geen wordt meegegeven), en dat één verzoek via de handlerklasse die je meegaf de handle en de state bereikt die make_server kreeg. Ze voert serve uit op een vervangende server met 3, 1 en 5 verbindingen en controleert dat handle_request precies zo vaak werd aangeroepen en dat de server na de laatste aanroep werd gesloten; een serve die blijft aanroepen, mislukt na 50 aanroepen in plaats van door te draaien. Niets uit je terminal wordt beoordeeld.