mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-14 13:12:23 +00:00
fix(multiplayer): reject websocket caps before upgrade
This commit is contained in:
+3
-1
@@ -1461,7 +1461,9 @@ Observability redaction now adds content-aware protection on top of denylisted f
|
|||||||
The authenticated control-plane WebSocket now caps each player at two
|
The authenticated control-plane WebSocket now caps each player at two
|
||||||
simultaneous connections, releasing capacity on disconnect; this complements
|
simultaneous connections, releasing capacity on disconnect; this complements
|
||||||
the bounded per-player event queue and prevents connection fan-out from
|
the bounded per-player event queue and prevents connection fan-out from
|
||||||
becoming an unbounded account-level resource cost.
|
becoming an unbounded account-level resource cost. Over-limit attempts fail
|
||||||
|
before upgrade with `429 websocket_connection_limited`, rather than becoming
|
||||||
|
ambiguous post-upgrade disconnects.
|
||||||
|
|
||||||
Proposal explicit-decline cooldowns are now durable: the declining player is
|
Proposal explicit-decline cooldowns are now durable: the declining player is
|
||||||
requeued for recovery, but a subsequent queue create is rejected until the
|
requeued for recovery, but a subsequent queue create is rejected until the
|
||||||
|
|||||||
@@ -161,6 +161,13 @@ func (s *Service) controlPlaneEvent(w http.ResponseWriter, r *http.Request) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
hub := s.getEventHub()
|
||||||
|
subscriber := hub.subscribe(playerID)
|
||||||
|
if subscriber == nil {
|
||||||
|
writeError(w, http.StatusTooManyRequests, "websocket_connection_limited")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer hub.unsubscribe(subscriber)
|
||||||
hijacker, ok := w.(http.Hijacker)
|
hijacker, ok := w.(http.Hijacker)
|
||||||
if !ok {
|
if !ok {
|
||||||
writeError(w, http.StatusNotImplemented, "websocket_unavailable")
|
writeError(w, http.StatusNotImplemented, "websocket_unavailable")
|
||||||
@@ -178,12 +185,6 @@ func (s *Service) controlPlaneEvent(w http.ResponseWriter, r *http.Request) {
|
|||||||
if err := buffered.Flush(); err != nil {
|
if err := buffered.Flush(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
subscriber := s.getEventHub().subscribe(playerID)
|
|
||||||
if subscriber == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer s.getEventHub().unsubscribe(subscriber)
|
|
||||||
|
|
||||||
var writeMu sync.Mutex
|
var writeMu sync.Mutex
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
func TestEventHubCapsConnectionsPerPlayerAndReleasesCapacity(t *testing.T) {
|
func TestEventHubCapsConnectionsPerPlayerAndReleasesCapacity(t *testing.T) {
|
||||||
hub := newEventHub()
|
hub := newEventHub()
|
||||||
@@ -20,3 +24,23 @@ func TestEventHubCapsConnectionsPerPlayerAndReleasesCapacity(t *testing.T) {
|
|||||||
}
|
}
|
||||||
hub.unsubscribe(second)
|
hub.unsubscribe(second)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEventConnectionCapReturnsHTTP429BeforeUpgrade(t *testing.T) {
|
||||||
|
service := &Service{SessionBackend: &sessionBackendSpy{}}
|
||||||
|
hub := service.getEventHub()
|
||||||
|
first := hub.subscribe("player-1")
|
||||||
|
second := hub.subscribe("player-1")
|
||||||
|
defer hub.unsubscribe(first)
|
||||||
|
defer hub.unsubscribe(second)
|
||||||
|
request := httptest.NewRequest(http.MethodGet, "/v1/events", nil)
|
||||||
|
request.Header.Set("Upgrade", "websocket")
|
||||||
|
request.Header.Set("Connection", "Upgrade")
|
||||||
|
request.Header.Set("Sec-WebSocket-Version", "13")
|
||||||
|
request.Header.Set("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ==")
|
||||||
|
request.Header.Set("Authorization", "Bearer session-1:token-1")
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
service.controlPlaneEvent(recorder, request)
|
||||||
|
if recorder.Code != http.StatusTooManyRequests {
|
||||||
|
t.Fatalf("connection-cap status = %d, want 429", recorder.Code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user