Skip to content

Commit c40fc44

Browse files
ll11l1lIllIl1lllFanglidingmmmray
committed
SplitHTTP: Client supports HTTP/3 (#3543)
Closes #3456 Co-authored-by: Fangliding <[email protected]> Co-authored-by: mmmray <[email protected]>
1 parent 02cd3b8 commit c40fc44

File tree

4 files changed

+36
-8
lines changed

4 files changed

+36
-8
lines changed

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ require (
4646
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
4747
github.com/onsi/ginkgo/v2 v2.19.0 // indirect
4848
github.com/pmezard/go-difflib v1.0.0 // indirect
49+
github.com/quic-go/qpack v0.4.0 // indirect
4950
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
5051
github.com/vishvananda/netns v0.0.4 // indirect
5152
go.uber.org/mock v0.4.0 // indirect

go.sum

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

transport/internet/splithttp/client.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ type DefaultDialerClient struct {
3232
download *http.Client
3333
upload *http.Client
3434
isH2 bool
35+
isH3 bool
3536
// pool of net.Conn, created using dialUploadConn
3637
uploadRawPool *sync.Pool
3738
dialUploadConn func(ctxInner context.Context) (net.Conn, error)
@@ -118,7 +119,7 @@ func (c *DefaultDialerClient) SendUploadRequest(ctx context.Context, url string,
118119
}
119120
req.Header = c.transportConfig.GetRequestHeader()
120121

121-
if c.isH2 {
122+
if c.isH2 || c.isH3 {
122123
resp, err := c.upload.Do(req)
123124
if err != nil {
124125
return err

transport/internet/splithttp/dialer.go

+31-7
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010
"sync"
1111
"time"
1212

13+
"github.com/quic-go/quic-go"
14+
"github.com/quic-go/quic-go/http3"
1315
"github.com/xtls/xray-core/common"
1416
"github.com/xtls/xray-core/common/buf"
1517
"github.com/xtls/xray-core/common/errors"
@@ -50,12 +52,9 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
5052
return client
5153
}
5254

53-
if browser_dialer.HasBrowserDialer() {
54-
return &BrowserDialerClient{}
55-
}
56-
5755
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
5856
isH2 := tlsConfig != nil && !(len(tlsConfig.NextProtocol) == 1 && tlsConfig.NextProtocol[0] == "http/1.1")
57+
isH3 := tlsConfig != nil && (len(tlsConfig.NextProtocol) == 1 && tlsConfig.NextProtocol[0] == "h3")
5958

6059
var gotlsConfig *gotls.Config
6160

@@ -83,10 +82,35 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
8382
return conn, nil
8483
}
8584

86-
var uploadTransport http.RoundTripper
8785
var downloadTransport http.RoundTripper
86+
var uploadTransport http.RoundTripper
8887

89-
if isH2 {
88+
if isH3 {
89+
dest.Network = net.Network_UDP
90+
quicConfig := &quic.Config{
91+
HandshakeIdleTimeout: 10 * time.Second,
92+
MaxIdleTimeout: 90 * time.Second,
93+
KeepAlivePeriod: 3 * time.Second,
94+
Allow0RTT: true,
95+
}
96+
roundTripper := &http3.RoundTripper{
97+
TLSClientConfig: gotlsConfig,
98+
QUICConfig: quicConfig,
99+
Dial: func(ctx context.Context, addr string, tlsCfg *gotls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
100+
conn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings)
101+
if err != nil {
102+
return nil, err
103+
}
104+
udpAddr, err := net.ResolveUDPAddr("udp", conn.RemoteAddr().String())
105+
if err != nil {
106+
return nil, err
107+
}
108+
return quic.DialEarly(ctx, conn.(*internet.PacketConnWrapper).Conn.(*net.UDPConn), udpAddr, tlsCfg, cfg)
109+
},
110+
}
111+
downloadTransport = roundTripper
112+
uploadTransport = roundTripper
113+
} else if isH2 {
90114
downloadTransport = &http2.Transport{
91115
DialTLSContext: func(ctxInner context.Context, network string, addr string, cfg *gotls.Config) (net.Conn, error) {
92116
return dialContext(ctxInner)
@@ -107,7 +131,6 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
107131
// http.Client and our custom dial context.
108132
DisableKeepAlives: true,
109133
}
110-
111134
// we use uploadRawPool for that
112135
uploadTransport = nil
113136
}
@@ -121,6 +144,7 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
121144
Transport: uploadTransport,
122145
},
123146
isH2: isH2,
147+
isH3: isH3,
124148
uploadRawPool: &sync.Pool{},
125149
dialUploadConn: dialContext,
126150
}

0 commit comments

Comments
 (0)