Wer darf sich verbinden? · Übung
Vor dem Upgrade ablehnen
check_request weiß, wer handle_connection läuft, hat die Bibliothek schon 101 gesendet, also kommt der Handler zu spät, um mit einem Status abzulehnen. Die Entscheidung muss im
Der Anfrage-Hook
serve nimmt ein Schlüsselwortargument, process_request: eine process_request(connection, request) aufruft, nachdem sie die Anfrage gelesen hat und bevor sie irgendeine Antwort schreibt. request.path ist das ganze Anfrageziel samt Query, und request.headers ist dasselbe Headers-
Nonebedeutet “mach weiter”: Die Bibliothek antwortet101und ruft deinen Handler auf, wie bisher immer.Ein Antwortobjekt bedeutet “sende stattdessen das hier”: Die Bibliothek schreibt diese Antwort, schließt die Verbindung und ruft deinen Handler nie auf.
GET /score, no token
process_request returns a response:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer
no WebSocket connection; the handler never runs
GET /watch
process_request returns None:
HTTP/1.1 101 Switching Protocols
then frames flow, and viewer_handler runs
In Worten: Zwei Handshakes kommen nebeneinander an. Der an /score ohne Token wird mit 401 und einer Bearer-Aufforderung beantwortet, und es entsteht nie eine /watch passiert den Hook, bekommt seine 101 und wird ein Zuschauer.
Dein process_request gibt für eine /watch-Anfrage None zurück. Was passiert als Nächstes?
Eine Antwort, die die Bibliothek baut
Die Bytes der Antwort schreibst du nicht selbst. Die Verbindung, die der Hook bekommt, hat dafür eine connection.respond(status, text) gibt eine Antwort mit diesem Status zurück, mit einem Klartext-Body, der text enthält, und mit den Headern, die die Bibliothek hinzufügt (Connection: close, Content-Length, Content-Type: text/plain; charset=utf-8, ein Date und ein Server). Die headers der Antwort lassen sich noch ändern, bevor du sie zurückgibst, und eine 401 sollte eine Aufforderung (Challenge) tragen, den Header, der dem Client sagt, wie er sich authentifizieren soll. Die Server in www-authenticate: Bearer gesendet; hier:
response = connection.respond(401, "a valid scorekeeper token is needed\n")
response.headers["WWW-Authenticate"] = "Bearer"
Der Hook gibt dann response zurück.
Was sieht ein Client? In einem Lauf auf einem lokalen Computer hat ein Hook, der so eine Antwort zurückgab (mit dem kürzeren Text “a valid token is needed” und einem Zeilenumbruch), den Python-Client der Bibliothek InvalidStatus auslösen lassen, mit der Meldung server rejected WebSocket connection: HTTP 401. Seine Antwort hatte den Status 401, den Grund Unauthorized, die Header Connection: close, Content-Length: 24 (die Länge dieses Textes; der Text dieser Lektion ergibt 36), Content-Type: text/plain; charset=utf-8 und WWW-Authenticate: Bearer und genau diesen Text als Body. Der Handler wurde für diese /score-Anfrage nie aufgerufen, für eine /watch-Anfrage dagegen schon.
Was löst der Client der Bibliothek aus, wenn der Hook des Servers 401 antwortet?
Eine Funktion, die eine Funktion erstellt
Der Hook nimmt genau zwei connection und request, und die Bibliothek wählt sie. Das erwartete Token muss ihn auf andere Weise erreichen. make_handler(board) hatte denselben Bedarf: Es gibt einen verschachtelten handler(connection) zurück, der sich board merkt. make_request_check(expected) macht dasselbe mit dem Token. Es definiert ein verschachteltes process_request(connection, request), das check_request(request.path, request.headers, expected) aufruft, None zurückgibt, wenn diese Antwort None ist, und andernfalls die 401-Antwort mit ihrer Aufforderung baut und zurückgibt. Dann gibt make_request_check diese verschachtelte Funktion zurück, ohne sie aufzurufen. Das Token wird einmal gewählt, wenn der Server gebaut wird, und jeder Handshake danach wird damit geprüft.
In Kapitel 2 hat check_upgrade die Bytes der Antwort selbst zurückgegeben, Statuszeile und Header von Hand ausgeschrieben. Hier ist die Entscheidung deine und die Bytes sind die der Bibliothek.
Einbauen
open_server(board, *, serve=serve) wird zu open_server(board, token, *, serve=serve), und sein Aufruf von serve bekommt neben den process_request=make_request_check(token). Das main aus Kapitel 10 wird das Token mit read_token lesen und hier übergeben.
Der Text der Antwort nennt das Problem und sonst nichts. Er enthält nie das erwartete Token, und er wiederholt nie, was der Client gesendet hat. Das könnte ein echtes Token mit einem Tippfehler sein oder ein Token, das für einen anderen Dienst gedacht ist.
Warum sagt der Body der 401 "a valid scorekeeper token is needed", statt zu zitieren, was gesendet wurde?
Deine Aufgabe
Schreibe in scoreboard_server.py make_request_check(expected) wie beschrieben und gib open_server seinen token und das Argument process_request. check_request aus Lektion 2 trifft weiterhin die Entscheidung.
Klicke auf Run. Der Abschnitt zu Lektion 3 baut deinen Hook mit dem erwarteten Token der Demo und ruft ihn zweimal mit einer Stellvertreter-Verbindung auf. Das respond dieses Stellvertreters gibt ein einfaches Objekt mit status_code, einem headers-body zurück, nicht die Antwort der Bibliothek, damit die Demo keinen Server braucht; der Lauf oben zeigt die echte. Eine fertige Version hat auf der Seite ausgegeben:
Lesson 3: the request hook
/score without a token: 401, WWW-Authenticate: Bearer, body 'a valid scorekeeper token is needed\n'
/watch: None (the handshake goes on to 101)
Klicke dann auf Submit.
Ein Punktezähler ohne das Token wird jetzt vor 101 abgelehnt, und Zuschauer brauchen weiterhin nichts. Die nächste Lektion stellt eine andere Frage über einen Handshake: nicht, wer ihn gesendet hat, sondern welche Webseite den Browser dazu gebracht hat, ihn zu senden.
Aufgabe
In scoreboard_server.py:
Schreibe
make_request_check(expected). Definiere darinprocess_request(connection, request): Rufecheck_request(request.path, request.headers, expected)auf; wenn esNonezurückgibt, gibNonezurück; rufe andernfalls einmalconnection.respond(status, "a valid scorekeeper token is needed\n")auf, setze auf demresponse.headers["WWW-Authenticate"] = "Bearer"und gib diese Antwort zurück.make_request_checkgibt die verschachteltezurück, ohne sie aufzurufen. Ändere
open_server(board, *, serve=serve)zuopen_server(board, token, *, serve=serve)und füge seinem Aufrufserve(...)process_request=make_request_check(token)hinzu, wobei du den Handler,"127.0.0.1",0 und die drei - behältst.
Klicke auf Run: Der Abschnitt zu Lektion 3 gibt für /score ohne Token 401, WWW-Authenticate: Bearer und den Body-Text aus und für /watch None.
Klicke dann auf Submit. Die Prüfung ruft deinen Hook so auf, wie es die Bibliothek tut, mit einer Stellvertreter-Verbindung, deren respond eine echte Antwort der Bibliothek zurückgibt, und mit erwarteten Tokens, die du nicht gesehen hast. Ein /score-respond-Aufruf und eine 401-Antwort bekommen, deren WWW-Authenticate-Header Bearer nennt und deren Body und Header weder das erwartete Token noch den gesendeten Wert enthalten. Das richtige Token, in einem Header oder in der Query, und /watch müssen None ergeben, ohne respond aufzurufen. Der Hook darf weder send noch close aufrufen: Vor 101 gibt es keine check_request durch eines, das alles ablehnt, um zu sehen, dass der Hook es fragt. open_server bekommt ein Stellvertreter-serve, und das process_request, das es übergibt, muss sich für das Token, das open_server bekommen hat, wie oben verhalten. Jeder Body-Text ohne Token ist in Ordnung, und ebenso der Header-Name in beliebiger Groß- und Kleinschreibung.