Files
Scoreboard/server/internal/realtime/origin_test.go
T
AskaEth 2c9ff65fc4 feat: 补齐第一批已知缺口(关闭握手 / 定时备份 / Origin 白名单 / DTO 收敛)
- httpapi:新增 POST /api/shutdown(admin 会话或 X-Shutdown-Token 常量时间比对)
  与 GET /api/rules/switches(recorder 可读,只回布尔开关);settings/standings 的
  重复 DTO 收敛到 internal/dto;standings.showIndividualMedals 贯通到设置读写与前端
- export:抽出 export.Service,新增自动备份定时任务(backup.intervalMinutes/keep),
  自动、手动、发布前三类备份共用同一把互斥锁与滚动清理
- realtime:/ws 去掉 InsecureSkipVerify,改用 OriginPatterns(同源与无 Origin 放行,
  跨站需命中 server.allowedOrigins)
- console:停止服务改走 POST /api/shutdown 优雅关闭(5 秒宽限后回退强杀),
  服务管理器新增「已连接的大屏实例」卡片
- deploy:build.bat 改用 for /f 读内部署名——set /p 在 chcp 65001 下返回空,
  导致最近这次构建的 exe 里根本没注入署名
- docs:备份策略、关闭超时、Origin 配置的说明与实现对齐
2026-09-09 21:41:08 +08:00

67 lines
1.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package realtime
import (
"context"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/coder/websocket"
)
// dialOrigin 以指定 Origin 发起握手;origin 为空表示不带 Origin 头(非浏览器客户端)。
func dialOrigin(t *testing.T, srv *httptest.Server, origin string) error {
t.Helper()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
opts := &websocket.DialOptions{}
if origin != "" {
opts.HTTPHeader = http.Header{"Origin": []string{origin}}
}
conn, _, err := websocket.Dial(ctx, "ws"+strings.TrimPrefix(srv.URL, "http")+"/ws", opts)
if err != nil {
return err
}
_ = conn.CloseNow()
return nil
}
func TestHandlerOrigin(t *testing.T) {
t.Run("同源放行", func(t *testing.T) {
e := newTestEnv(t)
// httptest.Server 的 URL 即页面 Origin:scheme + host 与请求 Host 同源。
if err := dialOrigin(t, e.srv, e.srv.URL); err != nil {
t.Fatalf("同源 Origin 应放行: %v", err)
}
})
t.Run("空 Origin 放行", func(t *testing.T) {
e := newTestEnv(t)
if err := dialOrigin(t, e.srv, ""); err != nil {
t.Fatalf("不带 Origin 的客户端应放行: %v", err)
}
})
t.Run("跨站拒绝", func(t *testing.T) {
e := newTestEnv(t)
if err := dialOrigin(t, e.srv, "http://evil.example"); err == nil {
t.Fatal("跨站 Origin 应拒绝")
}
})
t.Run("白名单放行", func(t *testing.T) {
e := newTestEnv(t)
e.hub.SetAllowedOrigins([]string{"http://evil.example"})
if err := dialOrigin(t, e.srv, "http://evil.example"); err != nil {
t.Fatalf("白名单内 Origin 应放行: %v", err)
}
if err := dialOrigin(t, e.srv, "http://other.example"); err == nil {
t.Fatal("白名单外的跨站 Origin 应拒绝")
}
})
}