diff --git a/common/net/bufconn.go b/common/net/bufconn.go index 37c8ba25..b7e98e04 100644 --- a/common/net/bufconn.go +++ b/common/net/bufconn.go @@ -84,9 +84,9 @@ func (c *BufferedConn) ReadCached() *buf.Buffer { // call in sing/common/bufio.C length := c.r.Buffered() b, _ := c.r.Peek(length) _, _ = c.r.Discard(length) - c.r = nil // drop bufio.Reader to let gc can clean up its internal buf return buf.As(b) } + c.r = nil // drop bufio.Reader to let gc can clean up its internal buf return nil } diff --git a/common/net/bufconn_unsafe.go b/common/net/bufconn_unsafe.go new file mode 100644 index 00000000..349321df --- /dev/null +++ b/common/net/bufconn_unsafe.go @@ -0,0 +1,34 @@ +package net + +import ( + "io" + "unsafe" +) + +// bufioReader copy from stdlib bufio/bufio.go +// This structure has remained unchanged from go1.5 to go1.21. +type bufioReader struct { + buf []byte + rd io.Reader // reader provided by the client + r, w int // buf read and write positions + err error + lastByte int // last byte read for UnreadByte; -1 means invalid + lastRuneSize int // size of last rune read for UnreadRune; -1 means invalid +} + +func (c *BufferedConn) AppendData(buf []byte) (ok bool) { + b := (*bufioReader)(unsafe.Pointer(c.r)) + pos := len(b.buf) - b.w - len(buf) + if pos >= -b.r { // len(b.buf)-(b.w - b.r) >= len(buf) + if pos < 0 { // len(b.buf)-b.w < len(buf) + // Slide existing data to beginning. + copy(b.buf, b.buf[b.r:b.w]) + b.w -= b.r + b.r = 0 + } + + b.w += copy(b.buf[b.w:], buf) + return true + } + return false +} diff --git a/transport/vmess/websocket.go b/transport/vmess/websocket.go index b30bb8aa..8e675bb0 100644 --- a/transport/vmess/websocket.go +++ b/transport/vmess/websocket.go @@ -554,7 +554,14 @@ func StreamUpgradedWebsocketConn(w http.ResponseWriter, r *http.Request) (net.Co } if edBuf := decodeXray0rtt(r.Header); len(edBuf) > 0 { - conn = N.NewCachedConn(conn, edBuf) + appendOk := false + if bufConn, ok := conn.(*N.BufferedConn); ok { + appendOk = bufConn.AppendData(edBuf) + } + if !appendOk { + conn = N.NewCachedConn(conn, edBuf) + } + } return conn, nil