67 lines
3.2 KiB
Go
67 lines
3.2 KiB
Go
package handlers
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"net/http"
|
||
|
|
|
||
|
|
evt "github.com/Silo-Server/silo-server/internal/events"
|
||
|
|
)
|
||
|
|
|
||
|
|
// eventsCapabilityResponse describes how a client may subscribe to the events
|
||
|
|
// websocket.
|
||
|
|
//
|
||
|
|
// Clients are expected to run a current build rather than negotiate down to an
|
||
|
|
// old server, so this is not a branch-on-capability contract. It is how a
|
||
|
|
// client tells the difference between "this server does not do that" and "the
|
||
|
|
// connection failed", which is what lets it say the deployment is out of date
|
||
|
|
// instead of failing opaquely: a server predating declared channels ignores
|
||
|
|
// ?channels=, answers required_action:"subscribe", and closes the connection
|
||
|
|
// after the grace period, which is indistinguishable from a broken socket.
|
||
|
|
type eventsCapabilityResponse struct {
|
||
|
|
SchemaVersion int `json:"schema_version"`
|
||
|
|
// SubscribeFrame reports the handshake: connect, then send a subscribe
|
||
|
|
// frame. Always true; named so a future removal is detectable rather than
|
||
|
|
// silent.
|
||
|
|
SubscribeFrame bool `json:"subscribe_frame"`
|
||
|
|
// DeclaredChannels reports that ?channels= is honored on connect, that such
|
||
|
|
// a connection is exempt from the subscribe grace period, and that its
|
||
|
|
// hello frame carries required_action:"none".
|
||
|
|
DeclaredChannels bool `json:"declared_channels"`
|
||
|
|
// SubscribeGracePeriodSeconds is how long a connection holding no
|
||
|
|
// subscription may stay silent before it is closed. 0 would mean no
|
||
|
|
// deadline.
|
||
|
|
SubscribeGracePeriodSeconds int `json:"subscribe_grace_period_seconds"`
|
||
|
|
// MaxRequestedChannels is the most channels one selection may name, on the
|
||
|
|
// URL or in a subscribe frame. Names past it are answered with a single
|
||
|
|
// too_many_channels rejection rather than one per name.
|
||
|
|
MaxRequestedChannels int `json:"max_requested_channels"`
|
||
|
|
// Channels is every channel a client may ask for on this server,
|
||
|
|
// independent of role — not every channel the server has: the plugins
|
||
|
|
// channel is host-to-plugin runtime dispatch and is granted to no role, so
|
||
|
|
// naming it here would advertise a request that can only be refused. What
|
||
|
|
// the caller may actually subscribe to arrives as available_channels in the
|
||
|
|
// hello frame, which is role-filtered.
|
||
|
|
Channels []evt.EventChannel `json:"channels"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// HandleCapability reports the events websocket's subscription capabilities.
|
||
|
|
//
|
||
|
|
// Per the v1 rules, new functionality is feature-detected rather than inferred
|
||
|
|
// from a version. This follows the existing per-subsystem convention
|
||
|
|
// (/notifications/capability, /playback/capability, /downloads/capability).
|
||
|
|
//
|
||
|
|
// A client that finds declared_channels false is talking to a server older than
|
||
|
|
// its own expectations; the useful response is to tell the user to update the
|
||
|
|
// server, not to silently fall back.
|
||
|
|
func (h *EventsHandler) HandleCapability(w http.ResponseWriter, r *http.Request) {
|
||
|
|
w.Header().Set("Content-Type", "application/json")
|
||
|
|
_ = json.NewEncoder(w).Encode(eventsCapabilityResponse{
|
||
|
|
SchemaVersion: 1,
|
||
|
|
SubscribeFrame: true,
|
||
|
|
DeclaredChannels: true,
|
||
|
|
SubscribeGracePeriodSeconds: int(subscribeGracePeriod.Seconds()),
|
||
|
|
MaxRequestedChannels: maxRequestedChannels,
|
||
|
|
Channels: evt.ClientChannels,
|
||
|
|
})
|
||
|
|
}
|