Maskers en controleframes · oefening
Zeg waarom je sluit
Je decoder herkent een close-
Wat een close-payload bevat
RFC 6455 sectie 5.5.1 staat twee vormen toe. De payload is ofwel leeg, ofwel begint hij met een unsigned code van 2 bytes in
88 02 03 e8
88 06 03 e8 64 6f 6e 65
88 is FIN plus 03 e8 is 1000 als big-endian getal van 16 bits, dat struct.pack("!H", 1000) bouwt en struct.unpack("!H", ...)[0] terugleest, precies zoals bij de lengte van 16 bits in hoofdstuk 3. 64 6f 6e 65 is “done” in UTF-8. Een close is een controleframe, dus de hele payload is hooguit 125 bytes, en de reden krijgt wat de code overlaat: hooguit 123 bytes.
Wat drukt dit af?
import struct
packed = struct.pack("!H", 1001).hex(" ")
number = struct.unpack("!H", bytes([0x03, 0xF1]))[0]
print(packed, number)
De codes die deze cursus gebruikt
Sectie 7.4.1 van de RFC definieert wat de codes in de reeks 1000 betekenen. Dit zijn de codes die deze cursus stuurt of leest:
| Code | Betekenis | Verstuurd in een close-frame? |
|---|---|---|
| 1000 | normale afsluiting: het gesprek is klaar | ja |
| 1001 | going away (gaat weg), bijvoorbeeld een server die afsluit | ja |
| 1002 | protocolfout: de peer brak een framingregel | ja |
| 1003 | niet-ondersteunde data: het eindpunt kan dit soort data niet accepteren | ja |
| 1005 | er stond geen | nooit; gemeld door een ontvanger |
| 1006 | de verbinding eindigde zonder enig close-frame | nooit; gemeld door een ontvanger |
| 1007 | ongeldige payloaddata, zoals een tekstbericht dat geen UTF-8 is | ja |
| 1008 | beleidsschending, de algemene weigering als geen andere code past | ja |
| 1009 | bericht te groot | ja |
| 1011 | de server liep tegen een onverwachte toestand aan | ja |
1005 en 1006 zijn anders dan de rest. Ze beschrijven wat een ontvanger zag, en ze bestaan zodat een programma “de peer stuurde een lege close” of “de verbinding hield gewoon op” kan melden met een getal, net als elk ander einde. Ze reizen nooit in een close-frame.
Codes die niet mogen reizen
De tabel van de RFC is niet de hele
onder 1000: niet gebruikt;
1004: gereserveerd;
1005, 1006 en 1015: labels die een eindpunt over zichzelf meldt, nooit verstuurd (1015 betekent dat de
- mislukte); 1016 tot en met 2999: aan niemand toegewezen;
3000 tot en met 3999: geregistreerd door bibliotheken en frameworks;
4000 tot en met 4999: voor privégebruik door een applicatie.
Dan blijven 1000 tot en met 1003, 1007 tot en met 1014, en 3000 tot en met 4999 over als de codes die in een close-frame mogen verschijnen. Deze verzameling is, toen deze cursus werd geschreven, vergeleken met de controle die de bibliotheek websockets (die je vanaf hoofdstuk 6 gebruikt) op sluitcodes toepast, en de twee zijn het over elke code eens; ze is niet vergeleken met de live registerpagina.
Een server sluit met 1012 (service restart). Accepteert parse_close_payload het?
Wat is de langste reden die een close-frame kan dragen?
Een close lezen
parse_close_payload maakt van de payload weer {"code": ..., "reason": ...}, en het moet vier gevallen uit elkaar houden:
Leeg. Geldig. Er was geen code aanwezig, dus meld 1005 met de reden
"". Dit is het label van de ontvanger in actie: de peer stuurde geen 1005, hij stuurde niets.Eén byte. Ongeldig: er is geen ruimte voor een code van 2 bytes.
Een code die niet mag reizen, zoals 1004 of 1005. Ongeldig: de peer brak de regels.
Een reden die geen UTF-8 is. Ongeldig.
.decode("utf-8")werpt bij zulke bytesUnicodeDecodeErrorop, en die mag je laten gaan:UnicodeDecodeErroris een soort, dus een aanroeper die ValueErrorvangt, vangt hem ook.
try:
bytes.fromhex("c3 28").decode("utf-8")
except ValueError:
print("a UnicodeDecodeError, caught as a ValueError")
Dezelfde controles beschermen de andere richting. close_payload(code, reason="") weigert een payload te bouwen met een code die niet verstuurd mag worden, of met een reden die de payload langer maakt dan 125 bytes, geteld na het coderen.
Het close-frame van een peer heeft een lege payload. Welke code meldt parse_close_payload?
Je opdracht
Schrijf in ws_frames.py de drie
close_code_allowed(code):Truevoor 1000 tot en met 1003, 1007 tot en met 1014 en 3000 tot en met 4999,Falsevoor elke andere code.close_payload(code, reason=""): de code als 2 bytes in netwerkbytevolgorde, daarna de reden in UTF-8. WerpValueErrorop voor een code dieclose_code_allowedweigert, en voor een payload van meer dan 125 bytes.parse_close_payload(payload):{"code": 1005, "reason": ""}voor een lege payload; anders de code van 2 bytes en de reden gedecodeerd als UTF-8. WerpValueErrorop voor een payload van één byte en voor een code die niet mag reizen; een reden die geen UTF-8 is, magUnicodeDecodeErroropwerpen.
Druk op Run. De eerste sectie van les 4 bouwt een payload voor close 1000 “done” en leest hem terug. De tweede probeert hetzelfde met 1005, en show drukt de ValueError af die je close_payload opwerpt; de melding is van jou, dus die van jou kan anders luiden dan deze:
Lesson 4: close 1000 and back
{'payload': '03 e8 64 6f 6e 65', 'parsed': {'code': 1000, 'reason': 'done'}}
Lesson 4: close 1005, which no one may send
ValueError: close code 1005 may not be sent
Lesson 6: what a server reads
not written yet
Druk daarna op Submit. De controle vraagt close_code_allowed naar elke code van 0 tot en met 5.000. Ze bouwt payloads voor de codes van de RFC en voor een code en reden die je nog niet hebt gezien, waaronder een met een reden van precies 123 bytes en een die een byte langer is, en verwacht ValueError voor codes die niet verstuurd mogen worden. Ze leest payloads met codes uit het register en redenen in UTF-8, en verwacht ValueError voor een payload van één byte, codes die niet mogen reizen, en een reden die geen UTF-8 is. Elk frame tot nu toe droeg een heel bericht; de volgende les bekijkt berichten die in stukken aankomen.
Opdracht
Schrijf de drie ws_frames.py:
close_code_allowed(code):Truevoor 1000 tot en met 1003, 1007 tot en met 1014 en 3000 tot en met 4999;Falsevoor elke andere code.close_payload(code, reason=""): de code als 2 bytes in, daarna de reden gecodeerd als . Werp op voor een code die close_code_allowedweigert, en voor een payload langer dan 125 bytes (een reden van hooguit 123 bytes na het coderen).parse_close_payload(payload):{"code": 1005, "reason": ""}voor een lege payload; anders{"code": ..., "reason": ...}met de big-endian code van 2 bytes en de rest gedecodeerd als UTF-8. WerpValueErrorop voor een payload van één byte en voor een code die niet mag reizen. Een reden die geen UTF-8 is, magUnicodeDecodeErroropwerpen, en dat is eenValueError.
Druk op Run: de secties van les 4 moeten {'payload': '03 e8 64 6f 6e 65', 'parsed': {'code': 1000, 'reason': 'done'}} afdrukken en daarna je ValueError voor 1005. Druk daarna op Submit. De controle vergelijkt close_code_allowed met de regel voor elke code van 0 tot en met 5.000, bouwt de payloads van de RFC en een onbekende met een vinkje als reden, accepteert een reden van 123 bytes en weigert een van 124 bytes, weigert codes zoals 1004, 1005, 1006, 1015, 999, 2999 en 5000 in beide richtingen, leest onbekende codes 1012, 1014, 3000 en 4999 met UTF-8-redenen, en weigert een payload van één byte en een reden die geen UTF-8 is. Je resultaatdict mag extra sleutels bevatten.