Skip to content

Commit c3aef60

Browse files
test(wsrelay): lock websocket reuse rules
1 parent 1e5ec10 commit c3aef60

2 files changed

Lines changed: 59 additions & 1 deletion

File tree

proxy/wsrelay/manager.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ func (m *Manager) AcquireConnection(
284284

285285
if v, ok := m.connections.Load(key); ok {
286286
wc := v.(*WsConnection)
287-
if wc.IsConnected() && !wc.IsExpired() && wc.session != nil && wc.session.PendingCount() == 0 {
287+
if canReuseConnection(wc) {
288288
wc.Touch()
289289
return wc, nil
290290
}
@@ -309,6 +309,19 @@ func (m *Manager) AcquireConnection(
309309
return wc, nil
310310
}
311311

312+
func canReuseConnection(wc *WsConnection) bool {
313+
if wc == nil {
314+
return false
315+
}
316+
if !wc.IsConnected() || wc.IsExpired() {
317+
return false
318+
}
319+
if wc.session == nil {
320+
return false
321+
}
322+
return wc.session.PendingCount() == 0
323+
}
324+
312325
// createConnection 创建新 WebSocket 连接
313326
func (m *Manager) createConnection(
314327
ctx context.Context,

proxy/wsrelay/manager_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"net/http"
66
"testing"
7+
"time"
78

89
"github.com/codex2api/auth"
910
)
@@ -59,3 +60,47 @@ func TestPoolKeyKeepsSameSessionStable(t *testing.T) {
5960
t.Fatal("expected identical session keys to produce the same pool key")
6061
}
6162
}
63+
64+
func TestCanReuseConnection(t *testing.T) {
65+
manager := NewManager()
66+
t.Cleanup(manager.Stop)
67+
68+
t.Run("idle connected session can be reused", func(t *testing.T) {
69+
session := NewSession(42, manager)
70+
session.SetConnected(true)
71+
conn := &WsConnection{session: session}
72+
conn.SetState(StateConnected)
73+
conn.Touch()
74+
75+
if !canReuseConnection(conn) {
76+
t.Fatal("expected connection to be reusable")
77+
}
78+
})
79+
80+
t.Run("pending request blocks reuse", func(t *testing.T) {
81+
session := NewSession(42, manager)
82+
session.SetConnected(true)
83+
pending := session.AddPendingRequest("session-a")
84+
t.Cleanup(func() { session.RemovePendingRequest(pending.RequestID) })
85+
86+
conn := &WsConnection{session: session}
87+
conn.SetState(StateConnected)
88+
conn.Touch()
89+
90+
if canReuseConnection(conn) {
91+
t.Fatal("expected connection with pending request to be non-reusable")
92+
}
93+
})
94+
95+
t.Run("expired connection cannot be reused", func(t *testing.T) {
96+
session := NewSession(42, manager)
97+
session.SetConnected(true)
98+
conn := &WsConnection{session: session}
99+
conn.SetState(StateConnected)
100+
conn.lastUsed.Store(time.Now().Add(-IdleTimeout - time.Second).UnixNano())
101+
102+
if canReuseConnection(conn) {
103+
t.Fatal("expected expired connection to be non-reusable")
104+
}
105+
})
106+
}

0 commit comments

Comments
 (0)