Browse Source

Merge pull request #3418 from blob1807/add-url-fragment-to-split-join-url

core:net Add support for URL fragments
Jeroen van Rijn 1 year ago
parent
commit
f086b4710a
2 changed files with 80 additions and 23 deletions
  1. 15 2
      core/net/url.odin
  2. 65 21
      tests/core/net/test_core_net.odin

+ 15 - 2
core/net/url.odin

@@ -21,7 +21,7 @@ import "core:strconv"
 import "core:unicode/utf8"
 import "core:unicode/utf8"
 import "core:encoding/hex"
 import "core:encoding/hex"
 
 
-split_url :: proc(url: string, allocator := context.allocator) -> (scheme, host, path: string, queries: map[string]string) {
+split_url :: proc(url: string, allocator := context.allocator) -> (scheme, host, path: string, queries: map[string]string, fragment: string) {
 	s := url
 	s := url
 
 
 	i := strings.index(s, "://")
 	i := strings.index(s, "://")
@@ -30,6 +30,12 @@ split_url :: proc(url: string, allocator := context.allocator) -> (scheme, host,
 		s = s[i+3:]
 		s = s[i+3:]
 	}
 	}
 
 
+	i = strings.index(s, "#")
+	if i != -1 {
+		fragment = s[i+1:]
+		s = s[:i]
+	}
+
 	i = strings.index(s, "?")
 	i = strings.index(s, "?")
 	if i != -1 {
 	if i != -1 {
 		query_str := s[i+1:]
 		query_str := s[i+1:]
@@ -62,7 +68,7 @@ split_url :: proc(url: string, allocator := context.allocator) -> (scheme, host,
 	return
 	return
 }
 }
 
 
-join_url :: proc(scheme, host, path: string, queries: map[string]string, allocator := context.allocator) -> string {
+join_url :: proc(scheme, host, path: string, queries: map[string]string, fragment: string, allocator := context.allocator) -> string {
 	b := strings.builder_make(allocator)
 	b := strings.builder_make(allocator)
 	strings.builder_grow(&b, len(scheme) + 3 + len(host) + 1 + len(path))
 	strings.builder_grow(&b, len(scheme) + 3 + len(host) + 1 + len(path))
 
 
@@ -95,6 +101,13 @@ join_url :: proc(scheme, host, path: string, queries: map[string]string, allocat
 		i += 1
 		i += 1
 	}
 	}
 
 
+	if fragment != "" {
+		if fragment[0] != '#' {
+			strings.write_string(&b, "#")
+		}
+		strings.write_string(&b, strings.trim_space(fragment))
+	}
+
 	return strings.to_string(b)
 	return strings.to_string(b)
 }
 }
 
 

+ 65 - 21
tests/core/net/test_core_net.odin

@@ -473,6 +473,7 @@ client_sends_server_data :: proc(t: ^testing.T) {
 URL_Test :: struct {
 URL_Test :: struct {
 	scheme, host, path: string,
 	scheme, host, path: string,
 	queries: map[string]string,
 	queries: map[string]string,
+	fragment: string,
 	url: []string,
 	url: []string,
 }
 }
 
 
@@ -481,58 +482,78 @@ split_url_test :: proc(t: ^testing.T) {
 	test_cases := []URL_Test{
 	test_cases := []URL_Test{
 		{
 		{
 			"http", "example.com", "/",
 			"http", "example.com", "/",
-			{},
+			{}, "",
 			{"http://example.com"},
 			{"http://example.com"},
 		},
 		},
 		{
 		{
 			"https", "odin-lang.org", "/",
 			"https", "odin-lang.org", "/",
-			{},
+			{}, "",
 			{"https://odin-lang.org"},
 			{"https://odin-lang.org"},
 		},
 		},
 		{
 		{
 			"https", "odin-lang.org", "/docs/",
 			"https", "odin-lang.org", "/docs/",
-			{},
+			{}, "",
 			{"https://odin-lang.org/docs/"},
 			{"https://odin-lang.org/docs/"},
 		},
 		},
 		{
 		{
 			"https", "odin-lang.org", "/docs/overview",
 			"https", "odin-lang.org", "/docs/overview",
-			{},
+			{}, "",
 			{"https://odin-lang.org/docs/overview"},
 			{"https://odin-lang.org/docs/overview"},
 		},
 		},
 		{
 		{
 			"http", "example.com", "/",
 			"http", "example.com", "/",
-			{"a" = "b"},
+			{"a" = "b"}, "",
 			{"http://example.com?a=b"},
 			{"http://example.com?a=b"},
 		},
 		},
 		{
 		{
 			"http", "example.com", "/",
 			"http", "example.com", "/",
-			{"a" = ""},
+			{"a" = ""}, "",
 			{"http://example.com?a"},
 			{"http://example.com?a"},
 		},
 		},
 		{
 		{
 			"http", "example.com", "/",
 			"http", "example.com", "/",
-			{"a" = "b", "c" = "d"},
+			{"a" = "b", "c" = "d"}, "",
 			{"http://example.com?a=b&c=d"},
 			{"http://example.com?a=b&c=d"},
 		},
 		},
 		{
 		{
 			"http", "example.com", "/",
 			"http", "example.com", "/",
-			{"a" = "", "c" = "d"},
+			{"a" = "", "c" = "d"}, "",
 			{"http://example.com?a&c=d"},
 			{"http://example.com?a&c=d"},
 		},
 		},
 		{
 		{
 			"http", "example.com", "/example",
 			"http", "example.com", "/example",
-			{"a" = "", "b" = ""},
+			{"a" = "", "b" = ""}, "",
 			{"http://example.com/example?a&b"},
 			{"http://example.com/example?a&b"},
 		},
 		},
 		{
 		{
 			"https", "example.com", "/callback",
 			"https", "example.com", "/callback",
-			{"redirect" = "https://other.com/login"},
+			{"redirect" = "https://other.com/login"}, "",
 			{"https://example.com/callback?redirect=https://other.com/login"},
 			{"https://example.com/callback?redirect=https://other.com/login"},
 		},
 		},
+		{
+			"http", "example.com", "/",
+			{}, "Hellope",
+			{"http://example.com#Hellope"},
+		},
+		{
+			"https", "odin-lang.org", "/",
+			{"a" = ""}, "Hellope",
+			{"https://odin-lang.org?a#Hellope"},
+		},
+		{
+			"http", "example.com", "/",
+			{"a" = "b"}, "BeesKnees",
+			{"http://example.com?a=b#BeesKnees"},
+		},
+		{
+			"https", "odin-lang.org", "/docs/overview/",
+			{}, "hellope",
+			{"https://odin-lang.org/docs/overview/#hellope"},
+		},
 	}
 	}
 
 
 	for test in test_cases {
 	for test in test_cases {
-		scheme, host, path, queries := net.split_url(test.url[0])
+		scheme, host, path, queries, fragment := net.split_url(test.url[0])
 		defer {
 		defer {
 			delete(queries)
 			delete(queries)
 			delete(test.queries)
 			delete(test.queries)
@@ -551,6 +572,9 @@ split_url_test :: proc(t: ^testing.T) {
 			msg = fmt.tprintf("Expected `net.split_url` to return %s, got %s", expected, v)
 			msg = fmt.tprintf("Expected `net.split_url` to return %s, got %s", expected, v)
 			expect(t, v == expected, msg)
 			expect(t, v == expected, msg)
 		}
 		}
+		msg = fmt.tprintf("Expected `net.split_url` to return %s, got %s", test.fragment, fragment)
+		expect(t, fragment == test.fragment, msg)
+
 	}
 	}
 }
 }
 
 
@@ -560,53 +584,73 @@ join_url_test :: proc(t: ^testing.T) {
 	test_cases := []URL_Test{
 	test_cases := []URL_Test{
 		{
 		{
 			"http", "example.com", "/",
 			"http", "example.com", "/",
-			{},
+			{}, "",
 			{"http://example.com/"},
 			{"http://example.com/"},
 		},
 		},
 		{
 		{
 			"https", "odin-lang.org", "/",
 			"https", "odin-lang.org", "/",
-			{},
+			{}, "",
 			{"https://odin-lang.org/"},
 			{"https://odin-lang.org/"},
 		},
 		},
 		{
 		{
 			"https", "odin-lang.org", "/docs/",
 			"https", "odin-lang.org", "/docs/",
-			{},
+			{}, "",
 			{"https://odin-lang.org/docs/"},
 			{"https://odin-lang.org/docs/"},
 		},
 		},
 		{
 		{
 			"https", "odin-lang.org", "/docs/overview",
 			"https", "odin-lang.org", "/docs/overview",
-			{},
+			{}, "",
 			{"https://odin-lang.org/docs/overview"},
 			{"https://odin-lang.org/docs/overview"},
 		},
 		},
 		{
 		{
 			"http", "example.com", "/",
 			"http", "example.com", "/",
-			{"a" = "b"},
+			{"a" = "b"}, "",
 			{"http://example.com/?a=b"},
 			{"http://example.com/?a=b"},
 		},
 		},
 		{
 		{
 			"http", "example.com", "/",
 			"http", "example.com", "/",
-			{"a" = ""},
+			{"a" = ""}, "",
 			{"http://example.com/?a"},
 			{"http://example.com/?a"},
 		},
 		},
 		{
 		{
 			"http", "example.com", "/",
 			"http", "example.com", "/",
-			{"a" = "b", "c" = "d"},
+			{"a" = "b", "c" = "d"}, "",
 			{"http://example.com/?a=b&c=d", "http://example.com/?c=d&a=b"},
 			{"http://example.com/?a=b&c=d", "http://example.com/?c=d&a=b"},
 		},
 		},
 		{
 		{
 			"http", "example.com", "/",
 			"http", "example.com", "/",
-			{"a" = "", "c" = "d"},
+			{"a" = "", "c" = "d"}, "",
 			{"http://example.com/?a&c=d", "http://example.com/?c=d&a"},
 			{"http://example.com/?a&c=d", "http://example.com/?c=d&a"},
 		},
 		},
 		{
 		{
 			"http", "example.com", "/example",
 			"http", "example.com", "/example",
-			{"a" = "", "b" = ""},
+			{"a" = "", "b" = ""}, "",
 			{"http://example.com/example?a&b", "http://example.com/example?b&a"},
 			{"http://example.com/example?a&b", "http://example.com/example?b&a"},
 		},
 		},
+		{
+			"http", "example.com", "/",
+			{}, "Hellope",
+			{"http://example.com/#Hellope"},
+		},
+		{
+			"https", "odin-lang.org", "/",
+			{"a" = ""}, "Hellope",
+			{"https://odin-lang.org/?a#Hellope"},
+		},
+		{
+			"http", "example.com", "/",
+			{"a" = "b"}, "BeesKnees",
+			{"http://example.com/?a=b#BeesKnees"},
+		},
+		{
+			"https", "odin-lang.org", "/docs/overview/",
+			{}, "hellope",
+			{"https://odin-lang.org/docs/overview/#hellope"},
+		},
 	}
 	}
 
 
 	for test in test_cases {
 	for test in test_cases {
-		url := net.join_url(test.scheme, test.host, test.path, test.queries)
+		url := net.join_url(test.scheme, test.host, test.path, test.queries, test.fragment)
 		defer {
 		defer {
 			delete(url)
 			delete(url)
 			delete(test.queries)
 			delete(test.queries)