Een gesprek op loopback · oefening
De verbinding laten falen
Twee eindes werken nu: beide kanten kunnen sluiten, en beide melden 1000. De onbeschofte client in Run krijgt nog de oude behandeling. Na les 2 luiden zijn regels zo:
A rude client:
client: the server closed the connection without a close frame
server: stopped by ValueError: (1002, 'the frame breaks the masking rule')
De codec deed zijn werk: hij weigerde een niet-gemaskeerd serve_conversation uit, beëindigde de thread van de server, en de client kwam nooit te weten wat hij fout deed. Deze les maakt van die exception een close-frame.
Sluiten tegenover falen
Een close is een gesprek dat beide kanten samen afmaken. RFC 6455 sectie 7.1.7 beschrijft het andere einde, de verbinding laten falen, voor als een peer het protocol heeft gebroken. Het eindpunt dat het merkt, “SHOULD send a Close frame with an appropriate status code” (hoort een close-frame met een passende
Een falende server doet dus drie dingen: hij stuurt één close-frame met de code voor de gebroken regel, hij leest niets meer, en hij keert terug. Deze cursus stuurt die close altijd als de
Codes uit de codec
Sinds het project van hoofdstuk 4 is elke weigering in read_message een ValueError(code, reason). De code is exc.args[0] en de reden exc.args[1]:
1002, protocolfout: een niet-gemaskeerd frame naar een server, gereserveerde bits of
, een controleframe dat te lang of niet de laatste is, een die niet mag reizen; 1003, een bericht dat deze codec niet aankan: elk fragment, omdat de leercodec gefragmenteerde berichten niet samenvoegt;
1007, een tekstbericht of close-reden die geen geldige
is; 1009, bericht te groot: een frame waarvan de header meer dan
MAX_SIZEbytes aankondigt, geweigerd voordat er ook maar één payloadbyte gelezen wordt.
failure_frame(exc) bouwt de close uit die twee ws_frames.close_payload(exc.args[0], exc.args[1]) in een niet-gemaskeerd close-frame. Voor ValueError(1009, "x") is dat:
88 03 03 f1 78
03 f1 is 1009, en 78 is “x”.
Een gemaskeerd binair frame kondigt 2.000.000 bytes aan, meer dan MAX_SIZE. Wat stuurt de server?
Een peer die verdwijnt
Twee andere exceptions kunnen een leesactie beëindigen, en geen van beide is een gebroken regel:
ConnectionError:read_exactkreegb""omdat de peer desloot zonder close-frame, zoals de client van les 1 deed; TimeoutError: de peer stuurde 2 seconden niets, de time-out die de bedrading op elke geaccepteerde socket zet.
In beide gevallen is er niemand om een close naar te sturen, of geen reden om te denken dat een close gelezen zou worden. De server stuurt niets en meldt de code 1006, die RFC 6455 precies hiervoor reserveert: de verbinding eindigde zonder close-frame. 1006 verschijnt alleen ooit in meldingen. Net als 1005 mag hij nooit in een frame reizen.
De verbinding van de client valt midden in een gesprek weg. Wat stuurt de server?
Je opdracht
Schrijf failure_frame(exc). Zet daarna de serve_conversation, de sluitstappen erna inbegrepen, in try, met twee handlers:
def serve_conversation(sock, read_exact, *, max_messages=MAX_MESSAGES):
count = 0
try:
... # Lesson 2's loop and closing steps, unchanged
except ValueError as exc:
sock.sendall(failure_frame(exc))
return {"messages": count, "code": exc.args[0]}
except (ConnectionError, TimeoutError):
return {"messages": count, "code": 1006}
count is de naam die je lus gebruikt voor de berichten die tot nu toe zijn geëchood. Twee aparte clausules except ConnectionError en except TimeoutError werken net zo goed. Houd de handlers bij deze exceptiontypes: een brede except Exception zou een gebroken regel behandelen als een verdwenen peer en helemaal geen close sturen.
Waarom niet wachten
Na een close in les 2 wachtte de server op het antwoord. Na een falen doet hij dat niet. Wachten zou betekenen dat hij de peer nog één keer vertrouwt. Een client die een niet-gemaskeerd frame stuurde, kan een kapot programma zijn of iets wat helemaal geen
Waarom wacht de server na het laten falen van de verbinding niet op de close van de client?
Run
Druk op Run. Na deze les worden de regels van de onbeschofte client:
A rude client:
client: {'type': 'close', 'code': 1002, 'reason': 'the frame breaks the masking rule'}
server: {'messages': 0, 'code': 1002}
server thread still running: False
De onbeschofte client leest nu een close-frame dat zijn fout noemt, en de thread van de server eindigt met een resultaat in plaats van een exception. Het eerste gesprek meldt nog steeds 1000 aan beide kanten.
Druk daarna op Submit. Het geeft serve_conversation frames die elke regel hierboven breken, een header die te veel bytes aankondigt met niets erachter, en clients die verdwijnen of stil worden na één bericht, en controleert de close die je stuurt, de code die je teruggeeft, en dat er na een falen niets gelezen wordt.
Je server heeft nu een antwoord op elke manier waarop een gesprek kan eindigen. Tot nu toe heb je dat in Run gezien en heeft Submit het gecontroleerd; de volgende les schrijft eigen tests die het bewijzen, over twee verbonden sockets zonder enige
Opdracht
Schrijf één loopback_demo.py.
failure_frame(exc): geef het niet-gemaskeerde close-terug voor een ValueError(code, reason):ws_frames.close_payload(exc.args[0], exc.args[1])binnenws_frames.encode_frame(ws_frames.OP_CLOSE, ...).serve_conversation: zet deen de sluitstappen erna in try. Stuur bijValueError as excfailure_frame(exc)en geef meteen{"messages": n, "code": exc.args[0]}terug, zonder opnieuw te lezen. Stuur bijConnectionErrorofTimeoutErrorniets en geef{"messages": n, "code": 1006}terug. Vang alleen deze, niet Exception.
Druk op Run: de onbeschofte client moet nu {'type': 'close', 'code': 1002, ...} ontvangen, en het eerste gesprek moet nog steeds 1000 aan beide kanten melden.
Druk daarna op Submit. De controle roept failure_frame rechtstreeks aan, en geeft serve_conversation stand-in-clients die een niet-gemaskeerde tekst sturen, een tekst die geen