From 964859b4bca4e38bbf920594827d59a5823326f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=83=81=E3=82=BB?= <123655015+chise0713@users.noreply.github.com> Date: Sun, 21 Jul 2024 01:26:06 +0800 Subject: [PATCH 1/5] SplitHTTP: Remove unnecessary keepalives (#3565) Remove keep alive since quic-go/http3 doesn't support stream reuse Discussion see https://t.me/projectXray/3782492 Co-authored-by: Fangliding Co-authored-by: xqzr <34030394+xqzr@users.noreply.github.com> Co-authored-by: ll11l1lIllIl1lll <88377095+ll11l1lIllIl1lll@users.noreply.github.com> --- transport/internet/splithttp/dialer.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/transport/internet/splithttp/dialer.go b/transport/internet/splithttp/dialer.go index 5c22f8453005..3df653818efa 100644 --- a/transport/internet/splithttp/dialer.go +++ b/transport/internet/splithttp/dialer.go @@ -87,15 +87,8 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in if isH3 { dest.Network = net.Network_UDP - quicConfig := &quic.Config{ - HandshakeIdleTimeout: 10 * time.Second, - MaxIdleTimeout: 90 * time.Second, - KeepAlivePeriod: 3 * time.Second, - Allow0RTT: true, - } roundTripper := &http3.RoundTripper{ TLSClientConfig: gotlsConfig, - QUICConfig: quicConfig, Dial: func(ctx context.Context, addr string, tlsCfg *gotls.Config, cfg *quic.Config) (quic.EarlyConnection, error) { conn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings) if err != nil { From 529f206d33c35bcdfd6911aa37d604ff5338495b Mon Sep 17 00:00:00 2001 From: mmmray <142015632+mmmray@users.noreply.github.com> Date: Sun, 21 Jul 2024 02:29:50 +0200 Subject: [PATCH 2/5] Fix serverside TLS support of SplitHTTP H1/H2 (#3567) Fix #3566 Also update testsuite so that all tests read and write some data. Opening a connection is not enough to trigger connection errors, because the connection is so lazy. --- transport/internet/splithttp/hub.go | 24 ++++--- .../internet/splithttp/splithttp_test.go | 64 +++++++++++++++++-- 2 files changed, 73 insertions(+), 15 deletions(-) diff --git a/transport/internet/splithttp/hub.go b/transport/internet/splithttp/hub.go index 1ce8da6b0966..d4579bc72d12 100644 --- a/transport/internet/splithttp/hub.go +++ b/transport/internet/splithttp/hub.go @@ -269,7 +269,6 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet tlsConfig := getTLSConfig(streamSettings) l.isH3 = len(tlsConfig.NextProtos) == 1 && tlsConfig.NextProtos[0] == "h3" - if port == net.Port(0) { // unix listener, err = internet.ListenSystem(ctx, &net.UnixAddr{ Name: address.Domain(), @@ -285,9 +284,9 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet Port: int(port), }, streamSettings.SocketSettings) if err != nil { - return nil, errors.New("failed to listen UDP(for SH3) on ", address, ":", port).Base(err) + return nil, errors.New("failed to listen UDP(for SH3) on ", address, ":", port).Base(err) } - h3listener, err := quic.ListenEarly(Conn,tlsConfig, nil) + h3listener, err := quic.ListenEarly(Conn, tlsConfig, nil) if err != nil { return nil, errors.New("failed to listen QUIC(for SH3) on ", address, ":", port).Base(err) } @@ -314,7 +313,6 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet if err != nil { return nil, errors.New("failed to listen TCP(for SH) on ", address, ":", port).Base(err) } - l.listener = listener errors.LogInfo(ctx, "listening TCP(for SH) on ", address, ":", port) // h2cHandler can handle both plaintext HTTP/1.1 and h2c @@ -324,18 +322,24 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet ReadHeaderTimeout: time.Second * 4, MaxHeaderBytes: 8192, } + } + + // tcp/unix (h1/h2) + if listener != nil { + if config := v2tls.ConfigFromStreamSettings(streamSettings); config != nil { + if tlsConfig := config.GetTLSConfig(); tlsConfig != nil { + listener = tls.NewListener(listener, tlsConfig) + } + } + + l.listener = listener + go func() { if err := l.server.Serve(l.listener); err != nil { errors.LogWarningInner(ctx, err, "failed to serve http for splithttp") } }() } - l.listener = listener - if config := v2tls.ConfigFromStreamSettings(streamSettings); config != nil { - if tlsConfig := config.GetTLSConfig(); tlsConfig != nil { - listener = tls.NewListener(listener, tlsConfig) - } - } return l, err } diff --git a/transport/internet/splithttp/splithttp_test.go b/transport/internet/splithttp/splithttp_test.go index 5f59a738caa2..a3b609ab48ac 100644 --- a/transport/internet/splithttp/splithttp_test.go +++ b/transport/internet/splithttp/splithttp_test.go @@ -2,6 +2,7 @@ package splithttp_test import ( "context" + "crypto/rand" gotls "crypto/tls" "fmt" gonet "net" @@ -10,7 +11,9 @@ import ( "testing" "time" + "github.com/google/go-cmp/cmp" "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol/tls/cert" "github.com/xtls/xray-core/testing/servers/tcp" @@ -143,7 +146,16 @@ func Test_listenSHAndDial_TLS(t *testing.T) { } listen, err := ListenSH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) { go func() { - _ = conn.Close() + defer conn.Close() + + var b [1024]byte + conn.SetReadDeadline(time.Now().Add(2 * time.Second)) + _, err := conn.Read(b[:]) + if err != nil { + return + } + + common.Must2(conn.Write([]byte("Response"))) }() }) common.Must(err) @@ -151,7 +163,15 @@ func Test_listenSHAndDial_TLS(t *testing.T) { conn, err := Dial(context.Background(), net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings) common.Must(err) - _ = conn.Close() + + _, err = conn.Write([]byte("Test connection 1")) + common.Must(err) + + var b [1024]byte + n, _ := conn.Read(b[:]) + if string(b[:n]) != "Response" { + t.Error("response: ", string(b[:n])) + } end := time.Now() if !end.Before(start.Add(time.Second * 5)) { @@ -229,18 +249,52 @@ func Test_listenSHAndDial_QUIC(t *testing.T) { } listen, err := ListenSH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) { go func() { - _ = conn.Close() + defer conn.Close() + + b := buf.New() + defer b.Release() + + for { + b.Clear() + if _, err := b.ReadFrom(conn); err != nil { + return + } + common.Must2(conn.Write(b.Bytes())) + } }() }) common.Must(err) defer listen.Close() + time.Sleep(time.Second) + conn, err := Dial(context.Background(), net.UDPDestination(net.DomainAddress("localhost"), listenPort), streamSettings) common.Must(err) - _ = conn.Close() + defer conn.Close() + + const N = 1024 + b1 := make([]byte, N) + common.Must2(rand.Read(b1)) + b2 := buf.New() + + common.Must2(conn.Write(b1)) + + b2.Clear() + common.Must2(b2.ReadFullFrom(conn, N)) + if r := cmp.Diff(b2.Bytes(), b1); r != "" { + t.Error(r) + } + + common.Must2(conn.Write(b1)) + + b2.Clear() + common.Must2(b2.ReadFullFrom(conn, N)) + if r := cmp.Diff(b2.Bytes(), b1); r != "" { + t.Error(r) + } end := time.Now() if !end.Before(start.Add(time.Second * 5)) { t.Error("end: ", end, " start: ", start) } -} \ No newline at end of file +} From 22535d86439952a9764d65119bcc739929492717 Mon Sep 17 00:00:00 2001 From: RPRX <63339210+RPRX@users.noreply.github.com> Date: Sun, 21 Jul 2024 08:55:03 +0000 Subject: [PATCH 3/5] Fix SplitHTTP H3 didn't always reuse QUIC connection https://github.com/XTLS/Xray-core/issues/3560#issuecomment-2241531502 --- transport/internet/splithttp/dialer.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/transport/internet/splithttp/dialer.go b/transport/internet/splithttp/dialer.go index 3df653818efa..99558975aef5 100644 --- a/transport/internet/splithttp/dialer.go +++ b/transport/internet/splithttp/dialer.go @@ -41,6 +41,10 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in return &BrowserDialerClient{} } + tlsConfig := tls.ConfigFromStreamSettings(streamSettings) + isH2 := tlsConfig != nil && !(len(tlsConfig.NextProtocol) == 1 && tlsConfig.NextProtocol[0] == "http/1.1") + isH3 := tlsConfig != nil && (len(tlsConfig.NextProtocol) == 1 && tlsConfig.NextProtocol[0] == "h3") + globalDialerAccess.Lock() defer globalDialerAccess.Unlock() @@ -48,14 +52,13 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in globalDialerMap = make(map[dialerConf]DialerClient) } + if isH3 { + dest.Network = net.Network_UDP + } if client, found := globalDialerMap[dialerConf{dest, streamSettings}]; found { return client } - tlsConfig := tls.ConfigFromStreamSettings(streamSettings) - isH2 := tlsConfig != nil && !(len(tlsConfig.NextProtocol) == 1 && tlsConfig.NextProtocol[0] == "http/1.1") - isH3 := tlsConfig != nil && (len(tlsConfig.NextProtocol) == 1 && tlsConfig.NextProtocol[0] == "h3") - var gotlsConfig *gotls.Config if tlsConfig != nil { @@ -86,7 +89,6 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in var uploadTransport http.RoundTripper if isH3 { - dest.Network = net.Network_UDP roundTripper := &http3.RoundTripper{ TLSClientConfig: gotlsConfig, Dial: func(ctx context.Context, addr string, tlsCfg *gotls.Config, cfg *quic.Config) (quic.EarlyConnection, error) { From 0f65aa8ed803922abf9ce90ce963df54ac69af28 Mon Sep 17 00:00:00 2001 From: RPRX <63339210+RPRX@users.noreply.github.com> Date: Sun, 21 Jul 2024 20:45:05 +0000 Subject: [PATCH 4/5] Fix SplitHTTP H3 waited for downResponse before uploading https://github.com/XTLS/Xray-core/issues/3560#issuecomment-2241750579 --- transport/internet/splithttp/client.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/transport/internet/splithttp/client.go b/transport/internet/splithttp/client.go index 2a467d7dab93..8330d5d749fb 100644 --- a/transport/internet/splithttp/client.go +++ b/transport/internet/splithttp/client.go @@ -94,6 +94,10 @@ func (c *DefaultDialerClient) OpenDownload(ctx context.Context, baseURL string) gotDownResponse.Close() }() + if c.isH3 { + gotConn.Close() + } + // we want to block Dial until we know the remote address of the server, // for logging purposes <-gotConn.Wait() From c27d652d80bf7a0692ee6b8e4bde7b4483d466ac Mon Sep 17 00:00:00 2001 From: RPRX <63339210+RPRX@users.noreply.github.com> Date: Sun, 21 Jul 2024 21:32:26 +0000 Subject: [PATCH 5/5] v1.8.21 --- core/core.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/core.go b/core/core.go index 262cf5f3160f..077b26380c45 100644 --- a/core/core.go +++ b/core/core.go @@ -21,7 +21,7 @@ import ( var ( Version_x byte = 1 Version_y byte = 8 - Version_z byte = 20 + Version_z byte = 21 ) var (