From 8bf20742f6a80fb6bd7e98fc710de43c1de1ca1d Mon Sep 17 00:00:00 2001 From: ivolfram Date: Wed, 10 Jun 2026 20:41:55 +0300 Subject: [PATCH] XHTTP: only force trailing path slash when session/seq use path placement --- transport/internet/splithttp/config.go | 7 +- transport/internet/splithttp/config_test.go | 73 +++++++++++++++++++-- 2 files changed, 72 insertions(+), 8 deletions(-) diff --git a/transport/internet/splithttp/config.go b/transport/internet/splithttp/config.go index 9acf5e4ff349..c2441386c7d9 100644 --- a/transport/internet/splithttp/config.go +++ b/transport/internet/splithttp/config.go @@ -24,8 +24,11 @@ func (c *Config) GetNormalizedPath() string { path = "/" + path } - if path[len(path)-1] != '/' { - path = path + "/" + if c.GetNormalizedSessionPlacement() == PlacementPath || + c.GetNormalizedSeqPlacement() == PlacementPath { + if path[len(path)-1] != '/' { + path = path + "/" + } } return path diff --git a/transport/internet/splithttp/config_test.go b/transport/internet/splithttp/config_test.go index 39c3fd95aba7..2cb1e5e9daac 100644 --- a/transport/internet/splithttp/config_test.go +++ b/transport/internet/splithttp/config_test.go @@ -3,16 +3,77 @@ package splithttp_test import ( "testing" + "github.com/stretchr/testify/assert" . "github.com/xtls/xray-core/transport/internet/splithttp" ) func Test_GetNormalizedPath(t *testing.T) { - c := Config{ - Path: "/?world", + tests := []struct { + TestName string + Path string + SessionIDPlacement string + SeqPlacement string + Expected string + }{ + { + TestName: "default placement keeps trailing slash", + Path: "/sh", + Expected: "/sh/", + }, + { + TestName: "query string is stripped", + Path: "/?world", + Expected: "/", + }, + { + TestName: "both off path drops trailing slash", + Path: "/stream", + SessionIDPlacement: "query", + SeqPlacement: "query", + Expected: "/stream", + }, + { + TestName: "both off path keeps file-like path", + Path: "/stream/filename.extension", + SessionIDPlacement: "query", + SeqPlacement: "header", + Expected: "/stream/filename.extension", + }, + { + TestName: "seq in path keeps trailing slash", + Path: "/stream", + SessionIDPlacement: "query", + Expected: "/stream/", + }, + { + TestName: "session in path keeps trailing slash", + Path: "/stream", + SeqPlacement: "cookie", + Expected: "/stream/", + }, + { + TestName: "existing trailing slash preserved", + Path: "/stream/", + SessionIDPlacement: "query", + SeqPlacement: "query", + Expected: "/stream/", + }, + { + TestName: "root unchanged", + Path: "/", + SessionIDPlacement: "query", + SeqPlacement: "query", + Expected: "/", + }, } - - path := c.GetNormalizedPath() - if path != "/" { - t.Error("Unexpected: ", path) + for _, test := range tests { + t.Run(test.TestName, func(t *testing.T) { + c := Config{ + Path: test.Path, + SessionIDPlacement: test.SessionIDPlacement, + SeqPlacement: test.SeqPlacement, + } + assert.Equal(t, test.Expected, c.GetNormalizedPath()) + }) } }