Hoofdstuk 9
Wie mag er verbinding maken?
Hoe een browserclient eruitziet
Scorebord v2 houdt elke kijker bij en laat kijkers vallen die niet meer antwoorden. Het laat ook iedereen die /score bereikt de scores veranderen: scorekeeper_handler verwerkt een score van wie er ook verbinding maakte, en niets vraagt wie dat is. Dit hoofdstuk laat de server het vragen. Kijk eerst naar wie er verbinding zou maken. De echte kijkers van het scorebord zouden webpagina’s zijn, en een webpagina spreekt
Een kijkerspagina en een scorebijhouderspagina
Hier is het deel van een kijkerspagina dat met het scorebord praat, in
const socket = new WebSocket("ws://127.0.0.1:8765/watch");
socket.onmessage = (event) => {
const snapshot = JSON.parse(event.data);
showScores(snapshot.version, snapshot.scores);
};
En het deel van een scorebijhouderspagina dat een score stuurt:
const keeper = new WebSocket("ws://127.0.0.1:8765/score");
keeper.onopen = () => {
keeper.send(JSON.stringify({type: "score", team: "Owls", points: 3}));
};
Deze fragmenten zijn voor deze les geschreven. Ze worden nergens op deze site geserveerd of uitgevoerd, en niets hier kan een browser verbinden met de server in je werkruimte; je hoeft ze alleen te lezen. Elke regel heeft een tegenhanger in de Python-client die je in hoofdstuk 6 schreef:
new WebSocket(url)speelt de rol vanconnect(url): het opent de, stuurt het upgradeverzoek en controleert de 101en de acceptwaarde ervan. Het wacht daar alleen niet op. Het keert meteen terug, en de browser roept deonopenvan de pagina aan als deklaar is. Daarom stuurt de scorebijhouderspagina vanuit onopen.socket.onmessage = ...geeft de browser een functie. Waar je Python-clientrecvin eenaanriep, vraagt de pagina nooit om een bericht: de browser roept deze functie één keer aan voor elk bericht dat binnenkomt, met de tekst van het bericht in event.data.JSON.parseenJSON.stringifyzijnjson.loadsenjson.dumps. De enveloppen zijn dezelfde die hoofdstuk 7 definieerde: er komt eensnapshotbinnen, er gaat eenscoreuit.sendissend.
showScores is de eigen tekenfunctie van de pagina, geen onderdeel van de WebSocket-API.
Waarmee komt onmessage in het fragment overeen in je Python-client?
Wat de browser voor je beslist
Je Python-client kon elke header die hij wilde aan het upgradeverzoek toevoegen. Een webpagina kan dat niet. De constructor WebSocket van de browser neemt een Host, Upgrade, Connection, Sec-WebSocket-Key, Sec-WebSocket-Version, alle Origin die de pagina noemt die de verbinding opende, en daarover gaat les 4. Het script van de pagina kan op geen enkele manier een header aan dat verzoek toevoegen.
Dus Authorization: Bearer <token>, de header die je in
Waarom kan de scorebijhouderspagina in een browser geen Authorization: Bearer ... meesturen met haar WebSocket-handshake?
Waar het token dan heen gaat
De pagina kiest wel de URL, dus een token kan in de querystring meereizen:
ws://127.0.0.1:8765/score?token=<the token>
De bibliotheek geeft de server het hele request-target, query inbegrepen: connection.request.path is voor die handshake /score?token=<the token>, en daarom knipt handle_connection het al met urlsplit voordat hij paden vergelijkt. De controle van de volgende les accepteert het token van beide plekken: een Bearer-header, van een programma dat er een kan sturen, of een token-
De query heeft een prijs. URL’s worden opgeschreven: in het toegangslog van de server (en in het eigen debuglog van een bibliotheek, als dat aanstaat), in de logs van elke proxy onderweg, in de geschiedenis van de browser, in de ontwikkelaarstools. Een token in een URL kan later worden gelezen door iedereen die die plekken kan lezen. Een token dat in de query meereist, moet daarom kortlevend zijn: uitgegeven voor één sessie en kort daarna verlopen, zodat een kopie die in een log wordt gevonden al nutteloos is.
De andere route is een cookie. De browser stuurt de cookies die hij voor de site van de server heeft met de handshake mee, zoals bij elk verzoek, dus het script van de pagina raakt het token helemaal niet aan. Het addertje is dat de browser het cookie uit zichzelf kan meesturen, ook als de pagina die de verbinding opende van iemand anders is. Les 4 kijkt naar dat probleem.
Wat is de belangrijkste prijs van het token in de querystring zetten?
Optioneel: bekijk een browserclient
Deze stap wordt niet gecontroleerd. Als je een echte browserclient aan het werk wilt zien, open dan de ontwikkelaarstools van je browser op een pagina van deze site die de 101 die het beantwoordde, en de
De volgende les schrijft de controle zelf: een functie die het token vindt in een Bearer-header of in de query, het vergelijkt met het verwachte token, en 401 antwoordt voordat er een 101 wordt verstuurd.