0%

Weiterleitungen · Abschlussprojekt

Abschlussprojekt: Projekt: Bücher, die umgezogen sind

Du hast beide Hälften einer . Dein Server beantwortet /v0/books-Pfade mit einer 308, die die ID und die Query behält, und dein follow löst ein location auf, behält oder ändert die je nach Status und hört bei einem Hop-Limit auf. Du hast sie zusammen über pl_http.py follow laufen lassen, aber jede Prüfung bisher hat eine Hälfte nach der anderen bewiesen, gegen Ersatzobjekte. Und nichts, was du bisher geschrieben hast, sendet einen Schreibzugriff über deine Folgefunktion: follow nimmt einen Body, aber keine baut einen. Dieses Projekt schreibt diese Funktion, send_json, und zeigt, dass das PUT eines alten Clients über die Weiterleitung deines Servers in einem Hop das gemeinte Buch erreicht.

Was send_json tut

send_json(method, url, data, *, make_connection, max_hops, timeout) ist der Client, den eine alte Leseliste-App aufrufen würde, um ein Buch zu speichern:

  1. Kodiere data als in -Bytes: json.dumps(data, ensure_ascii=False).encode("utf-8"), wie es das json_response deines Servers tut.

  2. Sende es mit follow und übergib dabei den Body, einen Header Content-Type: application/json sowie make_connection, max_hops und timeout genau so, wie send_json sie bekommen hat.

  3. Füge dem Datensatz, den follow zurückgegeben hat, "data" hinzu: den als UTF-8-JSON dekodierten Body, wenn der content-type der Antwort application/json ist ( wie ; charset=utf-8 erlaubt) und der Body nicht leer ist, und sonst None. get_json hat in Kapitel 3 dieselbe Entscheidung getroffen.

Jeder Status ist ein Datensatz, wie bisher. Eine 422 vom Server kommt mit ihrem Fehler in "data" zurück; send_json löst nur aus, was follow auslöst, etwa beim Hop-Limit oder TimeoutError, wenn ein Server stumm bleibt.

Etwa acht Zeilen reichen, weil der schwierige Teil schon in follow steckt. Sieh dir an, was follow mit diesem Body macht, wenn es auf eine Weiterleitung trifft: Nach einer 308 gehen das PUT und sein Body unverändert an den neuen Pfad; nach einer 303 fallen der Body und sein content-type weg. Die 308 deines Servers und die Regel deiner Folgefunktion für 308 müssen übereinstimmen, sonst geht der Schreibzugriff eines alten Clients verloren.

Meilensteine

Schreib zuerst send_json. Starte dann das Szenario agree des Hilfsprogramms:

python pl_http.py agree

Es ruft dein send_json("PUT", "http://books.test/v0/books/2", ...) mit einem neuen Titel für Buch 2 auf, gegen deinen eigenen Server auf diesem Rechner. Du solltest zwei Anfragen sehen und dann die Daten, die dein Server gespeichert hat:

request 1  PUT /v0/books/2  ->  308 Permanent Redirect
request 2  PUT /books/2  ->  200 OK
...
data: {'id': 2, 'title': 'Café Society (2nd edition)', 'author': 'Ana Ruiz'}

Dann sendet das Hilfsprogramm sein eigenes GET /books/2, und auch diese Antwort trägt den neuen Titel: Der Schreibzugriff hat den aktuellen Pfad erreicht, und nur einmal.

Dann die . Starte das Szenario loop:

python pl_http.py loop

Hier betreibt dein Server einen kleinen, für diesen Kurs geschriebenen Handler, der im Kreis weiterleitet: /a antwortet mit 307 nach /b, und /b antwortet mit 307 nach /a. Das Hilfsprogramm ruft dein follow("GET", "http://books.test/a", max_hops=3, ...) auf. Eine korrekte Folgefunktion sendet vier Anfragen und löst ValueError aus; das Protokoll zeigt request 1 bis request 4, alle 307 Temporary Redirect, dann raised ValueError mit deiner Meldung, und das Szenario endet mit Status 0, weil hier der Fehler das richtige Ergebnis ist. Das Hilfsprogramm gibt dem Server fünf Verbindungen und öffnet die eine, die deine Folgefunktion nicht verwendet hat, selbst, was es als pl_http opened 1 empty connection(s) so serve() could stop waiting meldet. Eine Folgefunktion ohne Limit macht weiter, bis der Server aufgehört hat, und scheitert stattdessen mit ConnectionRefusedError.

Optional: Teste send_json selbst. fakes.py hat jetzt scripted_connections(responses, calls): ein make_connection, dessen Verbindungen der Reihe nach mit den übergebenen Antworten antworten, eine pro Verbindung, und für jede Verbindung Host, , Timeout, Anfragen und ob sie geschlossen wurde aufzeichnen. Füge scripted_connections der Zeile from fakes import in tests/test_wire.py hinzu, und einen Test wie diesen:

def test_send_json_follows_a_308():
    calls = []
    moved = scripted_response(308, "Permanent Redirect", [["location", "/books/2"]], b"")
    stored = scripted_response(200, "OK", [["content-type", "application/json"]], b'{"id": 2, "title": "Dune"}')
    connections = scripted_connections([moved, stored], calls)
    record = wire.send_json("PUT", "http://books.test/v0/books/2", {"title": "Dune"}, make_connection=connections)
    assert record["data"] == {"id": 2, "title": "Dune"}
    assert calls[1]["requests"][0]["method"] == "PUT"
    assert calls[1]["closed"]

Führ deine Tests im Projektordner aus:

python -m pytest -q

Sie gehören dir; Check my work führt sie nicht aus.

Was bewiesen wird, und wie

Check my work beweist die Übereinstimmung ohne . Es verbindet dein send_json in einem Prozess mit deinem eigenen reading_server.handle: Die Verbindung, die es deiner Folgefunktion übergibt, baut aus dem, was dein Code sendet, ein Anfrage-, schickt es durch dein read_body und handle, wie es http_adapter täte, und verwandelt das Antwort-Dictionary zurück in eine Antwort. Die 308, die deine Folgefunktion liest, ist also die, die dein Server geschrieben hat, und das PUT, das es wiederholt, ändert den Zustand, den dein Server führt. Die Läufe agree und loop zeigen dasselbe über eine echte Verbindung auf diesem Rechner. Diese Läufe sind zum Zuschauen da; sie werden nicht bewertet.

Drück dann Check my work.

Jede Anfrage in diesem Kurs stand bisher für sich allein: Der Server hat jeden Client in dem Moment vergessen, in dem er geantwortet hat, und kein Client konnte beweisen, wer er ist. Kapitel 7 gibt der Leseliste ein Gedächtnis dafür, wer fragt, mit einer Session in einem und einem Token, das entscheidet, wer schreiben darf.

Aufgabe

Schreib send_json(method, url, data, *, make_connection, max_hops, timeout) in wire.py, damit ein -Schreibzugriff über deine Folgefunktion geht und den aktuellen Pfad erreicht.

  • Kodiere data als JSON in -Bytes.

  • Sende es mit follow(method, url, ...), mit diesem Body, einem Header Content-Type: application/json und den make_connection, max_hops und timeout, die send_json bekommen hat.

  • Gib den Datensatz zurück, den follow zurückgegeben hat, mit hinzugefügtem "data": dem als UTF-8-JSON dekodierten Body, wenn der content-type der Antwort application/json ist (mit oder ohne ) und der Body nicht leer ist, sonst None.

  • Jeder Status ist ein Datensatz. Lass beim Hop-Limit und jede andere Ausnahme aus follow send_json verlassen.

Die deines Servers aus Lektion 3 und dein follow aus Lektion 4 müssen weiterhin funktionieren: Diese Prüfung lässt sie zusammen laufen. Führ python pl_http.py agree und python pl_http.py loop im aus, um sie auf einer echten Verbindung zu beobachten.

Drück dann Check my work. Es übergibt send_json ein make_connection, dessen Verbindungen jede Anfrage als Anfrage- durch dein eigenes read_body und reading_server.handle schicken, mit einem Zustand aus Büchern, die du noch nicht gesehen hast, und das Antwort-Dictionary deines Servers zurück in eine Antwort verwandeln; es wird kein geöffnet. Es prüft, dass ein PUT an einen alten Pfad mit Query das Buch nach genau einer 308 ändert und 200 mit dem gespeicherten Buch in "data" zurückgibt; dass ein POST an /v0/books nach einer 308 mit 201 ein Buch anlegt; dass eine 422 als Datensatz mit dem Fehler in "data" zurückkommt; dass eine Antwort ohne Body oder mit einem Body, der kein JSON ist, für "data" None ergibt; dass ein mit 303 beantwortetes POST als GET ohne Body weitergeht; dass eine Weiterleitungsschleife nach max_hops + 1 Anfragen ValueError auslöst; und dass der Timeout jede Verbindung erreicht. Deine eigenen Tests werden nicht ausgeführt. Nichts aus deinem Terminal wird bewertet.