Skip to content

Commit d3ff16d

Browse files
committed
Fix parsing of URLs with query or fragment delimiters but no path component
1 parent 0ced6b9 commit d3ff16d

File tree

3 files changed

+43
-0
lines changed

3 files changed

+43
-0
lines changed

ChangeLog

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
2025-10-22 Frederik Seiffert <frederik@algoriddim.com>
2+
3+
* Source/NSURL.m: Fix parsing of URLs with query or fragment delimiters
4+
but no path component.
5+
16
2025-10-01 Richard Frith-Macdonald <rfm@gnu.org>
27

38
* Source/Additions/Unicode.m:

Source/NSURL.m

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -995,8 +995,18 @@ - (id) initWithString: (NSString*)aUrlString
995995
/*
996996
* Set 'end' to point to the start of the path, or just past
997997
* the 'authority' if there is no path.
998+
* Check for delimiters in order: '/' (path), '?' (query), '#' (fragment).
999+
* This properly delimits the authority section (RFC 3986).
9981000
*/
9991001
end = strchr(start, '/');
1002+
if (end == 0)
1003+
{
1004+
end = strchr(start, '?');
1005+
}
1006+
if (end == 0)
1007+
{
1008+
end = strchr(start, '#');
1009+
}
10001010
if (end == 0)
10011011
{
10021012
buf->hasNoPath = YES;

Tests/base/NSURL/basic.m

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,34 @@ int main()
392392
PASS_EQUAL([rel absoluteString], @"data:,$2A", "relative data URL works");
393393
PASS_EQUAL([rel baseURL], nil, "Base URL of relative data URL is nil");
394394

395+
/* Test URLs with query but no path */
396+
url = [NSURL URLWithString: @"https://example.com?key=value"];
397+
PASS(url != nil, "URL with query but no path should be valid");
398+
PASS_EQUAL([url scheme], @"https", "scheme of https://example.com?key=value is https");
399+
PASS_EQUAL([url host], @"example.com", "host of https://example.com?key=value is example.com");
400+
PASS_EQUAL([url path], @"", "path of https://example.com?key=value is empty");
401+
PASS_EQUAL([url query], @"key=value", "query of https://example.com?key=value is key=value");
402+
PASS_EQUAL([url absoluteString], @"https://example.com?key=value", "absoluteString works for URL with query but no path");
403+
404+
/* Test URLs with fragment but no path */
405+
url = [NSURL URLWithString: @"https://example.com#section"];
406+
PASS(url != nil, "URL with fragment but no path should be valid");
407+
PASS_EQUAL([url scheme], @"https", "scheme of https://example.com#section is https");
408+
PASS_EQUAL([url host], @"example.com", "host of https://example.com#section is example.com");
409+
PASS_EQUAL([url path], @"", "path of https://example.com#section is empty");
410+
PASS_EQUAL([url fragment], @"section", "fragment of https://example.com#section is section");
411+
PASS_EQUAL([url absoluteString], @"https://example.com#section", "absoluteString works for URL with fragment but no path");
412+
413+
/* Test URLs with query and fragment but no path */
414+
url = [NSURL URLWithString: @"https://example.com?key=value#section"];
415+
PASS(url != nil, "URL with query and fragment but no path should be valid");
416+
PASS_EQUAL([url scheme], @"https", "scheme of https://example.com?key=value#section is https");
417+
PASS_EQUAL([url host], @"example.com", "host of https://example.com?key=value#section is example.com");
418+
PASS_EQUAL([url path], @"", "path of https://example.com?key=value#section is empty");
419+
PASS_EQUAL([url query], @"key=value", "query of https://example.com?key=value#section is key=value");
420+
PASS_EQUAL([url fragment], @"section", "fragment of https://example.com?key=value#section is section");
421+
PASS_EQUAL([url absoluteString], @"https://example.com?key=value#section", "absoluteString works for URL with query and fragment but no path");
422+
395423
///NSURLQueryItem
396424

397425
//OSX behavior is to return query item with an empty string name

0 commit comments

Comments
 (0)