// Package wsconn 把一個 gorilla/websocket.Conn 包裝成 net.Conn, // 讓 hashicorp/yamux 這類 stream multiplexer 可以直接跑在 WebSocket 之上。 // // 從 POC `edge-ai-platform/server/pkg/wsconn` 直接複製,未做邏輯變動; // 對齊 tunnel.md §4.1 與 ADR-002「沿用 POC 的 tunnel 協定」。 // // 協定重點: // - 所有 frame 皆為 WebSocket Binary frame;無壓縮(yamux 已是位元流)。 // - 讀取時以 NextReader() 取得當前 frame 的 reader,剩餘 bytes 跨次呼叫延續。 // - 寫入時整個 []byte 打包為一個 Binary message(yamux 自己分 frame)。 package wsconn import ( "io" "net" "sync" "time" "github.com/gorilla/websocket" ) // Conn 是 *websocket.Conn 到 net.Conn 的 adapter。 // // rmu / wmu 分開,允許同時讀與寫(符合 net.Conn 典型期望)。 // 但同一方向不允許並發呼叫(yamux 已保證此約束)。 type Conn struct { ws *websocket.Conn reader io.Reader // 當前 WebSocket frame 的 reader(跨次呼叫延續) rmu sync.Mutex wmu sync.Mutex } // New 將 *websocket.Conn 包裝成符合 net.Conn 介面的 *Conn。 func New(ws *websocket.Conn) *Conn { return &Conn{ws: ws} } // Read 實作 io.Reader。 // // 若當前 frame reader 已讀盡會自動切到下一個 frame;EOF 自動透明處理。 func (c *Conn) Read(p []byte) (int, error) { c.rmu.Lock() defer c.rmu.Unlock() for { if c.reader != nil { n, err := c.reader.Read(p) if err == io.EOF { c.reader = nil if n > 0 { return n, nil } continue } return n, err } _, reader, err := c.ws.NextReader() if err != nil { return 0, err } c.reader = reader } } // Write 實作 io.Writer;整個 p 以單一 BinaryMessage 寫出。 func (c *Conn) Write(p []byte) (int, error) { c.wmu.Lock() defer c.wmu.Unlock() if err := c.ws.WriteMessage(websocket.BinaryMessage, p); err != nil { return 0, err } return len(p), nil } // Close 關閉底層 WebSocket 連線。 func (c *Conn) Close() error { return c.ws.Close() } // LocalAddr 回傳本地 WebSocket endpoint 的位址。 func (c *Conn) LocalAddr() net.Addr { return c.ws.LocalAddr() } // RemoteAddr 回傳對端 WebSocket endpoint 的位址。 func (c *Conn) RemoteAddr() net.Addr { return c.ws.RemoteAddr() } // SetDeadline 同時設定讀寫 deadline。 func (c *Conn) SetDeadline(t time.Time) error { if err := c.ws.SetReadDeadline(t); err != nil { return err } return c.ws.SetWriteDeadline(t) } // SetReadDeadline 設定讀取 deadline。 func (c *Conn) SetReadDeadline(t time.Time) error { return c.ws.SetReadDeadline(t) } // SetWriteDeadline 設定寫入 deadline。 func (c *Conn) SetWriteDeadline(t time.Time) error { return c.ws.SetWriteDeadline(t) } // 編譯時檢查:Conn 必須實作 net.Conn。 var _ net.Conn = (*Conn)(nil)