From fbb8bcd2c8f470f1af56186aa23c49f2c2b6399f Mon Sep 17 00:00:00 2001 From: Arpad Ryszka Date: Wed, 24 Jun 2026 03:28:42 +0200 Subject: [PATCH] fix url syntax --- doc/example/url.treerack | 14 ++--- url_test.go | 109 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 7 deletions(-) diff --git a/doc/example/url.treerack b/doc/example/url.treerack index 1c0fb44..9ae0aa3 100644 --- a/doc/example/url.treerack +++ b/doc/example/url.treerack @@ -15,7 +15,7 @@ percent-encoded:alias:failpass = "%" hex{2}; path-char:alias:failpass = unreserved | percent-encoded | subdelimiter | ":" | "@"; // scheme: -scheme = alpha (alpha | digit | [+-.])*; +scheme = alpha (alpha | digit | [+\-.])*; // userinfo: userinfo = (unreserved | percent-encoded | subdelimiter | ":")*; @@ -30,12 +30,12 @@ ls32:alias:failpass = h16 ":" h16 | ipv4; ipv6:failpass = (h16 ":"){6} ls32 | "::" (h16 ":"){5} ls32 | h16? "::" (h16 ":"){4} ls32 - | ((h16 ":")? h16)? "::" (h16 ":"){3} ls32 - | ((h16 ":"){,2} h16)? "::" (h16 ":"){2} ls32 - | ((h16 ":"){,3} h16)? "::" h16 ":" ls32 - | ((h16 ":"){,4} h16)? "::" ls32 - | ((h16 ":"){,5} h16)? "::" h16 - | ((h16 ":"){,6} h16)? "::"; + | (h16 (":" h16)?)? "::" (h16 ":"){3} ls32 + | (h16 (":" h16){,2})? "::" (h16 ":"){2} ls32 + | (h16 (":" h16){,3})? "::" h16 ":" ls32 + | (h16 (":" h16){,4})? "::" ls32 + | (h16 (":" h16){,5})? "::" h16 + | (h16 (":" h16){,6})? "::"; zone-id:alias:failpass = (unreserved | percent-encoded)+; ipv6-zone:failpass = ipv6 "%25" zone-id; // RFC6874 diff --git a/url_test.go b/url_test.go index 8aea823..a3f02ce 100644 --- a/url_test.go +++ b/url_test.go @@ -266,6 +266,79 @@ func TestURL(t *testing.T) { }}, }, { + title: "scheme and host ipv4 as domain name because of numeric range", + input: "http://256.0.0.1", + checks: []urlCheck{{ + stdQuery: func(u *url.URL) string { return u.Scheme }, + treerackQuery: queryTreerack("absolute-url", "scheme"), + expect: "http", + }, { + stdQuery: func(u *url.URL) string { return u.Host }, + treerackQuery: queryTreerack("absolute-url", "hostname", "domain-name"), + expect: "256.0.0.1", + }}, + }, { + + title: "scheme and host ipv4 as domain name because of leading zero", + input: "http://128.0.0.01", + checks: []urlCheck{{ + stdQuery: func(u *url.URL) string { return u.Scheme }, + treerackQuery: queryTreerack("absolute-url", "scheme"), + expect: "http", + }, { + stdQuery: func(u *url.URL) string { return u.Host }, + treerackQuery: queryTreerack("absolute-url", "hostname", "domain-name"), + expect: "128.0.0.01", + }}, + }, { + + title: "scheme and host ipv4", + input: "http://128.0.0.1", + checks: []urlCheck{{ + stdQuery: func(u *url.URL) string { return u.Scheme }, + treerackQuery: queryTreerack("absolute-url", "scheme"), + expect: "http", + }, { + stdQuery: func(u *url.URL) string { return u.Host }, + treerackQuery: queryTreerack("absolute-url", "hostname", "ipv4"), + expect: "128.0.0.1", + }}, + }, { + + title: "scheme and host ipv6", + input: "http://[fe80::1]", + checks: []urlCheck{{ + stdQuery: func(u *url.URL) string { return u.Scheme }, + treerackQuery: queryTreerack("absolute-url", "scheme"), + expect: "http", + }, { + stdQuery: func(u *url.URL) string { return u.Host }, + treerackQuery: fakeTRCheck("[fe80::1]"), + expect: "[fe80::1]", + }, { + stdQuery: fakeSTDCheck("fe80::1"), + treerackQuery: queryTreerack("absolute-url", "hostname", "ipv6"), + expect: "fe80::1", + }}, + }, { + + title: "scheme and host ipv6 with zone", + input: "http://[fe80::1%25eth0]", + checks: []urlCheck{{ + stdQuery: func(u *url.URL) string { return u.Scheme }, + treerackQuery: queryTreerack("absolute-url", "scheme"), + expect: "http", + }, { + stdQuery: func(u *url.URL) string { return u.Host }, + treerackQuery: fakeTRCheck("[fe80::1%eth0]"), + expect: "[fe80::1%eth0]", + }, { + stdQuery: fakeSTDCheck("fe80::1%25eth0"), + treerackQuery: queryTreerack("absolute-url", "hostname", "ipv6-zone"), + expect: "fe80::1%25eth0", + }}, + }, { + title: "scheme and host with port", input: "http://foo.bar:8080", checks: []urlCheck{{ @@ -1480,3 +1553,39 @@ func TestURL(t *testing.T) { }) } } + +func TestURLErrors(t *testing.T) { + for _, test := range []struct{ title, input string }{{ + + title: "scheme cannot start on a number", + input: "123http://foo.bar/foo/bar", + }, { + + title: "scheme characters", + input: "http,unix://foo.bar/foo/bar", + }, { + + title: "ipv4 in brackets", + input: "http://[128.0.0.1]", + }, { + + title: "ipv6 without closing bracket", + input: "http://[fe80::1%25eth0", + }, { + + title: "invalid port", + input: "http://foo.bar:99x99", + }, { + + title: "invalid path", + input: "http://foo.bar/foo%xbar", + }} { + t.Run(test.title, func(t *testing.T) { + p := urlParser() + if a, err := p(test.input); err == nil { + printURLTree(t, a) + t.Fatal("failed to fail", test.input) + } + }) + } +}