qri-io / qri

you're invited to a data party!
https://qri.io
GNU General Public License v3.0
1.1k stars 66 forks source link

feat(lib): scope websocket connections #1912

Closed ramfox closed 2 years ago

ramfox commented 2 years ago

Not every websocket connection should receive all events.

Now that events can be scoped, we should only send certain events on certain websocket connections.

To do that, we must associate each connection with a scope (profile id) and only send events with that profile id over the connection.


in lib/websocket.go The wsHandler.conns should be map[Profile.ID]*websocket.Conn

wsHandler.WSConnectionHandler should parse the token in the request. If it's valid, it should continue with accepting a websocket connection. The connection should be saved to wsHandler.conns indexed by the profile id.

wsHandler.wsMessageHandler - if the event does not have a scope, it should be sent over every connection (???). If the event does have a scope, it should be sent over the associated connection.


The frontend would need to delay it's websocket connection "upgrade" until the user has logged in. Non-logged in users should not be able to receive any messages over the websocket. (this follows nicely with out current set up, we require a user to create an account or sign in before running anything).

potential issues to research: 1) anything coming through dispatch is properly scoped. We also made a point to scope automated runs. Are there potentially other parts of the codebase that aren't scoped? 2) Is there are, do they need to be scoped? If something is a system wide message, for example, should we also be passing events that have no scope over all connections? 3) should the TTL of the token be the TTL of the connection? In which case, we need to keep track of TTLs and close any outstanding connections. How would a token-refresh play into any of this. Is there a way to notify the websocket handler of any refreshes, so we can extend the TTL without dropping connections? If there is a dropped connection, it should be the responsibility of the frontend to request a new connection.

ramfox commented 2 years ago

plans/diagrams of how to implement scoped websockets across backend, frontend, and cloud: https://www.figma.com/file/Isrq9Oi4dmMeNsHlzCPCDL/Websocket-plans?node-id=0%3A1

ramfox commented 2 years ago

New plan. Major overview points:

1) store connections based on a connection id. Store "subscriptions" by associating profile.IDs to connection IDs. 2) connections can exist without authentication 3) listen for "subscribe" and "unsubscribe" events off the connection 4) subscribe events "authenticate" the connection, associating it with a profile.ID (later Key), by sending over a struct containing a token (and in the future other potential configuration options). The token gets validated & the connection is then associated with the 5) unsubscribe events disassociate the connection with the profile.ID