Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
667c560aa1 | ||
|
|
5f8edd593c | ||
|
|
4c5f5e9720 |
@@ -127,7 +127,15 @@ func ParseYamlProxies(raw string) []model.ProxyNode {
|
||||
|
||||
// ParseProxyLines splits raw text into lines, skips comments/blanks/section headers,
|
||||
// and parses each line as a URI or client config line.
|
||||
//
|
||||
// Literal "\n" / "\r\n" sequences (backslash + n, as produced by some frontends
|
||||
// stuffing multi-line content into a single string) are normalized to real
|
||||
// newlines before splitting so each link is parsed as its own line.
|
||||
func ParseProxyLines(raw string) []model.ProxyNode {
|
||||
if strings.Contains(raw, `\n`) {
|
||||
raw = strings.ReplaceAll(raw, `\r\n`, "\n")
|
||||
raw = strings.ReplaceAll(raw, `\n`, "\n")
|
||||
}
|
||||
lines := strings.Split(raw, "\n")
|
||||
var result []model.ProxyNode
|
||||
index := 0
|
||||
|
||||
@@ -290,3 +290,94 @@ func TestNodeJSONSerialization(t *testing.T) {
|
||||
t.Errorf("expected type ss after round-trip, got %v", back["type"])
|
||||
}
|
||||
}
|
||||
|
||||
func vlessURI(name string) string {
|
||||
return "vless://uuid-1234@1.2.3.4:443?encryption=none&security=tls&type=ws&host=example.com&path=%2Fpath&sni=example.com#" + name
|
||||
}
|
||||
|
||||
// TestParseProxyLinesLiteralNewline reproduces the production case: content
|
||||
// stored with literal "\n" (backslash + n) between links instead of real
|
||||
// newlines. It must normalize to real newlines and parse every link.
|
||||
func TestParseProxyLinesLiteralNewline(t *testing.T) {
|
||||
raw := strings.Join([]string{
|
||||
vlessURI("ali-seoul"),
|
||||
vlessURI("Ali-Tokyo"),
|
||||
vlessURI("racknerd-la"),
|
||||
}, `\n`)
|
||||
|
||||
nodes := ParseProxies(raw)
|
||||
if len(nodes) != 3 {
|
||||
t.Fatalf("expected 3 nodes from literal-\\n separated links, got %d", len(nodes))
|
||||
}
|
||||
wantNames := []string{"ali-seoul", "Ali-Tokyo", "racknerd-la"}
|
||||
for i, want := range wantNames {
|
||||
if got := nodes[i]["name"]; got != want {
|
||||
t.Errorf("node %d: expected name %q, got %q", i, want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseProxyLinesLiteralCRLF normalizes literal "\\r\\n" sequences too.
|
||||
func TestParseProxyLinesLiteralCRLF(t *testing.T) {
|
||||
raw := strings.Join([]string{
|
||||
vlessURI("Node1"),
|
||||
vlessURI("Node2"),
|
||||
}, `\r\n`)
|
||||
|
||||
nodes := ParseProxies(raw)
|
||||
if len(nodes) != 2 {
|
||||
t.Fatalf("expected 2 nodes from literal-\\r\\n separated links, got %d", len(nodes))
|
||||
}
|
||||
if nodes[0]["name"] != "Node1" || nodes[1]["name"] != "Node2" {
|
||||
t.Errorf("unexpected names: %v / %v", nodes[0]["name"], nodes[1]["name"])
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseProxyLinesRealNewlineUnaffected: real newline input still parses
|
||||
// the same after the normalization change.
|
||||
func TestParseProxyLinesRealNewlineUnaffected(t *testing.T) {
|
||||
raw := strings.Join([]string{
|
||||
vlessURI("Node1"),
|
||||
"",
|
||||
"# comment",
|
||||
vlessURI("Node2"),
|
||||
}, "\n")
|
||||
|
||||
nodes := ParseProxies(raw)
|
||||
if len(nodes) != 2 {
|
||||
t.Fatalf("expected 2 nodes, got %d", len(nodes))
|
||||
}
|
||||
if nodes[0]["name"] != "Node1" || nodes[1]["name"] != "Node2" {
|
||||
t.Errorf("unexpected names: %v / %v", nodes[0]["name"], nodes[1]["name"])
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseProxyLinesSingleLinkUnchanged: a single link with no separators
|
||||
// keeps its exact behavior (name untouched).
|
||||
func TestParseProxyLinesSingleLinkUnchanged(t *testing.T) {
|
||||
raw := vlessURI("solo")
|
||||
nodes := ParseProxies(raw)
|
||||
if len(nodes) != 1 {
|
||||
t.Fatalf("expected 1 node, got %d", len(nodes))
|
||||
}
|
||||
if nodes[0]["name"] != "solo" {
|
||||
t.Errorf("expected name solo, got %v", nodes[0]["name"])
|
||||
}
|
||||
}
|
||||
|
||||
// TestDecodeMaybeBase64LiteralNewlinePassthrough: structured literal-\n content
|
||||
// must pass through DecodeMaybeBase64 unchanged — normalization happens inside
|
||||
// ParseProxyLines (after format/base64 detection, before splitting) and must
|
||||
// not interfere with base64 detection.
|
||||
func TestDecodeMaybeBase64LiteralNewlinePassthrough(t *testing.T) {
|
||||
raw := strings.Join([]string{
|
||||
vlessURI("Node1"),
|
||||
vlessURI("Node2"),
|
||||
}, `\n`)
|
||||
if got := DecodeMaybeBase64(raw); got != raw {
|
||||
t.Error("structured literal-\\n content must pass through unchanged")
|
||||
}
|
||||
if nodes := ParseProxies(DecodeMaybeBase64(raw)); len(nodes) != 2 {
|
||||
t.Fatalf("expected 2 nodes through full pipeline, got %d", len(nodes))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user