0%

Wie mag er verbinding maken? · eindproject

Eindproject: Project: scorebord v3

Alleen de houder van het token kan nu scoren, en het token wordt gecontroleerd vóór 101. Eén regel uit versie één staat nog: als een scorebijhouder iets stuurt wat geen geldige score is, sluit scorekeeper_handler de verbinding met 1008 en "that was not a valid score". Draai de demo voordat je iets verandert, en de sectie van les 5 eindigt precies zo:

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

De scorebijhouder van de demo stuurde eerst 11 punten, en daarmee was zijn gesprek voorbij. Dit project maakt scorebord v3 af: fouten krijgen antwoorden in plaats van een gesloten verbinding, en het token wordt nergens herhaald.

Eisen

Scorebord v3, zoals je het aan het eind van deze les achterlaat:

  1. Scores komen alleen van de houder van het token, gecontroleerd vóór 101 door de hook uit les 3. Kijkers hebben geen token nodig.

  2. error_message(board, reason) geeft {"type": "error", "version": <the board's current version>, "reason": reason} terug, met op de plek van de placeholder de huidige versie van het bord. Hij leest board["version"] terwijl hij board["lock"] vasthoudt, zoals elke andere leesactie van het bord.

  3. scorekeeper_handler beantwoordt elke met een error-envelop en blijft lezen. Een ValueError kan van drie plekken komen: parse_message, voor een binair bericht, tekst die geen is of een ongeldige envelop; de typecontrole, voor een geldige envelop die geen score is; en apply_score, voor een team dat het bord niet heeft. De reden is str(exc), de melding van de exception.

  4. Al het andere blijft: hoogstens MAX_SCORE_MESSAGES berichten, de ongeldige meegeteld; recv(timeout=WAIT), met een close 1000 en "no score arrived in time" als er niets binnenkomt; een geldige score beantwoord met de nieuwe snapshot; een stille terugkeer bij ConnectionClosed; 1000 zodra de limiet is bereikt.

  5. Geen enkel antwoord, geen envelop, geen sluitreden en geen afgedrukte regel bevat het token.

De main van hoofdstuk 10 bouwt de server met open_server(board, read_token("token.txt")), dus alles hierboven is wat een draaiend scorebord doet.

De scorebijhouder stuurt 11 punten terwijl het bord op versie 5 staat. Wat komt er terug?

Fouten zijn nu berichten

Een scorebijhouder is een persoon achter een toetsenbord, of een pagina die die persoon gebruikt, en mensen tikken verkeerd. Onder versie één kostte een getypte 11 de scorebijhouder zijn verbinding: zijn pagina moest de close opmerken, opnieuw verbinding maken, het token opnieuw sturen en het nog eens proberen, en de sluitreden alleen zei niets over welk deel fout was. Een error-envelop houdt het gesprek gaande en zegt in zijn reden wat er fout was, en het volgende bericht van de scorebijhouder wordt gelezen alsof er niets gebeurd is. Het bord verandert niet, dus kijkers horen er niets over: een error gaat alleen naar de scorebijhouder.

De envelop draagt de versie van het bord, zodat de client kan zien over welk bord de weigering gaat. Een scorebijhouderspagina die “rejected at version 5” toont naast een bord dat ze voor het laatst op versie 5 zag, weet dat haar beeld actueel is; bij versie 7 weet ze dat er tussendoor andere scores binnenkwamen.

Redenen zijn voor mensen geschreven. str(exc) is wat de exception ook zei, en voor tekst die geen JSON is, is dat de eigen melding van de JSON-parser, Expecting value: line 1 column 1 (char 0), die klopt maar niet vriendelijk is. Dat is voor deze cursus acceptabel: geen enkel programma hoort een reden te parsen, en het type en de version zijn waar een client op handelt.

In scorekeeper_handler sluit de clausule except ValueError niet meer en keert ze niet meer terug. Geef de exception erin een naam, except ValueError as exc:, maak daar van reply de error-envelop en in de try een snapshot, en stuur reply na het try-statement, één keer, welke van de twee het ook is.

Herhaal nooit een geheim

Het token reist in een header of in de query, en beide bereiken code die je schrijft. Een antwoord dat een deel van een verzoek herhaalt, kan het token herhalen in de logs van de client, de logs van de server, of op een scherm dat iemand filmt. De regel: geen enkel antwoord, geen envelop, geen sluitreden en geen afgedrukte regel bevat het token. In de praktijk:

  • de body van de 401 is de vaste tekst uit les 3;

  • de reden van een error komt uit de ValueError, nooit uit connection.request;

  • geen print van een pad of header, ook niet tijdens het debuggen, want het pad van een scorebijhouder die zijn token in de query stuurde, bevat het token. De eigen logging van de bibliotheek op debugniveau schrijft de verzoekregel en de headers, token inbegrepen; deze cursus zet die nooit aan, en een echte server hoort haar uit te laten.

Welke hiervan zou het token kunnen herhalen?

Je opdracht

Schrijf in scoreboard_server.py error_message(board, reason) en herschrijf scorekeeper_handler volgens de eisen hierboven. Laat elke andere zoals hij is.

Klik op Run. De sectie van les 5 geeft een stand-in-scorebijhouder vier berichten: 11 punten, de tekst not json, een score voor een team met de naam Bats, dat niet op het bord van de demo staat, en een geldige score van 2 voor Owls. Een afgemaakte versie, op de site uitgevoerd, drukte af:

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')]

Drie errors op versie 0, omdat er niets veranderde, en daarna de snapshot van de geldige score op versie 1. De verbinding bleef open tijdens alle vier; ze sloot pas toen de stand-in niets meer te sturen had en recv een time-out kreeg.

Klik daarna op Submit. Scorebord v3 is functioneel compleet. Het eindproject maakt er een programma van met een geschreven contract en een .

Opdracht

In scoreboard_server.py:

  1. Schrijf error_message(board, reason): lees binnen with board["lock"]: board["version"]; geef {"type": "error", "version": version, "reason": reason} terug.

  2. Herschrijf scorekeeper_handler(connection, board). Houd de limiet van MAX_SCORE_MESSAGES berichten (een ongeldig bericht telt mee), recv(timeout=WAIT) met connection.close(1000, "no score arrived in time") bij TimeoutError, de laatste close(1000, ...) bij de limiet, en de stille terugkeer bij ConnectionClosed. Verander alleen wat een doet: vang hem als except ValueError as exc:, zet reply = error_message(board, str(exc)), en stuur reply met scoreboard_messages.encode_message, precies zoals de snapshot van een geldige score wordt gestuurd. Sluit de verbinding niet bij een fout.

  3. Laat de van les 2 en les 3 en alles uit hoofdstuk 8 zoals ze zijn.

Klik op Run: de sectie van les 5 drukt drie error-enveloppen op versie 0 af, een snapshot op versie 1, en een close met 1000.

Klik daarna op Submit. De controle draait de controles van les 2 en les 3 opnieuw, met een token dat je nog niet hebt gezien, en controleert dat de outbox en de kijker uit hoofdstuk 8 nog werken. Daarna registreert ze twee kijkers en geeft ze je scorekeeper_handler tien berichten, beginnend op een bord met een versie boven 0: 11 punten, tekst die geen is, een binair bericht, een error-envelop, een score voor een team dat het bord niet heeft, een geldige score, punten true, een snapshot-envelop, nog een geldige score, en een JSON-, met een elfde bericht dat wacht. Elke fout moet een error-envelop krijgen met de huidige versie van het bord en een reden die niet leeg is; het bord en de outboxen van de kijkers mogen alleen veranderen voor de twee geldige scores, die hun snapshots krijgen; de handler moet tien berichten lezen, niet het elfde, en één keer sluiten met 1000. Het pad van de scorebijhouder bevat het token in zijn query, en het token mag in niets voorkomen wat wordt verstuurd, in geen sluitreden en in geen afgedrukte regel. Om zonder threads te draaien, vervangt de controle scoreboard_server.threading door haar eigen locks en events, dus houd import threading en maak locks alleen binnen functies, zoals make_board en make_outbox doen. Elke redentekst is prima.