diff --git a/Sources/FoundationEssentials/URL/URLParser.swift b/Sources/FoundationEssentials/URL/URLParser.swift index 7ea11362c..25a3a2e70 100644 --- a/Sources/FoundationEssentials/URL/URLParser.swift +++ b/Sources/FoundationEssentials/URL/URLParser.swift @@ -224,6 +224,8 @@ internal struct RFC3986Parser: URLParserProtocol { "phasset", "http+unix", "https+unix", + "ws+unix", + "wss+unix", ]) private static func looksLikeIPLiteral(_ host: some StringProtocol) -> Bool { diff --git a/Tests/FoundationEssentialsTests/URLTests.swift b/Tests/FoundationEssentialsTests/URLTests.swift index af91cee4b..7eeb7cac7 100644 --- a/Tests/FoundationEssentialsTests/URLTests.swift +++ b/Tests/FoundationEssentialsTests/URLTests.swift @@ -1137,4 +1137,38 @@ final class URLTests : XCTestCase { XCTAssertEqual(comp.host, "/path/to/socket") XCTAssertEqual(comp.path, "/info") } + + func testURLComponentsUnixDomainSocketOverWebSocketScheme() { + var comp = URLComponents() + comp.scheme = "ws+unix" + comp.host = "/path/to/socket" + comp.path = "/info" + XCTAssertEqual(comp.string, "ws+unix://%2Fpath%2Fto%2Fsocket/info") + + comp.scheme = "wss+unix" + XCTAssertEqual(comp.string, "wss+unix://%2Fpath%2Fto%2Fsocket/info") + + comp.encodedHost = "%2Fpath%2Fto%2Fsocket" + XCTAssertEqual(comp.string, "wss+unix://%2Fpath%2Fto%2Fsocket/info") + XCTAssertEqual(comp.encodedHost, "%2Fpath%2Fto%2Fsocket") + XCTAssertEqual(comp.host, "/path/to/socket") + XCTAssertEqual(comp.path, "/info") + + // "/path/to/socket" is not a valid host for schemes + // that IDNA-encode hosts instead of percent-encoding + comp.scheme = "ws" + XCTAssertNil(comp.string) + + comp.scheme = "wss" + XCTAssertNil(comp.string) + + comp.scheme = "wss+unix" + XCTAssertEqual(comp.string, "wss+unix://%2Fpath%2Fto%2Fsocket/info") + + // Check that we can parse a percent-encoded ws+unix URL string + comp = URLComponents(string: "ws+unix://%2Fpath%2Fto%2Fsocket/info")! + XCTAssertEqual(comp.encodedHost, "%2Fpath%2Fto%2Fsocket") + XCTAssertEqual(comp.host, "/path/to/socket") + XCTAssertEqual(comp.path, "/info") + } }