fix(sub): send the serverDescription tail only to clients that read it (#7108)

A Host Description is appended to every raw-subscription node name as a base64
`?serverDescription=` tail. Only Happ and INCY read it; v2rayN, v2rayNG,
OpenClash and the rest show it as part of the node name, and the only way out
was clearing the Description (#7105).

The raw subscription body now carries the tail only when the requesting
User-Agent is Happ or INCY, for the endpoint links and the Hysteria2 loop
alike. Copied links, QR codes and the panel's link export keep it, since the
server cannot tell which client they end up in.

Closes #7105
This commit is contained in:
Jay Hemnani
2026-11-13 04:54:13 +05:30
committed by GitHub
parent 7367ff75cd
commit 243b5c9da7
5 changed files with 37 additions and 2 deletions
+26
View File
@@ -803,6 +803,32 @@ func TestLegacyClashEndpointNormalizesShadowsocksCipher(t *testing.T) {
}
}
// #7105: only Happ and INCY read the tail; other clients show it in the node name.
func TestRawSubServerDescriptionByClient(t *testing.T) {
seedSubDB(t)
seedSubInbound(t, "s1", "vl", 4492, 1, `{"network":"tcp","security":"none","externalProxy":[{"forceTls":"same","dest":"a.example.com","port":443,"remark":"A","serverDescription":"Berlin"}]}`)
gin.SetMode(gin.TestMode)
cases := map[string]bool{"Happ/3.1": true, "INCY/1.0.0/ios": true, "v2rayN/7.10": false}
for userAgent, wantTail := range cases {
t.Run(userAgent, func(t *testing.T) {
router := gin.New()
NewSUBController(router.Group("/"), WithSUBEncryption(false))
req := httptest.NewRequest(http.MethodGet, "http://sub.example.com/sub/s1", nil)
req.Header.Set("User-Agent", userAgent)
resp := httptest.NewRecorder()
router.ServeHTTP(resp, req)
body := resp.Body.String()
if resp.Code != http.StatusOK || !strings.Contains(body, "vless") {
t.Fatalf("status=%d, body=%s", resp.Code, body)
}
if got := strings.Contains(body, "?serverDescription=QmVybGlu"); got != wantTail {
t.Fatalf("tail present = %v, want %v; body=%s", got, wantTail, body)
}
})
}
}
func TestIncyRoutingIsolatedFromHappHeaders(t *testing.T) {
seedSubDB(t)
seedSubInbound(t, "s1", "vl", 4492, 1, `{"network":"tcp","security":"none"}`)