0%

Hoofdstuk 2

Vragen om van protocol te wisselen

Een upgradeverzoek lezen

Een begint haar leven als een -verzoek. De client opent een , stuurt een GET met een paar extra headers, en vraagt de server om op deze verbinding te stoppen met HTTP en over te schakelen naar het WebSocket-protocol. Als de server instemt, zegt hij dat in één antwoord, en vanaf dan draagt dezelfde verbinding WebSocket-berichten in beide richtingen. Die uitwisseling heet de , en in dit hoofdstuk schrijf je beide kanten ervan.

Hier is het verzoek dat een client stuurt. Het is het voorbeeld uit RFC 6455, het document dat het WebSocket-protocol definieert, in sectie 1.3:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

Zoals in elk HTTP-verzoek dat je in HTTP with Python schreef, eindigt elke regel op \r\n en beëindigt een lege regel de kop. Met die regeleinden en de lege regel is dit verzoek 230 bytes. Het heeft geen body.

Regel voor regel

De eerste regel is een verzoekregel zoals elke andere: de GET, het doel /chat en de versie HTTP/1.1. RFC 6455 eist de methode GET en HTTP-versie 1.1 of hoger, en in de praktijk is de versie altijd HTTP/1.1.

Host: server.example.com noemt de server waarvoor het verzoek bedoeld is. HTTP/1.1 eist hem in elk verzoek, om de reden die je in HTTP with Python tegenkwam: één adres kan veel sites bedienen, en de server moet weten welke de client bedoelde.

Upgrade: websocket noemt het protocol waarnaar de client wil overschakelen. Upgrade is een gewone voor elke protocolwissel; het token websocket zegt welke.

Connection: Upgrade vertelt de server dat deze verbinding zelf op de Upgrade-header moet reageren. De Connection-header bevat een door komma’s gescheiden tokens, en een client mag er naast Upgrade nog andere in zetten. Veel clients sturen Connection: keep-alive, Upgrade, en dat vraagt net zo goed om de upgrade, in welke volgorde ook. Waar het om gaat, is dat Upgrade een van de tokens is.

Sec-WebSocket-Key bevat 16 willekeurige bytes, geschreven in , de codering die je in HTTP with Python gebruikte om bytes als headertekst te vervoeren. De client kiest voor elk verzoek een nieuwe sleutel; RFC 6455 eist dat. Het antwoord van de server moet een bevatten die uit deze sleutel is berekend, en les 3 berekent die.

Sec-WebSocket-Version: 13 is de versie van het WebSocket-protocol die de client spreekt. Versie 13 is degene die RFC 6455 definieert; drafts die daarvóór geschreven zijn, gebruikten andere nummers.

Headernamen worden vergeleken zonder op hoofdletters te letten, zoals in elk HTTP-bericht, dus upgrade: WebSocket betekent hetzelfde als Upgrade: websocket. De twee tokens worden op dezelfde manier vergeleken: websocket in Upgrade en Upgrade in Connection mogen in elke mix van hoofdletters aankomen.

De sleutel lijkt een geheim, maar dat is hij niet. Decodeer hem en kijk wat de auteurs van de RFC erin hebben gestopt:

Try it

Het drukt b'the sample nonce' en 16 af. Een nonce is een waarde die één keer wordt gebruikt. Wat zijn 16 bytes ook zijn, de sleutel reist in het volle zicht. Zijn taak is elk verzoek anders te maken, zodat het antwoord dat eruit wordt berekend bij dit verzoek hoort en bij geen ander.

Dat antwoord maakt de handshake betrouwbaar. Een server die WebSocket spreekt, berekent een waarde uit de sleutel en stuurt die terug. Een server of een cache ertussen die WebSockets niet begrijpt, kan die waarde niet maken, dus een client die de juiste ontvangt, weet dat zijn verzoek als WebSocket-verzoek is begrepen en niet is aangezien voor een gewone GET. De sleutel versleutelt niets, dat is de taak van , en hij zegt niets over wie de gebruiker is.

Twee headers die deze cursus weglaat

Twee regels van het voorbeeld zijn optioneel, en de server van deze cursus heeft ze niet nodig.

Origin: http://example.com noemt de webpagina waarvan het script de verbinding opende. Een browser stuurt hem altijd; een Python-client, zoals die je in les 5 schrijft, mag hem weglaten. Een server kan hem gebruiken om verbindingen te weigeren van pagina’s die hij niet vertrouwt, en hoofdstuk 9 laat zien hoe. Tot dan negeert je server hem.

Sec-WebSocket-Protocol: chat, superchat biedt een lijst subprotocollen aan, talen op applicatieniveau die de client over de WebSocket zou kunnen spreken, en laat de server er een kiezen. In het voorbeeld van de RFC noemt het antwoord van de server chat. Deze cursus definieert zijn eigen berichten en onderhandelt over geen enkel subprotocol, dus je server stuurt die header nooit, en je client vraagt er nooit om.

Wat er na het verzoek gebeurt

Als het verzoek aanvaardbaar is, antwoordt de server met de status 101 Switching Protocols, een paar headers en een lege regel, en verder niets. Na die lege regel is HTTP op deze verbinding voorbij. Dezelfde TCP-verbinding blijft open, en beide kanten sturen nu WebSocket-, in beide richtingen, wanneer ze iets te zeggen hebben. Volg de twee lijnen omlaag in de afbeelding: ze breken nooit, omdat de verbinding die de HTTP-uitwisseling droeg dezelfde is die de frames draagt.

Eén HTTP-verzoek en één 101-antwoord schakelen de verbinding om, en na de lege regel draagt dezelfde verbinding frames in beide richtingen.

In woorden: de client stuurt het upgradeverzoek, GET /chat HTTP/1.1 met de headers Upgrade, Connection, de sleutel en de versie; de server antwoordt met HTTP/1.1 101 Switching Protocols met de waarde die uit de sleutel is berekend, en vanaf dan stromen frames in beide richtingen over dezelfde verbinding, zonder koppeling van verzoek en antwoord. Als het verzoek niet aanvaardbaar is, antwoordt de server in plaats daarvan met een gewone HTTP-fout, en wordt er niet omgeschakeld. Les 4 beslist welke van de twee.

Welke van deze headers in het voorbeeld zou een client kunnen weglaten en toch een 101 krijgen van de server van deze cursus?

Een verzoek bevat Connection: keep-alive, Upgrade. Vraagt het om de upgrade?

Waar dient Sec-WebSocket-Key voor?

Je kunt nu elke regel van een upgradeverzoek lezen en zeggen waarom hij er staat. In de volgende les open je het project van het hoofdstuk en verander je deze bytes in een die je server kan controleren.