0%

Wer darf sich verbinden? · Abschlussprojekt

Abschlussprojekt: Projekt: Anzeigetafel v3

Nur wer das Token hat, kann jetzt , und das Token wird vor 101 geprüft. Eine Regel aus Version eins gilt noch: Wenn ein Punktezähler irgendetwas sendet, das keine gültige Wertung ist, schließt scorekeeper_handler die Verbindung mit 1008 und "that was not a valid score". Führe die Demo aus, bevor du etwas änderst, und der Abschnitt zu Lektion 5 endet genau so:

Lesson 5: the scorekeeper's mistakes
    then the connection was closed with [(1008, 'that was not a valid score')]

Der Punktezähler der Demo hat zuerst 11 Punkte gesendet, und damit war sein Gespräch zu Ende. Dieses Projekt stellt Anzeigetafel v3 fertig: Fehler bekommen Antworten statt einer geschlossenen Verbindung, und das Token wird nirgendwo wiederholt.

Anforderungen

Anzeigetafel v3, so wie du sie am Ende dieser Lektion zurücklässt:

  1. Wertungen kommen nur von dem, der das Token hat, geprüft vor 101 durch den Hook aus Lektion 3. Zuschauer brauchen kein Token.

  2. error_message(board, reason) gibt {"type": "error", "version": <the board's current version>, "reason": reason} zurück. Es liest board["version"], während es board["lock"] hält, wie jeder andere Lesezugriff auf die Tafel.

  3. scorekeeper_handler beantwortet jeden mit einem error-Umschlag und liest weiter. Ein ValueError kann von drei Stellen kommen: von parse_message, bei einer Binärnachricht, Text, der kein ist, oder einem ungültigen Umschlag; von der Typprüfung, bei einem gültigen Umschlag, der keine score ist; und von apply_score, bei einem Team, das die Tafel nicht hat. Der Grund ist str(exc), die Meldung der Ausnahme.

  4. Alles andere bleibt: höchstens MAX_SCORE_MESSAGES Nachrichten, die ungültigen mitgezählt; recv(timeout=WAIT), mit einem Schließen mit 1000 und "no score arrived in time", wenn nichts ankommt; eine gültige Wertung, beantwortet mit dem neuen Snapshot; eine stille Rückkehr bei ConnectionClosed; 1000, sobald die Grenze erreicht ist.

  5. Keine Antwort, kein Umschlag, kein Grund beim Schließen und keine ausgegebene Zeile enthält das Token.

Das main aus Kapitel 10 wird den Server mit open_server(board, read_token("token.txt")) bauen, also ist alles oben das, was eine laufende Anzeigetafel tut.

Der Punktezähler sendet 11 Punkte, während die Tafel bei Version 5 steht. Was kommt zurück?

Fehler sind jetzt Nachrichten

Ein Punktezähler ist ein Mensch an einer Tastatur oder eine Seite, die er verwendet, und Menschen vertippen sich. Unter Version eins hat eine getippte 11 den Punktezähler seine Verbindung gekostet: Seine Seite hätte das Schließen bemerken, sich neu verbinden, das Token erneut senden und es noch einmal versuchen müssen, und der Grund beim Schließen allein sagte nichts darüber, welcher Teil falsch war. Ein error-Umschlag hält das Gespräch am Laufen und sagt in seinem Grund, was falsch war, und die nächste Nachricht des Punktezählers wird gelesen, als wäre nichts passiert. Die Tafel ändert sich nicht, also hören die Zuschauer nichts davon: Ein Fehler geht nur an den Punktezähler.

Der Umschlag trägt die Version der Tafel, damit der Client erkennen kann, auf welche Tafel sich die Ablehnung bezieht. Die Seite eines Punktezählers, die “rejected at version 5” neben einer Tafel zeigt, die sie zuletzt bei Version 5 gesehen hat, weiß, dass ihre Ansicht aktuell ist; bei Version 7 weiß sie, dass dazwischen andere Wertungen angekommen sind.

Gründe werden für Menschen geschrieben. str(exc) ist, was immer die Ausnahme gesagt hat, und für Text, der kein JSON ist, ist das die eigene Meldung des JSON-Parsers, Expecting value: line 1 column 1 (char 0), die zutreffend ist, aber nicht freundlich. Für diesen Kurs ist das akzeptabel: Kein Programm sollte einen Grund parsen, und nach type und version richtet sich ein Client.

In scorekeeper_handler schließt die Klausel except ValueError nicht mehr und kehrt nicht mehr zurück. Benenne darin die Ausnahme, except ValueError as exc:, mach dort reply zum Fehler-Umschlag und im try zu einem Snapshot, und sende reply nach der try-Anweisung, einmal, egal was es ist.

Niemals ein Geheimnis wiederholen

Das Token reist in einem Header oder in der Query, und beides erreicht Code, den du schreibst. Eine Antwort, die einen Teil einer Anfrage wiederholt, kann das Token in die Logs des Clients, die Logs des Servers oder auf einen Bildschirm wiederholen, den jemand filmt. Die Regel: Keine Antwort, kein Umschlag, kein Grund beim Schließen und keine ausgegebene Zeile enthält das Token. In der Praxis:

  • der Body der 401 ist der feste Text aus Lektion 3;

  • der Grund eines Fehlers kommt aus dem ValueError, niemals aus connection.request;

  • kein print eines Pfades oder Headers, nicht einmal beim Debuggen, denn der Pfad eines Punktezählers, der sein Token in der Query gesendet hat, enthält das Token. Das eigene Logging der Bibliothek auf ihrer Debug-Stufe schreibt die Anfragezeile und die Header, Token eingeschlossen; dieser Kurs schaltet es nie ein, und ein echter Server sollte es ausgeschaltet lassen.

Was davon könnte das Token wiederholen?

Deine Aufgabe

Schreibe in scoreboard_server.py error_message(board, reason) und schreibe scorekeeper_handler nach den Anforderungen oben um. Lass jede andere , wie sie ist.

Klicke auf Run. Der Abschnitt zu Lektion 5 gibt einem Stellvertreter-Punktezähler vier Nachrichten: 11 Punkte, den Text not json, eine Wertung für ein Team namens Bats, das nicht auf der Tafel der Demo steht, und eine gültige Wertung von 2 für Owls. Eine fertige Version hat auf der Seite ausgegeben:

Lesson 5: the scorekeeper's mistakes
    the scorekeeper was sent {'type': 'error', 'version': 0, 'reason': 'points must be a whole number from 1 to 10'}
    the scorekeeper was sent {'type': 'error', 'version': 0, 'reason': 'Expecting value: line 1 column 1 (char 0)'}
    the scorekeeper was sent {'type': 'error', 'version': 0, 'reason': 'unknown team'}
    the scorekeeper was sent {'type': 'snapshot', 'version': 1, 'scores': {'Owls': 2, 'Foxes': 0}}
    then the connection was closed with [(1000, 'no score arrived in time')]

Drei Fehler bei Version 0, weil sich nichts geändert hat, dann der Snapshot der gültigen Wertung bei Version 1. Die Verbindung blieb durch alle vier hindurch offen; sie wurde erst geschlossen, als der Stellvertreter nichts mehr zu senden hatte und recv in ein Timeout lief.

Klicke dann auf Submit. Anzeigetafel v3 ist funktional vollständig. Das Abschlussprojekt macht daraus ein Programm mit einem schriftlichen Vertrag und einer .

Aufgabe

In scoreboard_server.py:

  1. Schreibe error_message(board, reason): Lies innerhalb von with board["lock"]: board["version"]; gib {"type": "error", "version": version, "reason": reason} zurück.

  2. Schreibe scorekeeper_handler(connection, board) um. Behalte seine Grenze von MAX_SCORE_MESSAGES Nachrichten (eine ungültige zählt mit), recv(timeout=WAIT) mit connection.close(1000, "no score arrived in time") bei TimeoutError, das abschließende close(1000, ...) an der Grenze und die stille Rückkehr bei ConnectionClosed. Ändere nur, was ein bewirkt: Fange ihn als except ValueError as exc: ab, setze reply = error_message(board, str(exc)) und sende reply mit scoreboard_messages.encode_message, genau so, wie der Snapshot einer gültigen Wertung gesendet wird. Schließe die Verbindung bei einem Fehler nicht.

  3. Lass die aus Lektion 2 und Lektion 3 und alles aus Kapitel 8, wie sie sind.

Klicke auf Run: Der Abschnitt zu Lektion 5 gibt drei error-Umschläge bei Version 0 aus, einen Snapshot bei Version 1 und ein Schließen mit 1000.

Klicke dann auf Submit. Die Prüfung führt die Prüfungen aus Lektion 2 und Lektion 3 erneut aus, mit einem Token, das du nicht gesehen hast, und prüft, dass Outbox und Zuschauer aus Kapitel 8 noch funktionieren. Dann registriert sie zwei Zuschauer und gibt deinem scorekeeper_handler zehn Nachrichten, beginnend auf einer Tafel mit einer Version über 0: 11 Punkte, Text, der kein ist, eine Binärnachricht, einen error-Umschlag, eine Wertung für ein Team, das die Tafel nicht hat, eine gültige Wertung, Punkte true, einen snapshot-Umschlag, eine weitere gültige Wertung und eine JSON-, wobei eine elfte Nachricht wartet. Jeder Fehler muss einen error-Umschlag mit der aktuellen Version der Tafel und einem nicht leeren Grund bekommen; die Tafel und die Outboxen der Zuschauer dürfen sich nur bei den beiden gültigen Wertungen ändern, die ihre Snapshots bekommen; der Handler muss zehn Nachrichten lesen, nicht die elfte, und einmal mit 1000 schließen. Der Pfad des Punktezählers enthält das Token in seiner Query, und das Token darf in nichts Gesendetem, in keinem Grund beim Schließen und in keiner ausgegebenen Zeile erscheinen. Um ohne Threads zu laufen, ersetzt die Prüfung scoreboard_server.threading durch eigene Locks und Events; behalte also import threading und erstelle Locks nur innerhalb von Funktionen, wie es make_board und make_outbox tun. Jeder Text für den Grund ist in Ordnung.