From 17d85263e2cc0d7068e1280efeb6ccae3a1bab27 Mon Sep 17 00:00:00 2001 From: lrh2000 Date: Mon, 3 Jan 2022 22:53:15 +0800 Subject: [PATCH] proxy/http: Avoid getting stuck when using server-first protocols PR #99 reduces an extra RTT when setting up the CONNECT tunnel via http proxy, however, server-first protocols (such as MySQL) will be unusable as v2ray keeps to wait for the client request. This commit solves the problem by limiting the maximum waiting time to 50 ms. --- proxy/http/client.go | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/proxy/http/client.go b/proxy/http/client.go index eba4ee7b1..0e2175a62 100644 --- a/proxy/http/client.go +++ b/proxy/http/client.go @@ -8,6 +8,7 @@ import ( "net/http" "net/url" "sync" + "time" "golang.org/x/net/http2" @@ -79,14 +80,23 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter var user *protocol.MemoryUser var conn internet.Connection - mbuf, _ := link.Reader.ReadMultiBuffer() - len := mbuf.Len() - firstPayload := bytespool.Alloc(len) - mbuf, _ = buf.SplitBytes(mbuf, firstPayload) - firstPayload = firstPayload[:len] + var firstPayload []byte - buf.ReleaseMulti(mbuf) - defer bytespool.Free(firstPayload) + if reader, ok := link.Reader.(buf.TimeoutReader); ok { + // 0-RTT optimization for HTTP/2: If the payload comes within 50 ms, it can be + // transmitted together. Note we should not get stuck here, as the payload may + // not exist (considering to access MySQL database via a HTTP proxy, where the + // server sends hello to the client first). + if mbuf, _ := reader.ReadMultiBufferTimeout(50 * time.Millisecond); mbuf != nil { + mlen := mbuf.Len() + firstPayload = bytespool.Alloc(mlen) + mbuf, _ = buf.SplitBytes(mbuf, firstPayload) + firstPayload = firstPayload[:mlen] + + buf.ReleaseMulti(mbuf) + defer bytespool.Free(firstPayload) + } + } if err := retry.ExponentialBackoff(5, 100).On(func() error { server := c.serverPicker.PickServer()