소스 검색

SplitHTTP: Client supports HTTP/3 (#3543)

Closes https://github.com/XTLS/Xray-core/issues/3456

Co-authored-by: Fangliding <[email protected]>
Co-authored-by: mmmray <[email protected]>
ll11l1lIllIl1lll 1 년 전
부모
커밋
c40fc44a34
4개의 변경된 파일36개의 추가작업 그리고 8개의 파일을 삭제
  1. 1 0
      go.mod
  2. 2 0
      go.sum
  3. 2 1
      transport/internet/splithttp/client.go
  4. 31 7
      transport/internet/splithttp/dialer.go

+ 1 - 0
go.mod

@@ -46,6 +46,7 @@ require (
 	github.com/klauspost/cpuid/v2 v2.2.7 // indirect
 	github.com/onsi/ginkgo/v2 v2.19.0 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
+	github.com/quic-go/qpack v0.4.0 // indirect
 	github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
 	github.com/vishvananda/netns v0.0.4 // indirect
 	go.uber.org/mock v0.4.0 // indirect

+ 2 - 0
go.sum

@@ -110,6 +110,8 @@ github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
+github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
 github.com/quic-go/quic-go v0.45.1 h1:tPfeYCk+uZHjmDRwHHQmvHRYL2t44ROTujLeFVBmjCA=
 github.com/quic-go/quic-go v0.45.1/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI=
 github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=

+ 2 - 1
transport/internet/splithttp/client.go

@@ -32,6 +32,7 @@ type DefaultDialerClient struct {
 	download        *http.Client
 	upload          *http.Client
 	isH2            bool
+	isH3            bool
 	// pool of net.Conn, created using dialUploadConn
 	uploadRawPool  *sync.Pool
 	dialUploadConn func(ctxInner context.Context) (net.Conn, error)
@@ -118,7 +119,7 @@ func (c *DefaultDialerClient) SendUploadRequest(ctx context.Context, url string,
 	}
 	req.Header = c.transportConfig.GetRequestHeader()
 
-	if c.isH2 {
+	if c.isH2 || c.isH3 {
 		resp, err := c.upload.Do(req)
 		if err != nil {
 			return err

+ 31 - 7
transport/internet/splithttp/dialer.go

@@ -10,6 +10,8 @@ import (
 	"sync"
 	"time"
 
+	"github.com/quic-go/quic-go"
+	"github.com/quic-go/quic-go/http3"
 	"github.com/xtls/xray-core/common"
 	"github.com/xtls/xray-core/common/buf"
 	"github.com/xtls/xray-core/common/errors"
@@ -50,12 +52,9 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
 		return client
 	}
 
-	if browser_dialer.HasBrowserDialer() {
-		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")
 
 	var gotlsConfig *gotls.Config
 
@@ -83,10 +82,35 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
 		return conn, nil
 	}
 
-	var uploadTransport http.RoundTripper
 	var downloadTransport http.RoundTripper
+	var uploadTransport http.RoundTripper
 
-	if isH2 {
+	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 {
+					return nil, err
+				}
+				udpAddr, err := net.ResolveUDPAddr("udp", conn.RemoteAddr().String())
+				if err != nil {
+					return nil, err
+				}
+				return quic.DialEarly(ctx, conn.(*internet.PacketConnWrapper).Conn.(*net.UDPConn), udpAddr, tlsCfg, cfg)
+			},
+		}
+		downloadTransport = roundTripper
+		uploadTransport = roundTripper
+	} else if isH2 {
 		downloadTransport = &http2.Transport{
 			DialTLSContext: func(ctxInner context.Context, network string, addr string, cfg *gotls.Config) (net.Conn, error) {
 				return dialContext(ctxInner)
@@ -107,7 +131,6 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
 			// http.Client and our custom dial context.
 			DisableKeepAlives: true,
 		}
-
 		// we use uploadRawPool for that
 		uploadTransport = nil
 	}
@@ -121,6 +144,7 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
 			Transport: uploadTransport,
 		},
 		isH2:           isH2,
+		isH3:           isH3,
 		uploadRawPool:  &sync.Pool{},
 		dialUploadConn: dialContext,
 	}