chore: let xhttp server passing stream-one and stream-up mode test under http1

This commit is contained in:
wwqgtxx
2026-04-19 12:34:46 +08:00
parent 3f2b0ba631
commit 2cfff83e85
2 changed files with 183 additions and 169 deletions
+167 -160
View File
@@ -378,40 +378,43 @@ func TestInboundVless_XHTTP(t *testing.T) {
}
return inboundOptions, outboundOptions
}
t.Run("nosplit", func(t *testing.T) {
t.Run("single", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
testInboundVlessTLS(t, inboundOptions, outboundOptions, false)
})
t.Run("reuse", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
testInboundVlessTLS(t, inboundOptions, withXHTTPReuse(outboundOptions), false)
})
})
t.Run("split", func(t *testing.T) {
if testCase.mode == "stream-one" { // stream-one not supported download settings
return
}
t.Run("single", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
outboundOptions.XHTTPOpts.DownloadSettings = &outbound.XHTTPDownloadSettings{}
testInboundVlessTLS(t, inboundOptions, outboundOptions, false)
})
t.Run("reuse", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
outboundOptions.XHTTPOpts.DownloadSettings = &outbound.XHTTPDownloadSettings{}
testInboundVlessTLS(t, inboundOptions, withXHTTPReuse(outboundOptions), false)
})
})
testInboundVless_XHTTP(t, getConfig, testCase.mode)
})
}
}
func testInboundVless_XHTTP(t *testing.T, getConfig func() (inbound.VlessOption, outbound.VlessOption), mode string) {
t.Run("nosplit", func(t *testing.T) {
t.Run("single", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
testInboundVlessTLS(t, inboundOptions, outboundOptions, false)
})
t.Run("reuse", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
testInboundVlessTLS(t, inboundOptions, withXHTTPReuse(outboundOptions), false)
})
})
t.Run("split", func(t *testing.T) {
if mode == "stream-one" { // stream-one not supported download settings
return
}
t.Run("single", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
outboundOptions.XHTTPOpts.DownloadSettings = &outbound.XHTTPDownloadSettings{}
testInboundVlessTLS(t, inboundOptions, outboundOptions, false)
})
t.Run("reuse", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
outboundOptions.XHTTPOpts.DownloadSettings = &outbound.XHTTPDownloadSettings{}
testInboundVlessTLS(t, inboundOptions, withXHTTPReuse(outboundOptions), false)
})
})
}
func TestInboundVless_XHTTP_Reality(t *testing.T) {
testCases := []struct {
mode string
@@ -455,40 +458,43 @@ func TestInboundVless_XHTTP_Reality(t *testing.T) {
}
return inboundOptions, outboundOptions
}
t.Run("nosplit", func(t *testing.T) {
t.Run("single", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
testInboundVless(t, inboundOptions, outboundOptions)
})
t.Run("reuse", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
testInboundVless(t, inboundOptions, withXHTTPReuse(outboundOptions))
})
})
t.Run("split", func(t *testing.T) {
if testCase.mode == "stream-one" { // stream-one not supported download settings
return
}
t.Run("single", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
outboundOptions.XHTTPOpts.DownloadSettings = &outbound.XHTTPDownloadSettings{}
testInboundVless(t, inboundOptions, outboundOptions)
})
t.Run("reuse", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
outboundOptions.XHTTPOpts.DownloadSettings = &outbound.XHTTPDownloadSettings{}
testInboundVless(t, inboundOptions, withXHTTPReuse(outboundOptions))
})
})
testInboundVless_XHTTP_Reality(t, getConfig, testCase.mode)
})
}
}
func testInboundVless_XHTTP_Reality(t *testing.T, getConfig func() (inbound.VlessOption, outbound.VlessOption), mode string) {
t.Run("nosplit", func(t *testing.T) {
t.Run("single", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
testInboundVless(t, inboundOptions, outboundOptions)
})
t.Run("reuse", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
testInboundVless(t, inboundOptions, withXHTTPReuse(outboundOptions))
})
})
t.Run("split", func(t *testing.T) {
if mode == "stream-one" { // stream-one not supported download settings
return
}
t.Run("single", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
outboundOptions.XHTTPOpts.DownloadSettings = &outbound.XHTTPDownloadSettings{}
testInboundVless(t, inboundOptions, outboundOptions)
})
t.Run("reuse", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
outboundOptions.XHTTPOpts.DownloadSettings = &outbound.XHTTPDownloadSettings{}
testInboundVless(t, inboundOptions, withXHTTPReuse(outboundOptions))
})
})
}
func TestInboundVless_XHTTP_Encryption(t *testing.T) {
privateKeyBase64, passwordBase64, _, err := encryption.GenX25519("")
if err != nil {
@@ -526,123 +532,124 @@ func TestInboundVless_XHTTP_Encryption(t *testing.T) {
}
return inboundOptions, outboundOptions
}
t.Run("nosplit", func(t *testing.T) {
t.Run("single", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
testInboundVless(t, inboundOptions, outboundOptions)
})
t.Run("reuse", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
testInboundVless(t, inboundOptions, withXHTTPReuse(outboundOptions))
})
})
t.Run("split", func(t *testing.T) {
if testCase.mode == "stream-one" { // stream-one not supported download settings
return
}
t.Run("single", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
outboundOptions.XHTTPOpts.DownloadSettings = &outbound.XHTTPDownloadSettings{}
testInboundVless(t, inboundOptions, outboundOptions)
})
t.Run("reuse", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
outboundOptions.XHTTPOpts.DownloadSettings = &outbound.XHTTPDownloadSettings{}
testInboundVless(t, inboundOptions, withXHTTPReuse(outboundOptions))
})
})
testInboundVless_XHTTP_Encryption(t, getConfig, testCase.mode)
})
}
}
func TestInboundVless_XHTTP_PacketUp_H1(t *testing.T) {
getConfig := func() (inbound.VlessOption, outbound.VlessOption) {
inboundOptions := inbound.VlessOption{
Certificate: tlsCertificate,
PrivateKey: tlsPrivateKey,
XHTTPConfig: inbound.XHTTPConfig{
Path: "/vless-xhttp",
Host: "example.com",
Mode: "packet-up",
},
}
outboundOptions := outbound.VlessOption{
TLS: true,
Fingerprint: tlsFingerprint,
Network: "xhttp",
ALPN: []string{"http/1.1"},
XHTTPOpts: outbound.XHTTPOptions{
Path: "/vless-xhttp",
Host: "example.com",
Mode: "packet-up",
},
}
return inboundOptions, outboundOptions
}
func testInboundVless_XHTTP_Encryption(t *testing.T, getConfig func() (inbound.VlessOption, outbound.VlessOption), mode string) {
t.Run("nosplit", func(t *testing.T) {
t.Run("single", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
testInboundVless(t, inboundOptions, outboundOptions)
})
t.Run("default", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
testInboundVlessTLS(t, inboundOptions, outboundOptions, false)
t.Run("reuse", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
testInboundVless(t, inboundOptions, withXHTTPReuse(outboundOptions))
})
})
t.Run("reuse", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
testInboundVlessTLS(t, inboundOptions, withXHTTPReuse(outboundOptions), false)
t.Run("split", func(t *testing.T) {
if mode == "stream-one" { // stream-one not supported download settings
return
}
t.Run("single", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
outboundOptions.XHTTPOpts.DownloadSettings = &outbound.XHTTPDownloadSettings{}
testInboundVless(t, inboundOptions, outboundOptions)
})
t.Run("reuse", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
outboundOptions.XHTTPOpts.DownloadSettings = &outbound.XHTTPDownloadSettings{}
testInboundVless(t, inboundOptions, withXHTTPReuse(outboundOptions))
})
})
}
func TestInboundVless_XHTTP_PacketUp_H1_Encryption(t *testing.T) {
func TestInboundVless_XHTTP_H1(t *testing.T) {
testCases := []struct {
mode string
}{
{mode: "auto"},
{mode: "stream-one"},
{mode: "stream-up"},
{mode: "packet-up"},
}
for _, testCase := range testCases {
testCase := testCase
t.Run(testCase.mode, func(t *testing.T) {
getConfig := func() (inbound.VlessOption, outbound.VlessOption) {
inboundOptions := inbound.VlessOption{
Certificate: tlsCertificate,
PrivateKey: tlsPrivateKey,
XHTTPConfig: inbound.XHTTPConfig{
Path: "/vless-xhttp",
Host: "example.com",
Mode: testCase.mode,
},
}
outboundOptions := outbound.VlessOption{
TLS: true,
Fingerprint: tlsFingerprint,
Network: "xhttp",
ALPN: []string{"http/1.1"},
XHTTPOpts: outbound.XHTTPOptions{
Path: "/vless-xhttp",
Host: "example.com",
Mode: testCase.mode,
},
}
return inboundOptions, outboundOptions
}
testInboundVless_XHTTP(t, getConfig, testCase.mode)
})
}
}
func TestInboundVless_XHTTP_H1_Encryption(t *testing.T) {
privateKeyBase64, passwordBase64, _, err := encryption.GenX25519("")
if err != nil {
t.Fatal(err)
return
}
getConfig := func() (inbound.VlessOption, outbound.VlessOption) {
inboundOptions := inbound.VlessOption{
Decryption: "mlkem768x25519plus.native.600s." + privateKeyBase64,
XHTTPConfig: inbound.XHTTPConfig{
Path: "/vless-xhttp",
Host: "example.com",
Mode: "packet-up",
},
}
outboundOptions := outbound.VlessOption{
Encryption: "mlkem768x25519plus.native.0rtt." + passwordBase64,
Network: "xhttp",
ALPN: []string{"http/1.1"},
XHTTPOpts: outbound.XHTTPOptions{
Path: "/vless-xhttp",
Host: "example.com",
Mode: "packet-up",
},
}
return inboundOptions, outboundOptions
testCases := []struct {
mode string
}{
{mode: "auto"},
{mode: "stream-one"},
{mode: "stream-up"},
{mode: "packet-up"},
}
t.Run("default", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
testInboundVless(t, inboundOptions, outboundOptions)
t.Run("xtls-rprx-vision", func(t *testing.T) {
outboundOptions := outboundOptions
outboundOptions.Flow = "xtls-rprx-vision"
testInboundVless(t, inboundOptions, outboundOptions)
for _, testCase := range testCases {
testCase := testCase
t.Run(testCase.mode, func(t *testing.T) {
getConfig := func() (inbound.VlessOption, outbound.VlessOption) {
inboundOptions := inbound.VlessOption{
Decryption: "mlkem768x25519plus.native.600s." + privateKeyBase64,
XHTTPConfig: inbound.XHTTPConfig{
Path: "/vless-xhttp",
Host: "example.com",
Mode: testCase.mode,
},
}
outboundOptions := outbound.VlessOption{
Encryption: "mlkem768x25519plus.native.0rtt." + passwordBase64,
Network: "xhttp",
ALPN: []string{"http/1.1"},
XHTTPOpts: outbound.XHTTPOptions{
Path: "/vless-xhttp",
Host: "example.com",
Mode: testCase.mode,
},
}
return inboundOptions, outboundOptions
}
testInboundVless_XHTTP_Encryption(t, getConfig, testCase.mode)
})
})
t.Run("reuse", func(t *testing.T) {
inboundOptions, outboundOptions := getConfig()
testInboundVless(t, inboundOptions, outboundOptions)
t.Run("xtls-rprx-vision", func(t *testing.T) {
outboundOptions := outboundOptions
outboundOptions.Flow = "xtls-rprx-vision"
testInboundVless(t, inboundOptions, outboundOptions)
})
})
}
}
func withXHTTPReuse(out outbound.VlessOption) outbound.VlessOption {
+16 -9
View File
@@ -28,13 +28,13 @@ type httpServerConn struct {
mu sync.Mutex
w http.ResponseWriter
flusher http.Flusher
reader io.Reader
reader io.ReadCloser
closed bool
done chan struct{}
once sync.Once
}
func newHTTPServerConn(w http.ResponseWriter, r io.Reader) *httpServerConn {
func newHTTPServerConn(w http.ResponseWriter, r io.ReadCloser) *httpServerConn {
flusher, _ := w.(http.Flusher)
return &httpServerConn{
w: w,
@@ -70,7 +70,7 @@ func (c *httpServerConn) Close() error {
c.mu.Unlock()
close(c.done)
})
return nil
return c.reader.Close()
}
func (c *httpServerConn) Wait() <-chan struct{} {
@@ -305,6 +305,11 @@ func (h *requestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/event-stream")
}
w.WriteHeader(http.StatusOK)
rc := http.NewResponseController(w)
_ = rc.EnableFullDuplex() // http1 need to enable full duplex manually
_ = rc.Flush() // force flush the response header
referrer := r.Header.Get("Referer")
if referrer != "" && h.scStreamUpServerSecs.Max > 0 {
go func() {
@@ -440,9 +445,10 @@ func (h *requestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/event-stream")
}
w.WriteHeader(http.StatusOK)
if flusher, ok := w.(http.Flusher); ok {
flusher.Flush()
}
rc := http.NewResponseController(w)
_ = rc.EnableFullDuplex() // http1 need to enable full duplex manually
_ = rc.Flush() // force flush the response header
httpSC := newHTTPServerConn(w, r.Body)
conn := &Conn{
@@ -470,9 +476,10 @@ func (h *requestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Accel-Buffering", "no")
w.Header().Set("Cache-Control", "no-store")
w.WriteHeader(http.StatusOK)
if flusher, ok := w.(http.Flusher); ok {
flusher.Flush()
}
rc := http.NewResponseController(w)
_ = rc.EnableFullDuplex() // http1 need to enable full duplex manually
_ = rc.Flush() // force flush the response header
httpSC := newHTTPServerConn(w, r.Body)
conn := &Conn{