Skip to content

Commit b9852c2

Browse files
authored
(142667792) URL.absoluteString crashes if baseURL starts with colon (#1119)
1 parent f337a2c commit b9852c2

File tree

3 files changed

+28
-4
lines changed

3 files changed

+28
-4
lines changed

Sources/FoundationEssentials/URL/URL.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1123,7 +1123,9 @@ public struct URL: Equatable, Sendable, Hashable {
11231123
}
11241124

11251125
if let baseScheme = _baseParseInfo.scheme {
1126-
result.scheme = String(baseScheme)
1126+
// Scheme might be empty, which URL allows for compatibility,
1127+
// but URLComponents does not, so we force it internally.
1128+
result.forceScheme(String(baseScheme))
11271129
}
11281130

11291131
if hasAuthority {

Sources/FoundationEssentials/URL/URLComponents.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,12 @@ public struct URLComponents: Hashable, Equatable, Sendable {
142142
return nil
143143
}
144144

145-
mutating func setScheme(_ newValue: String?) throws {
145+
mutating func setScheme(_ newValue: String?, force: Bool = false) throws {
146146
reset(.scheme)
147-
guard Parser.validate(newValue, component: .scheme) else {
148-
throw InvalidComponentError.scheme
147+
if !force {
148+
guard Parser.validate(newValue, component: .scheme) else {
149+
throw InvalidComponentError.scheme
150+
}
149151
}
150152
_scheme = newValue
151153
if encodedHost != nil {
@@ -733,6 +735,11 @@ public struct URLComponents: Hashable, Equatable, Sendable {
733735
}
734736
}
735737

738+
/// Used by `URL` to allow empty scheme for compatibility.
739+
internal mutating func forceScheme(_ scheme: String) {
740+
try? components.setScheme(scheme, force: true)
741+
}
742+
736743
#if FOUNDATION_FRAMEWORK
737744
/// Throwing function used by `_NSSwiftURLComponents` to generate an exception for ObjC callers
738745
internal mutating func setScheme(_ newValue: String?) throws {

Tests/FoundationEssentialsTests/URLTests.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,21 @@ final class URLTests : XCTestCase {
946946
XCTAssertEqual(schemeOnly.absoluteString, "scheme:foo")
947947
}
948948

949+
func testURLEmptySchemeCompatibility() throws {
950+
var url = try XCTUnwrap(URL(string: ":memory:"))
951+
XCTAssertEqual(url.scheme, "")
952+
953+
let base = try XCTUnwrap(URL(string: "://home"))
954+
XCTAssertEqual(base.host(), "home")
955+
956+
url = try XCTUnwrap(URL(string: "/path", relativeTo: base))
957+
XCTAssertEqual(url.scheme, "")
958+
XCTAssertEqual(url.host(), "home")
959+
XCTAssertEqual(url.path, "/path")
960+
XCTAssertEqual(url.absoluteString, "://home/path")
961+
XCTAssertEqual(url.absoluteURL.scheme, "")
962+
}
963+
949964
func testURLComponentsPercentEncodedUnencodedProperties() throws {
950965
var comp = URLComponents()
951966

0 commit comments

Comments
 (0)