mirror of https://github.com/XTLS/Xray-core
XHTTP client: Add decideHTTPVersion() and more logs
https://github.com/XTLS/Xray-core/pull/4150#issuecomment-2537981368dependabot/go_modules/google.golang.org/grpc-1.69.0
parent
743435d6e6
commit
7463561856
|
@ -39,8 +39,7 @@ type DialerClient interface {
|
||||||
type DefaultDialerClient struct {
|
type DefaultDialerClient struct {
|
||||||
transportConfig *Config
|
transportConfig *Config
|
||||||
client *http.Client
|
client *http.Client
|
||||||
isH2 bool
|
httpVersion string
|
||||||
isH3 bool
|
|
||||||
// pool of net.Conn, created using dialUploadConn
|
// pool of net.Conn, created using dialUploadConn
|
||||||
uploadRawPool *sync.Pool
|
uploadRawPool *sync.Pool
|
||||||
dialUploadConn func(ctxInner context.Context) (net.Conn, error)
|
dialUploadConn func(ctxInner context.Context) (net.Conn, error)
|
||||||
|
@ -172,7 +171,7 @@ func (c *DefaultDialerClient) SendUploadRequest(ctx context.Context, url string,
|
||||||
req.ContentLength = contentLength
|
req.ContentLength = contentLength
|
||||||
req.Header = c.transportConfig.GetRequestHeader()
|
req.Header = c.transportConfig.GetRequestHeader()
|
||||||
|
|
||||||
if c.isH2 || c.isH3 {
|
if c.httpVersion != "1.1" {
|
||||||
resp, err := c.client.Do(req)
|
resp, err := c.client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -3,6 +3,7 @@ package splithttp
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
gotls "crypto/tls"
|
gotls "crypto/tls"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptrace"
|
"net/http/httptrace"
|
||||||
|
@ -83,23 +84,32 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
|
||||||
return res.Resource.(DialerClient), res
|
return res.Resource.(DialerClient), res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func decideHTTPVersion(tlsConfig *tls.Config, realityConfig *reality.Config) string {
|
||||||
|
if realityConfig != nil {
|
||||||
|
return "2"
|
||||||
|
}
|
||||||
|
if tlsConfig == nil {
|
||||||
|
return "1.1"
|
||||||
|
}
|
||||||
|
if len(tlsConfig.NextProtocol) != 1 {
|
||||||
|
return "2"
|
||||||
|
}
|
||||||
|
if tlsConfig.NextProtocol[0] == "http/1.1" {
|
||||||
|
return "1.1"
|
||||||
|
}
|
||||||
|
if tlsConfig.NextProtocol[0] == "h3" {
|
||||||
|
return "3"
|
||||||
|
}
|
||||||
|
return "2"
|
||||||
|
}
|
||||||
|
|
||||||
func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStreamConfig) DialerClient {
|
func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStreamConfig) DialerClient {
|
||||||
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
|
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
|
||||||
realityConfig := reality.ConfigFromStreamSettings(streamSettings)
|
realityConfig := reality.ConfigFromStreamSettings(streamSettings)
|
||||||
|
|
||||||
isH2 := false
|
httpVersion := decideHTTPVersion(tlsConfig, realityConfig)
|
||||||
isH3 := false
|
if httpVersion == "3" {
|
||||||
|
dest.Network = net.Network_UDP // better to keep this line
|
||||||
if tlsConfig != nil {
|
|
||||||
isH2 = !(len(tlsConfig.NextProtocol) == 1 && tlsConfig.NextProtocol[0] == "http/1.1")
|
|
||||||
isH3 = len(tlsConfig.NextProtocol) == 1 && tlsConfig.NextProtocol[0] == "h3"
|
|
||||||
} else if realityConfig != nil {
|
|
||||||
isH2 = true
|
|
||||||
isH3 = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if isH3 {
|
|
||||||
dest.Network = net.Network_UDP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var gotlsConfig *gotls.Config
|
var gotlsConfig *gotls.Config
|
||||||
|
@ -138,7 +148,7 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
||||||
|
|
||||||
var transport http.RoundTripper
|
var transport http.RoundTripper
|
||||||
|
|
||||||
if isH3 {
|
if httpVersion == "3" {
|
||||||
if keepAlivePeriod == 0 {
|
if keepAlivePeriod == 0 {
|
||||||
keepAlivePeriod = quicgoH3KeepAlivePeriod
|
keepAlivePeriod = quicgoH3KeepAlivePeriod
|
||||||
}
|
}
|
||||||
|
@ -194,7 +204,7 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
||||||
return quic.DialEarly(ctx, udpConn, udpAddr, tlsCfg, cfg)
|
return quic.DialEarly(ctx, udpConn, udpAddr, tlsCfg, cfg)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else if isH2 {
|
} else if httpVersion == "2" {
|
||||||
if keepAlivePeriod == 0 {
|
if keepAlivePeriod == 0 {
|
||||||
keepAlivePeriod = chromeH2KeepAlivePeriod
|
keepAlivePeriod = chromeH2KeepAlivePeriod
|
||||||
}
|
}
|
||||||
|
@ -228,8 +238,7 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
||||||
client: &http.Client{
|
client: &http.Client{
|
||||||
Transport: transport,
|
Transport: transport,
|
||||||
},
|
},
|
||||||
isH2: isH2,
|
httpVersion: httpVersion,
|
||||||
isH3: isH3,
|
|
||||||
uploadRawPool: &sync.Pool{},
|
uploadRawPool: &sync.Pool{},
|
||||||
dialUploadConn: dialContext,
|
dialUploadConn: dialContext,
|
||||||
}
|
}
|
||||||
|
@ -242,16 +251,16 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) {
|
func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) {
|
||||||
errors.LogInfo(ctx, "dialing splithttp to ", dest)
|
|
||||||
|
|
||||||
var requestURL url.URL
|
|
||||||
|
|
||||||
transportConfiguration := streamSettings.ProtocolSettings.(*Config)
|
|
||||||
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
|
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
|
||||||
realityConfig := reality.ConfigFromStreamSettings(streamSettings)
|
realityConfig := reality.ConfigFromStreamSettings(streamSettings)
|
||||||
|
|
||||||
scMaxEachPostBytes := transportConfiguration.GetNormalizedScMaxEachPostBytes()
|
httpVersion := decideHTTPVersion(tlsConfig, realityConfig)
|
||||||
scMinPostsIntervalMs := transportConfiguration.GetNormalizedScMinPostsIntervalMs()
|
if httpVersion == "3" {
|
||||||
|
dest.Network = net.Network_UDP
|
||||||
|
}
|
||||||
|
|
||||||
|
transportConfiguration := streamSettings.ProtocolSettings.(*Config)
|
||||||
|
var requestURL url.URL
|
||||||
|
|
||||||
if tlsConfig != nil || realityConfig != nil {
|
if tlsConfig != nil || realityConfig != nil {
|
||||||
requestURL.Scheme = "https"
|
requestURL.Scheme = "https"
|
||||||
|
@ -275,8 +284,21 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
||||||
|
|
||||||
httpClient, muxRes := getHTTPClient(ctx, dest, streamSettings)
|
httpClient, muxRes := getHTTPClient(ctx, dest, streamSettings)
|
||||||
|
|
||||||
httpClient2 := httpClient
|
mode := transportConfiguration.Mode
|
||||||
|
if mode == "" || mode == "auto" {
|
||||||
|
mode = "packet-up"
|
||||||
|
if httpVersion == "2" {
|
||||||
|
mode = "stream-up"
|
||||||
|
}
|
||||||
|
if realityConfig != nil && transportConfiguration.DownloadSettings == nil {
|
||||||
|
mode = "stream-one"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errors.LogInfo(ctx, fmt.Sprintf("XHTTP is dialing to %s, mode %s, HTTP version %s, host %s", dest, mode, httpVersion, requestURL.Host))
|
||||||
|
|
||||||
requestURL2 := requestURL
|
requestURL2 := requestURL
|
||||||
|
httpClient2 := httpClient
|
||||||
var muxRes2 *muxResource
|
var muxRes2 *muxResource
|
||||||
if transportConfiguration.DownloadSettings != nil {
|
if transportConfiguration.DownloadSettings != nil {
|
||||||
globalDialerAccess.Lock()
|
globalDialerAccess.Lock()
|
||||||
|
@ -286,9 +308,12 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
||||||
globalDialerAccess.Unlock()
|
globalDialerAccess.Unlock()
|
||||||
memory2 := streamSettings.DownloadSettings
|
memory2 := streamSettings.DownloadSettings
|
||||||
dest2 := *memory2.Destination // just panic
|
dest2 := *memory2.Destination // just panic
|
||||||
httpClient2, muxRes2 = getHTTPClient(ctx, dest2, memory2)
|
|
||||||
tlsConfig2 := tls.ConfigFromStreamSettings(memory2)
|
tlsConfig2 := tls.ConfigFromStreamSettings(memory2)
|
||||||
realityConfig2 := reality.ConfigFromStreamSettings(memory2)
|
realityConfig2 := reality.ConfigFromStreamSettings(memory2)
|
||||||
|
httpVersion2 := decideHTTPVersion(tlsConfig2, realityConfig2)
|
||||||
|
if httpVersion2 == "3" {
|
||||||
|
dest2.Network = net.Network_UDP
|
||||||
|
}
|
||||||
if tlsConfig2 != nil || realityConfig2 != nil {
|
if tlsConfig2 != nil || realityConfig2 != nil {
|
||||||
requestURL2.Scheme = "https"
|
requestURL2.Scheme = "https"
|
||||||
} else {
|
} else {
|
||||||
|
@ -307,20 +332,10 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
||||||
}
|
}
|
||||||
requestURL2.Path = config2.GetNormalizedPath() + sessionIdUuid.String()
|
requestURL2.Path = config2.GetNormalizedPath() + sessionIdUuid.String()
|
||||||
requestURL2.RawQuery = config2.GetNormalizedQuery()
|
requestURL2.RawQuery = config2.GetNormalizedQuery()
|
||||||
|
httpClient2, muxRes2 = getHTTPClient(ctx, dest2, memory2)
|
||||||
|
errors.LogInfo(ctx, fmt.Sprintf("XHTTP is downloading from %s, mode %s, HTTP version %s, host %s", dest2, "stream-down", httpVersion2, requestURL2.Host))
|
||||||
}
|
}
|
||||||
|
|
||||||
mode := transportConfiguration.Mode
|
|
||||||
if mode == "" || mode == "auto" {
|
|
||||||
mode = "packet-up"
|
|
||||||
if (tlsConfig != nil && (len(tlsConfig.NextProtocol) != 1 || tlsConfig.NextProtocol[0] == "h2")) || realityConfig != nil {
|
|
||||||
mode = "stream-up"
|
|
||||||
}
|
|
||||||
if realityConfig != nil && transportConfiguration.DownloadSettings == nil {
|
|
||||||
mode = "stream-one"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
errors.LogInfo(ctx, "XHTTP is using mode: "+mode)
|
|
||||||
|
|
||||||
var writer io.WriteCloser
|
var writer io.WriteCloser
|
||||||
var reader io.ReadCloser
|
var reader io.ReadCloser
|
||||||
var remoteAddr, localAddr net.Addr
|
var remoteAddr, localAddr net.Addr
|
||||||
|
@ -373,6 +388,9 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
||||||
return stat.Connection(&conn), nil
|
return stat.Connection(&conn), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scMaxEachPostBytes := transportConfiguration.GetNormalizedScMaxEachPostBytes()
|
||||||
|
scMinPostsIntervalMs := transportConfiguration.GetNormalizedScMinPostsIntervalMs()
|
||||||
|
|
||||||
maxUploadSize := scMaxEachPostBytes.roll()
|
maxUploadSize := scMaxEachPostBytes.roll()
|
||||||
// WithSizeLimit(0) will still allow single bytes to pass, and a lot of
|
// WithSizeLimit(0) will still allow single bytes to pass, and a lot of
|
||||||
// code relies on this behavior. Subtract 1 so that together with
|
// code relies on this behavior. Subtract 1 so that together with
|
||||||
|
@ -408,10 +426,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
||||||
seq += 1
|
seq += 1
|
||||||
|
|
||||||
if scMinPostsIntervalMs.From > 0 {
|
if scMinPostsIntervalMs.From > 0 {
|
||||||
sleep := time.Duration(scMinPostsIntervalMs.roll())*time.Millisecond - time.Since(lastWrite)
|
time.Sleep(time.Duration(scMinPostsIntervalMs.roll())*time.Millisecond - time.Since(lastWrite))
|
||||||
if sleep > 0 {
|
|
||||||
time.Sleep(sleep)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// by offloading the uploads into a buffered pipe, multiple conn.Write
|
// by offloading the uploads into a buffered pipe, multiple conn.Write
|
||||||
|
|
|
@ -333,30 +333,30 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet
|
||||||
Net: "unix",
|
Net: "unix",
|
||||||
}, streamSettings.SocketSettings)
|
}, streamSettings.SocketSettings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("failed to listen unix domain socket(for SH) on ", address).Base(err)
|
return nil, errors.New("failed to listen UNIX domain socket for XHTTP on ", address).Base(err)
|
||||||
}
|
}
|
||||||
errors.LogInfo(ctx, "listening unix domain socket(for SH) on ", address)
|
errors.LogInfo(ctx, "listening UNIX domain socket for XHTTP on ", address)
|
||||||
} else if l.isH3 { // quic
|
} else if l.isH3 { // quic
|
||||||
Conn, err := internet.ListenSystemPacket(context.Background(), &net.UDPAddr{
|
Conn, err := internet.ListenSystemPacket(context.Background(), &net.UDPAddr{
|
||||||
IP: address.IP(),
|
IP: address.IP(),
|
||||||
Port: int(port),
|
Port: int(port),
|
||||||
}, streamSettings.SocketSettings)
|
}, streamSettings.SocketSettings)
|
||||||
if err != nil {
|
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 XHTTP/3 on ", address, ":", port).Base(err)
|
||||||
}
|
}
|
||||||
h3listener, err := quic.ListenEarly(Conn, tlsConfig, nil)
|
h3listener, err := quic.ListenEarly(Conn, tlsConfig, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("failed to listen QUIC(for SH3) on ", address, ":", port).Base(err)
|
return nil, errors.New("failed to listen QUIC for XHTTP/3 on ", address, ":", port).Base(err)
|
||||||
}
|
}
|
||||||
l.h3listener = h3listener
|
l.h3listener = h3listener
|
||||||
errors.LogInfo(ctx, "listening QUIC(for SH3) on ", address, ":", port)
|
errors.LogInfo(ctx, "listening QUIC for XHTTP/3 on ", address, ":", port)
|
||||||
|
|
||||||
l.h3server = &http3.Server{
|
l.h3server = &http3.Server{
|
||||||
Handler: handler,
|
Handler: handler,
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
if err := l.h3server.ServeListener(l.h3listener); err != nil {
|
if err := l.h3server.ServeListener(l.h3listener); err != nil {
|
||||||
errors.LogWarningInner(ctx, err, "failed to serve http3 for splithttp")
|
errors.LogWarningInner(ctx, err, "failed to serve HTTP/3 for XHTTP/3")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
} else { // tcp
|
} else { // tcp
|
||||||
|
@ -369,9 +369,9 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet
|
||||||
Port: int(port),
|
Port: int(port),
|
||||||
}, streamSettings.SocketSettings)
|
}, streamSettings.SocketSettings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("failed to listen TCP(for SH) on ", address, ":", port).Base(err)
|
return nil, errors.New("failed to listen TCP for XHTTP on ", address, ":", port).Base(err)
|
||||||
}
|
}
|
||||||
errors.LogInfo(ctx, "listening TCP(for SH) on ", address, ":", port)
|
errors.LogInfo(ctx, "listening TCP for XHTTP on ", address, ":", port)
|
||||||
}
|
}
|
||||||
|
|
||||||
// tcp/unix (h1/h2)
|
// tcp/unix (h1/h2)
|
||||||
|
@ -397,7 +397,7 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if err := l.server.Serve(l.listener); err != nil {
|
if err := l.server.Serve(l.listener); err != nil {
|
||||||
errors.LogWarningInner(ctx, err, "failed to serve http for splithttp")
|
errors.LogWarningInner(ctx, err, "failed to serve HTTP for XHTTP")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ func (h *uploadQueue) Push(p Packet) error {
|
||||||
if p.Reader != nil {
|
if p.Reader != nil {
|
||||||
p.Reader.Close()
|
p.Reader.Close()
|
||||||
}
|
}
|
||||||
return errors.New("splithttp packet queue closed")
|
return errors.New("packet queue closed")
|
||||||
}
|
}
|
||||||
|
|
||||||
h.pushedPackets <- p
|
h.pushedPackets <- p
|
||||||
|
|
Loading…
Reference in New Issue