diff --git a/core/net/url.odin b/core/net/url.odin index 7ad88bd1f..5257b757c 100644 --- a/core/net/url.odin +++ b/core/net/url.odin @@ -21,7 +21,7 @@ import "core:strconv" import "core:unicode/utf8" 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 i := strings.index(s, "://") @@ -30,6 +30,12 @@ split_url :: proc(url: string, allocator := context.allocator) -> (scheme, host, s = s[i+3:] } + i = strings.index_byte(s, '#') + if i != -1 { + fragment = s[i+1:] + s = s[:i] + } + i = strings.index(s, "?") if i != -1 { query_str := s[i+1:] @@ -62,7 +68,7 @@ split_url :: proc(url: string, allocator := context.allocator) -> (scheme, host, 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) 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 } + if fragment != "" { + if fragment[0] != '#' { + strings.write_byte(&b, '#') + } + strings.write_string(&b, strings.trim_space(fragment)) + } + return strings.to_string(b) } diff --git a/tests/core/net/test_core_net.odin b/tests/core/net/test_core_net.odin index 579298904..2a581c66b 100644 --- a/tests/core/net/test_core_net.odin +++ b/tests/core/net/test_core_net.odin @@ -473,6 +473,7 @@ client_sends_server_data :: proc(t: ^testing.T) { URL_Test :: struct { scheme, host, path: string, queries: map[string]string, + fragment: string, url: []string, } @@ -481,58 +482,78 @@ split_url_test :: proc(t: ^testing.T) { test_cases := []URL_Test{ { "http", "example.com", "/", - {}, + {}, "", {"http://example.com"}, }, { "https", "odin-lang.org", "/", - {}, + {}, "", {"https://odin-lang.org"}, }, { "https", "odin-lang.org", "/docs/", - {}, + {}, "", {"https://odin-lang.org/docs/"}, }, { "https", "odin-lang.org", "/docs/overview", - {}, + {}, "", {"https://odin-lang.org/docs/overview"}, }, { "http", "example.com", "/", - {"a" = "b"}, + {"a" = "b"}, "", {"http://example.com?a=b"}, }, { "http", "example.com", "/", - {"a" = ""}, + {"a" = ""}, "", {"http://example.com?a"}, }, { "http", "example.com", "/", - {"a" = "b", "c" = "d"}, + {"a" = "b", "c" = "d"}, "", {"http://example.com?a=b&c=d"}, }, { "http", "example.com", "/", - {"a" = "", "c" = "d"}, + {"a" = "", "c" = "d"}, "", {"http://example.com?a&c=d"}, }, { "http", "example.com", "/example", - {"a" = "", "b" = ""}, + {"a" = "", "b" = ""}, "", {"http://example.com/example?a&b"}, }, { "https", "example.com", "/callback", - {"redirect" = "https://other.com/login"}, + {"redirect" = "https://other.com/login"}, "", {"https://example.com/callback?redirect=https://other.com/login"}, }, + { + "http", "odin-lang.org", "/", + {}, "Hellope", + {"http://odin-lang.org#Hellope"} + }, + { + "https", "odin-lang.org", "/", + {"a" = ""}, "Hellope", + {"https://odin-lang.org?a#Hellope"} + }, + { + "http", "example.com", "/", + {"a" = "b"}, "Hellope", + {"http://example.com?a=b#Hellope"} + }, + { + "https", "example.com", "/example", + {}, "Hellope", + {"https://example.com/example#Hellope"} + }, } 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 { delete(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) 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{ { "http", "example.com", "/", - {}, + {}, "", {"http://example.com/"}, }, { "https", "odin-lang.org", "/", - {}, + {}, "", {"https://odin-lang.org/"}, }, { "https", "odin-lang.org", "/docs/", - {}, + {}, "", {"https://odin-lang.org/docs/"}, }, { "https", "odin-lang.org", "/docs/overview", - {}, + {}, "", {"https://odin-lang.org/docs/overview"}, }, { "http", "example.com", "/", - {"a" = "b"}, + {"a" = "b"}, "", {"http://example.com/?a=b"}, }, { "http", "example.com", "/", - {"a" = ""}, + {"a" = ""}, "", {"http://example.com/?a"}, }, { "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" = "", "c" = "d"}, + {"a" = "", "c" = "d"}, "", {"http://example.com/?a&c=d", "http://example.com/?c=d&a"}, }, { "http", "example.com", "/example", - {"a" = "", "b" = ""}, + {"a" = "", "b" = ""}, "", {"http://example.com/example?a&b", "http://example.com/example?b&a"}, }, + { + "http", "odin-lang.org", "", + {}, "Hellope", + {"http://odin-lang.org#Hellope"} + }, + { + "https", "odin-lang.org", "", + {"a" = ""}, "Hellope", + {"https://odin-lang.org?a#Hellope"} + }, + { + "http", "example.com", "", + {"a" = "b"}, "Hellope", + {"http://example.com?a=b#Hellope"} + }, + { + "https", "example.com", "/example", + {}, "Hellope", + {"https://example.com/example#Hellope"} + }, } 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 { delete(url) delete(test.queries)