From 6fc0a40c2ac57c8acb7853a91979667ece37a1cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E6=89=87=E6=BB=91=E7=BF=94=E7=BF=BC?= Date: Sat, 16 Aug 2025 02:01:15 +0800 Subject: [PATCH] XHTTP client: Fix edge-case issue for `packet-up` mode (#5020) https://github.com/XTLS/Xray-core/pull/4952#issuecomment-3184080580 --- common/buf/buffer.go | 7 ++++++- transport/internet/splithttp/dialer.go | 19 +++++++++-------- .../internet/splithttp/splithttp_test.go | 21 ++++++++----------- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/common/buf/buffer.go b/common/buf/buffer.go index 04c886a7..5d3680a2 100644 --- a/common/buf/buffer.go +++ b/common/buf/buffer.go @@ -13,6 +13,8 @@ const ( Size = 8192 ) +var ErrBufferFull = errors.New("buffer is full") + var zero = [Size * 10]byte{0} var pool = bytespool.GetPool(Size) @@ -266,13 +268,16 @@ func (b *Buffer) IsFull() bool { func (b *Buffer) Write(data []byte) (int, error) { nBytes := copy(b.v[b.end:], data) b.end += int32(nBytes) + if nBytes < len(data) { + return nBytes, ErrBufferFull + } return nBytes, nil } // WriteByte writes a single byte into the buffer. func (b *Buffer) WriteByte(v byte) error { if b.IsFull() { - return errors.New("buffer full") + return ErrBufferFull } b.v[b.end] = v b.end++ diff --git a/transport/internet/splithttp/dialer.go b/transport/internet/splithttp/dialer.go index e409e61c..3f6e9ea3 100644 --- a/transport/internet/splithttp/dialer.go +++ b/transport/internet/splithttp/dialer.go @@ -489,15 +489,16 @@ func (w uploadWriter) Write(b []byte) (int, error) { } */ - buffer := buf.New() - n, err := buffer.Write(b) - if err != nil { - return 0, err - } + buffer := buf.MultiBufferContainer{} + common.Must2(buffer.Write(b)) - err = w.WriteMultiBuffer([]*buf.Buffer{buffer}) - if err != nil { - return 0, err + var writed int + for _, buff := range buffer.MultiBuffer { + err := w.WriteMultiBuffer(buf.MultiBuffer{buff}) + if err != nil { + return writed, err + } + writed += int(buff.Len()) } - return n, nil + return writed, nil } diff --git a/transport/internet/splithttp/splithttp_test.go b/transport/internet/splithttp/splithttp_test.go index 1db54255..c1bb8580 100644 --- a/transport/internet/splithttp/splithttp_test.go +++ b/transport/internet/splithttp/splithttp_test.go @@ -1,6 +1,7 @@ package splithttp_test import ( + "bytes" "context" "crypto/rand" "fmt" @@ -421,18 +422,12 @@ func Test_maxUpload(t *testing.T) { }, } - var uploadSize int + uploadReceived := make([]byte, 10001) listen, err := ListenXH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) { go func(c stat.Connection) { defer c.Close() - var b [10240]byte c.SetReadDeadline(time.Now().Add(2 * time.Second)) - n, err := c.Read(b[:]) - if err != nil { - return - } - - uploadSize = n + io.ReadFull(c, uploadReceived) common.Must2(c.Write([]byte("Response"))) }(conn) @@ -441,10 +436,12 @@ func Test_maxUpload(t *testing.T) { ctx := context.Background() conn, err := Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings) + common.Must(err) // send a slightly too large upload - var upload [10001]byte - _, err = conn.Write(upload[:]) + upload := make([]byte, 10001) + rand.Read(upload) + _, err = conn.Write(upload) common.Must(err) var b [10240]byte @@ -455,8 +452,8 @@ func Test_maxUpload(t *testing.T) { } common.Must(conn.Close()) - if uploadSize > 10000 || uploadSize == 0 { - t.Error("incorrect upload size: ", uploadSize) + if !bytes.Equal(upload, uploadReceived) { + t.Error("incorrect upload", upload, uploadReceived) } common.Must(listen.Close())