service/websocket.go
AI Engineer e8369d4680 feat(service): Client Key 应答头条件化,静态文件/WebSocket 仅 Cookie 维护,配置字段命名统一(by AI)
- Device-Id/Session-Id 仅当请求头未携带时才写入应答头
- 静态文件和 WebSocket 升级应答仅通过 Cookie 维护身份
- Client App 头改为 App-Name/App-Version(破折号命名)
- NoLogHeaders → NoLogRequestHeaders,NoLogOutputFields → NoLogResponseFields,新增 NoLogResponseHeaders
- 默认排除列表动态构建,用户只需追加自定义字段
- Cookie 头智能过滤:不再整体排除,仅剔除匹配排除列表的 key

Co-Authored-By: deepseek-v4-pro[1m] <deepseek-ai@claude-code-best.win>
2026-06-22 19:01:53 +08:00

115 lines
3.0 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 service
import (
"apigo.cc/go/jsmod"
"apigo.cc/go/log"
"github.com/gorilla/websocket"
"net/http"
"reflect"
)
var defaultUpgrader = &websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool { return true },
}
// WebSocketConn 包装标准的 websocket.Conn提供更友好的接口
type WebSocketConn struct {
Conn *websocket.Conn
}
// Send 发送消息,支持 string, []byte 或 自动转 JSON
func (c *WebSocketConn) Send(data any) error {
var err error
switch v := data.(type) {
case string:
err = c.Conn.WriteMessage(websocket.TextMessage, []byte(v))
case []byte:
err = c.Conn.WriteMessage(websocket.BinaryMessage, v)
default:
err = c.Conn.WriteJSON(v)
}
if err != nil {
return jsmod.MakeError(err)
}
return nil
}
// ReadString 读取字符串消息
func (c *WebSocketConn) ReadString() (string, error) {
_, b, err := c.Conn.ReadMessage()
if err != nil {
return "", jsmod.MakeError(err)
}
return string(b), nil
}
// ReadBytes 读取二进制消息
func (c *WebSocketConn) ReadBytes() ([]byte, error) {
_, b, err := c.Conn.ReadMessage()
if err != nil {
return nil, jsmod.MakeError(err)
}
return b, nil
}
// ReadJSON 读取 JSON 消息
func (c *WebSocketConn) ReadJSON(v any) error {
if err := c.Conn.ReadJSON(v); err != nil {
return jsmod.MakeError(err)
}
return nil
}
// Close 关闭连接
func (c *WebSocketConn) Close() error {
if err := c.Conn.Close(); err != nil {
return jsmod.MakeError(err)
}
return nil
}
// Upgrade 将 HTTP 请求升级为 WebSocket 连接
func Upgrade(response *Response, request *Request) (*WebSocketConn, error) {
conn, err := defaultUpgrader.Upgrade(response.Writer, request.Request, nil)
if err != nil {
return nil, jsmod.MakeError(err)
}
return &WebSocketConn{Conn: conn}, nil
}
func (ws *WebServer) doWebsocketService(wsc *websocketServiceType, request *Request, response *Response, logger *log.Logger, object any) {
// WebSocket 浏览器 API 不支持自定义请求头,只能通过 Cookie 传递身份标识,
// 因此不应在升级应答中返回 Device-Id / Session-Id 头。
response.Header().Del(ws.usedDeviceIdKey)
response.Header().Del(ws.usedSessionIdKey)
wsConn, err := Upgrade(response, request)
if err != nil {
logger.Error("websocket upgrade failed", "error", err.Error())
return
}
defer wsConn.Close()
// 调用业务处理函数,注入依赖
params := make([]reflect.Value, wsc.funcType.NumIn())
for i := 0; i < len(params); i++ {
t := wsc.funcType.In(i)
if t == reflect.TypeOf(request) {
params[i] = reflect.ValueOf(request)
} else if t == reflect.TypeOf(logger) {
params[i] = reflect.ValueOf(logger)
} else if t == reflect.TypeOf(wsConn) {
params[i] = reflect.ValueOf(wsConn)
} else if t == reflect.TypeOf(wsConn.Conn) {
params[i] = reflect.ValueOf(wsConn.Conn)
} else if object != nil && reflect.TypeOf(object).AssignableTo(t) {
params[i] = reflect.ValueOf(object)
} else if obj := ws.GetInject(t); obj != nil {
params[i] = reflect.ValueOf(obj)
} else {
params[i] = reflect.New(t).Elem()
}
}
wsc.funcValue.Call(params)
}