HTTP selbst ausliefern · Übung
HEAD wie GET beantworten
In Kapitel 1 hat dein read_body nach einer Anfrage HEAD b"" zurückgegeben, ohne etwas zu lesen, weil eine Antwort auf HEAD keinen Body hat, auch wenn ihr Kopf eine Länge ankündigt. Da hat der Client dem Server vertraut. Jetzt bist du der Server, und dein handle beantwortet HEAD mit 405, weil Lektion 4 nur GET erlaubt hat. Führe das Szenario für diese Lektion aus, ein GET /books und danach ein HEAD /books:
python pl_http.py head
Das GET bekommt die HEAD bekommt 405 Method Not Allowed mit allow: GET, und unter dieser Antwort gibt pl_http eine weitere Zeile aus, die die Bytes zählt, die followed the reply, und sie zeigt: deinen Fehler-Body. pl_http liest, wie jeder Client, nach HEAD keinen Body, also sind diese Bytes überzählig; sie sind angekommen, weil nichts zwischen deinem handle und dem http.server sendet es, auch nach HEAD. Nach HEAD keinen Body zu senden ist deine Aufgabe.
Eine Regel am Ende von handle
HEAD fragt nach genau dem, was GET bekommen würde, ohne den Body: derselbe Status und dieselben Header. Ein Client benutzt es, um zu sehen, ob etwas existiert oder wie groß es ist, ohne es herunterzuladen. HEAD /books ist also ein 200 mit den Headern der Liste, und HEAD /nowhere ist ein 404 mit den Headern der Fehlerantwort.
Am einfachsten bekommst du all das richtig hin mit einer Regel am Ende von handle, nach dem Routing. Route HEAD genau wie GET, halte die Antwort in einer
if method == "HEAD":
response["body"] = b""
return response
Jeder Zweig darüber, die Liste, das 404 und das 405, beantwortet HEAD dann so, wie er GET beantwortet, ohne den Body. Eine Regel, die nur im Zweig für /books steht, würde nach HEAD /nowhere immer noch den Body des 404 senden.
Warum steht die HEAD-Regel nach dem Routing statt im Zweig für /books?
Die Länge beschreibt weiterhin GET
Die Regel leert den Body und lässt die Header in Ruhe, also bleibt content-length, was es war: die Länge des Bodys, den GET senden würde. Die Definition von HEAD in GET gesendet hätte, und erlaubt ihm, Felder wegzulassen, deren content-length bei einer Antwort auf HEAD also senden oder weglassen, aber wenn er es sendet, muss der Wert die Länge sein, die GET senden würde (Abschnitt 8.6). Die Regel dieses Servers, festgehalten in SERVICE_CONTRACT.md, ist, es zu senden, weil ein Client so die Größe ohne den Download erfährt. Eine Länge von 0 würde behaupten, dass die Liste leer ist.
Die Client-Seite ist schon sicher. http.client liest nach HEAD keinen Body, egal was die Länge sagt, und dein wire.py hat das auch nicht getan. Ein trotzdem gesendeter Body bleibt ungelesen in der Verbindung. Unter HTTP/1.0 schließt der Server nach einer Antwort, und die verirrten Bytes verschwinden mit der Verbindung. Ab Kapitel 9 trägt eine Verbindung mehrere Anfragen, und diese Bytes würden als Anfang der nächsten Antwort gelesen.
GET /books sendet 132 Body-Bytes. Welches content-length trägt HEAD /books auf diesem Server?
Deine Aufgabe
Setze am Anfang von
handlemethod = request["method"]. Route dannHEADso, wie duGETroutest: Auf/booksantwortetmethod == "GET" or method == "HEAD"mit der Liste.Halte die Antwort jedes Zweigs in einer Variablen,
response, und gib sie einmal zurück, am Ende.Setze direkt vor diesem
returnresponse["body"] = b"", wenn dieHEADist.Ändere das 405 zu
not_allowed("GET, HEAD"), weil/booksjetzt beides unterstützt.
Führe python pl_http.py head noch einmal aus und vergleiche die beiden Zeilen server -> client. Die Statuszeile und jeder Header sind in beiden gleich, einschließlich content-length: 132. Die Antwort auf GET geht mit dem Body weiter; die Antwort auf HEAD hört bei der Leerzeile auf, und keine Zeile folgt ihr.
Drücke dann Check my work. Dein Server kann die Bücher auflisten und HEAD beantworten, aber noch kein einzelnes Buch zeigen. Die Projektlektion fügt /books/{id} hinzu.
Aufgabe
Erweitere handle in reading_server.py, sodass HEAD genau das bekommt, was GET bekommen würde, ohne den Body.
Route
HEADwieGET: Auf/booksantwortenGETundHEADbeide mit der. Halte die Antwort jedes Zweigs in einer
und gib sie einmal zurück, am Ende von handle. Setze direkt vor diesemden Body auf b"", wenn dieHEADist, damit die Regel jeden Pfad abdeckt, auch das 404 und das 405.Lass die Header in Ruhe:
content-lengthin einer Antwort aufHEADist die Länge des Bodys, denGETsenden würde.Beantworte andere Methoden auf
/booksmitnot_allowed("GET, HEAD").
Führe im python pl_http.py head aus und vergleiche die Antworten auf GET und HEAD.
Drücke dann Check my work. Die Prüfung ruft handle mit eigenen States auf und vergleicht jede Antwort auf HEAD mit der Antwort auf GET für dasselbe Ziel: für /books, für ein kodiertes /books mit einer Query, für eine leere Liste und für Pfade, die mit 404 antworten. Jede Antwort auf HEAD muss denselben Status und dieselben Header haben wie die Antwort auf GET, mit content-length gleich der Länge des Bodys von GET, und einen leeren Body. Antworten auf GET müssen ihre Bodys behalten, und DELETE, PUT und POST auf /books müssen mit 405 antworten, mit einem Header allow, der sowohl GET als auch HEAD nennt. Nichts aus deinem Terminal wird bewertet, und kein Server wird gestartet.