Files

67 lines
3.2 KiB
Go
Raw Permalink Normal View History

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,
})
}