Receiving socket messages on the backend

HI,

because it doesn't look like it is possible for plugins to receive messages from the client send through the socket connection I would like to create a pull request for it. But want to ask @foosel if you're okey with it and what branch I should target, devel or maintenence.

I think the changes wouldn't be that extensive. Some thoughts how I would implement it:

  • Adding a new event to the EventManager, e.g. CLIENT_MESSAGE
  • Extending the on_message() method of the PrinterStateConnection to fire an event if 'plugin' in message
  • Adding a sendPluginMessage(plugin, data) method to socket.js

I think that would be it and it shouldn't affect any existing functionality. What do you think?

Hm.. Could you explain in what situation you'd need something like this?

The thing is, the direction from the client to the server should already be covered by the REST API. The only reason there is a socket in place is to allow real time push events from the server to the client, which otherwise could only be done through polling (which puts unnecessary strain on everyone involved).

Of course. I want to ensure data integrity between clients. At the moment the backend answers the REST call (tagged with a request id) and also sends the same response via the web socket to all clients. To prevent reacting to the same response twice (REST and socket message) the client saves the request id and compares them with the id from the socket message.

Well of course this works, bit it is kind of cumbersome. Therefore my thought was to use the websocket also for making requests.

Ah. In the past I've solved situations like this with just the response in the REST call, but before that was returned I sent an event to all connected clients (or a plugin message). The client with the active REST call then knew it was active and would ignore the event, the other clients would see the event and read that as a request to update themselves via the REST API (but of course you can also just include the data in the push message).

The problem with the socket is that at least in the current stable/maintenance releases it doesn't have any kind of access control, meaning that if that was opened up to allow plugins to inject events, it would punch a hole into things. That could get mitigated somewhat by forcing plugins to subscribe to some plugin manager registration to receive such push messages, but further control would then still fall to the individual plugins. What I really don't want is any kind of write access for anonymous clients via the socket that affects anything but the socket connection itself (e.g. the throttle messages that are already supported). So we'd need a solution for that.

On the devel branch the socket client will now have to authenticate itself with the server via the socket as well, using a session token retrieved during the login API call. So there the server does now the user level and can restrict certain messages. But we don't have that mechanism on stable/maintenance yet and I'm not sure if backporting it could be done easily.

I will take a look at the devel branch on how the authentication works, but it sounds like this change will be more suited for version 1.4 of OctoPrint.

Are you generally interested in such a pull request?

From a resource utilization standpoint, it's a sensible change, since it eliminates the overhead of a new connection for the API call. Not a big deal for clients, but a lot of OctoPrint servers run on resource constrained hardware. I'm always an advocate for anything that is more efficient. It could also reduce some AAA overhead as well, depending on how the socket's AAA state is stored, at least for low-risk operations that you're not concerned about rechecking permissions for if the permissions aren't stale.

For example, pausing a print might be something that doesn't cause a user permissions lookup if one has been done within some timeout period (say 5 minutes), but deleting a file or starting/stopping a print might always cause an authorization check (thus also refreshing the AAA timestamp). It might even be something where there's a table in the database that has specific actions and sets AAA parameters around them, allowing them to be read once at startup and stored globally for the instance, and only refreshed if they are changed (if they're configurable via UI, then automagically, or if not, then by a signal or restart on the rare non-POSIX OS).

Generally yes. Maybe with some kind of new mixin that would then be called for messages fitting plugin_<identifier>_<message> (e.g. plugin_someplugin_my_shiny_message, prefix to avoid naming collisions).