Skip to content

Commit b215b9a

Browse files
committed
(142667792) URL.absoluteString crashes if baseURL starts with colon (swiftlang#1119)
1 parent 352b1f8 commit b215b9a

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
@@ -1113,7 +1113,9 @@ public struct URL: Equatable, Sendable, Hashable {
11131113
}
11141114

11151115
if let baseScheme = _baseParseInfo.scheme {
1116-
result.scheme = String(baseScheme)
1116+
// Scheme might be empty, which URL allows for compatibility,
1117+
// but URLComponents does not, so we force it internally.
1118+
result.forceScheme(String(baseScheme))
11171119
}
11181120

11191121
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
@@ -966,6 +966,21 @@ final class URLTests : XCTestCase {
966966
XCTAssertEqual(schemeOnly.absoluteString, "scheme:foo")
967967
}
968968

969+
func testURLEmptySchemeCompatibility() throws {
970+
var url = try XCTUnwrap(URL(string: ":memory:"))
971+
XCTAssertEqual(url.scheme, "")
972+
973+
let base = try XCTUnwrap(URL(string: "://home"))
974+
XCTAssertEqual(base.host(), "home")
975+
976+
url = try XCTUnwrap(URL(string: "/path", relativeTo: base))
977+
XCTAssertEqual(url.scheme, "")
978+
XCTAssertEqual(url.host(), "home")
979+
XCTAssertEqual(url.path, "/path")
980+
XCTAssertEqual(url.absoluteString, "://home/path")
981+
XCTAssertEqual(url.absoluteURL.scheme, "")
982+
}
983+
969984
func testURLComponentsPercentEncodedUnencodedProperties() throws {
970985
var comp = URLComponents()
971986

0 commit comments

Comments
 (0)