|
@@ -3,6 +3,7 @@ package splithttp
|
|
|
import (
|
|
|
"context"
|
|
|
gotls "crypto/tls"
|
|
|
+ "fmt"
|
|
|
"io"
|
|
|
"net/http"
|
|
|
"net/http/httptrace"
|
|
@@ -83,23 +84,32 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
|
|
|
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 {
|
|
|
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
|
|
|
realityConfig := reality.ConfigFromStreamSettings(streamSettings)
|
|
|
|
|
|
- isH2 := false
|
|
|
- isH3 := false
|
|
|
-
|
|
|
- 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
|
|
|
+ httpVersion := decideHTTPVersion(tlsConfig, realityConfig)
|
|
|
+ if httpVersion == "3" {
|
|
|
+ dest.Network = net.Network_UDP // better to keep this line
|
|
|
}
|
|
|
|
|
|
var gotlsConfig *gotls.Config
|
|
@@ -138,7 +148,7 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
|
|
|
|
|
var transport http.RoundTripper
|
|
|
|
|
|
- if isH3 {
|
|
|
+ if httpVersion == "3" {
|
|
|
if keepAlivePeriod == 0 {
|
|
|
keepAlivePeriod = quicgoH3KeepAlivePeriod
|
|
|
}
|
|
@@ -194,7 +204,7 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
|
|
return quic.DialEarly(ctx, udpConn, udpAddr, tlsCfg, cfg)
|
|
|
},
|
|
|
}
|
|
|
- } else if isH2 {
|
|
|
+ } else if httpVersion == "2" {
|
|
|
if keepAlivePeriod == 0 {
|
|
|
keepAlivePeriod = chromeH2KeepAlivePeriod
|
|
|
}
|
|
@@ -228,8 +238,7 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
|
|
client: &http.Client{
|
|
|
Transport: transport,
|
|
|
},
|
|
|
- isH2: isH2,
|
|
|
- isH3: isH3,
|
|
|
+ httpVersion: httpVersion,
|
|
|
uploadRawPool: &sync.Pool{},
|
|
|
dialUploadConn: dialContext,
|
|
|
}
|
|
@@ -242,16 +251,16 @@ func init() {
|
|
|
}
|
|
|
|
|
|
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)
|
|
|
realityConfig := reality.ConfigFromStreamSettings(streamSettings)
|
|
|
|
|
|
- scMaxEachPostBytes := transportConfiguration.GetNormalizedScMaxEachPostBytes()
|
|
|
- scMinPostsIntervalMs := transportConfiguration.GetNormalizedScMinPostsIntervalMs()
|
|
|
+ httpVersion := decideHTTPVersion(tlsConfig, realityConfig)
|
|
|
+ if httpVersion == "3" {
|
|
|
+ dest.Network = net.Network_UDP
|
|
|
+ }
|
|
|
+
|
|
|
+ transportConfiguration := streamSettings.ProtocolSettings.(*Config)
|
|
|
+ var requestURL url.URL
|
|
|
|
|
|
if tlsConfig != nil || realityConfig != nil {
|
|
|
requestURL.Scheme = "https"
|
|
@@ -275,8 +284,21 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
|
|
|
|
|
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
|
|
|
+ httpClient2 := httpClient
|
|
|
var muxRes2 *muxResource
|
|
|
if transportConfiguration.DownloadSettings != nil {
|
|
|
globalDialerAccess.Lock()
|
|
@@ -286,9 +308,12 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
|
|
globalDialerAccess.Unlock()
|
|
|
memory2 := streamSettings.DownloadSettings
|
|
|
dest2 := *memory2.Destination // just panic
|
|
|
- httpClient2, muxRes2 = getHTTPClient(ctx, dest2, memory2)
|
|
|
tlsConfig2 := tls.ConfigFromStreamSettings(memory2)
|
|
|
realityConfig2 := reality.ConfigFromStreamSettings(memory2)
|
|
|
+ httpVersion2 := decideHTTPVersion(tlsConfig2, realityConfig2)
|
|
|
+ if httpVersion2 == "3" {
|
|
|
+ dest2.Network = net.Network_UDP
|
|
|
+ }
|
|
|
if tlsConfig2 != nil || realityConfig2 != nil {
|
|
|
requestURL2.Scheme = "https"
|
|
|
} else {
|
|
@@ -307,20 +332,10 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
|
|
}
|
|
|
requestURL2.Path = config2.GetNormalizedPath() + sessionIdUuid.String()
|
|
|
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 reader io.ReadCloser
|
|
|
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
|
|
|
}
|
|
|
|
|
|
+ scMaxEachPostBytes := transportConfiguration.GetNormalizedScMaxEachPostBytes()
|
|
|
+ scMinPostsIntervalMs := transportConfiguration.GetNormalizedScMinPostsIntervalMs()
|
|
|
+
|
|
|
maxUploadSize := scMaxEachPostBytes.roll()
|
|
|
// WithSizeLimit(0) will still allow single bytes to pass, and a lot of
|
|
|
// 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
|
|
|
|
|
|
if scMinPostsIntervalMs.From > 0 {
|
|
|
- sleep := time.Duration(scMinPostsIntervalMs.roll())*time.Millisecond - time.Since(lastWrite)
|
|
|
- if sleep > 0 {
|
|
|
- time.Sleep(sleep)
|
|
|
- }
|
|
|
+ time.Sleep(time.Duration(scMinPostsIntervalMs.roll())*time.Millisecond - time.Since(lastWrite))
|
|
|
}
|
|
|
|
|
|
// by offloading the uploads into a buffered pipe, multiple conn.Write
|