Skip to content

Commit

Permalink
Merge pull request #71 from guoye-zhang/windows
Browse files Browse the repository at this point in the history
Support building without CoreFoundation on Windows
  • Loading branch information
guoye-zhang authored Nov 13, 2024
2 parents 46dd772 + 74e4995 commit 4f278a0
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 3 deletions.
62 changes: 59 additions & 3 deletions Sources/HTTPTypesFoundation/HTTPRequest+URL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@
//
//===----------------------------------------------------------------------===//

import CoreFoundation
import Foundation
import HTTPTypes

#if canImport(CoreFoundation)
import CoreFoundation
#endif // canImport(CoreFoundation)

extension HTTPRequest {
/// The URL of the request synthesized from the scheme, authority, and path pseudo header
/// fields.
Expand Down Expand Up @@ -80,6 +83,7 @@ extension URL {
buffer.append(contentsOf: authority)
buffer.append(contentsOf: path)

#if canImport(CoreFoundation)
if let url = buffer.withUnsafeBytes({ buffer in
CFURLCreateAbsoluteURLWithBytes(
kCFAllocatorDefault,
Expand All @@ -94,9 +98,14 @@ extension URL {
} else {
return nil
}
#else // canImport(CoreFoundation)
// This initializer does not preserve WHATWG URLs
self.init(string: String(decoding: buffer, as: UTF8.self))
#endif // canImport(CoreFoundation)
}

fileprivate var httpRequestComponents: (scheme: [UInt8], authority: [UInt8]?, path: [UInt8]) {
#if canImport(CoreFoundation)
// CFURL parser based on byte ranges does not unnecessarily percent-encode WHATWG URL
let url = unsafeBitCast(self.absoluteURL as NSURL, to: CFURL.self)
let length = CFURLGetBytes(url, nil, 0)
Expand Down Expand Up @@ -133,11 +142,11 @@ extension URL {
let requestPathRange = unionRange(pathRange, queryRange)
if pathRange.length == 0 {
if requestPathRange.length == 0 {
path = Array("/".utf8)
path = [UInt8(ascii: "/")]
} else {
let pathBuffer = bufferSlice(requestPathRange)
path = [UInt8](unsafeUninitializedCapacity: pathBuffer.count + 1) { buffer, initializedCount in
buffer[0] = 0x2F
buffer[0] = UInt8(ascii: "/")
UnsafeMutableRawBufferPointer(UnsafeMutableBufferPointer(rebasing: buffer[1...])).copyMemory(
from: UnsafeRawBufferPointer(pathBuffer)
)
Expand All @@ -149,5 +158,52 @@ extension URL {
}
return (scheme, authority, path)
}
#else // canImport(CoreFoundation)
guard let components = URLComponents(url: self, resolvingAgainstBaseURL: true),
let urlString = components.string
else {
fatalError("Invalid URL")
}

guard let schemeRange = components.rangeOfScheme else {
fatalError("Schemeless URL is not supported")
}
let scheme = Array(urlString[schemeRange].utf8)

let authority: [UInt8]?
if let hostRange = components.rangeOfHost {
let authorityRange =
if let portRange = components.rangeOfPort {
hostRange.lowerBound..<portRange.upperBound
} else {
hostRange
}
authority = Array(urlString[authorityRange].utf8)
} else {
authority = nil
}

let pathRange = components.rangeOfPath
let queryRange = components.rangeOfQuery
let requestPathRange: Range<String.Index>?
if let lowerBound = pathRange?.lowerBound ?? queryRange?.lowerBound,
let upperBound = queryRange?.upperBound ?? pathRange?.upperBound
{
requestPathRange = lowerBound..<upperBound
} else {
requestPathRange = nil
}
let path: [UInt8]
if let pathRange, !pathRange.isEmpty, let requestPathRange {
path = Array(urlString[requestPathRange].utf8)
} else {
if let requestPathRange, !requestPathRange.isEmpty {
path = [UInt8(ascii: "/")] + Array(urlString[requestPathRange].utf8)
} else {
path = [UInt8(ascii: "/")]
}
}
return (scheme, authority, path)
#endif // canImport(CoreFoundation)
}
}
30 changes: 30 additions & 0 deletions Tests/HTTPTypesFoundationTests/HTTPTypesFoundationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,40 @@ final class HTTPTypesFoundationTests: XCTestCase {
let request5 = HTTPRequest(url: URL(string: "data:,Hello%2C%20World%21")!)
XCTAssertEqual(request5.scheme, "data")
XCTAssertNil(request5.authority)
#if canImport(CoreFoundation)
XCTAssertEqual(request5.path, "/")
#else // canImport(CoreFoundation)
XCTAssertEqual(request5.path, ",Hello%2C%20World%21")
#endif // canImport(CoreFoundation)
XCTAssertNil(request5.url)
}

func testRequestURLAuthorityParsing() {
let request1 = HTTPRequest(url: URL(string: "https://[::1]")!)
XCTAssertEqual(request1.scheme, "https")
XCTAssertEqual(request1.authority, "[::1]")
XCTAssertEqual(request1.path, "/")
XCTAssertEqual(request1.url?.absoluteString, "https://[::1]/")

let request2 = HTTPRequest(url: URL(string: "https://[::1]:443")!)
XCTAssertEqual(request2.scheme, "https")
XCTAssertEqual(request2.authority, "[::1]:443")
XCTAssertEqual(request2.path, "/")
XCTAssertEqual(request2.url?.absoluteString, "https://[::1]:443/")

let request3 = HTTPRequest(url: URL(string: "https://127.0.0.1")!)
XCTAssertEqual(request3.scheme, "https")
XCTAssertEqual(request3.authority, "127.0.0.1")
XCTAssertEqual(request3.path, "/")
XCTAssertEqual(request3.url?.absoluteString, "https://127.0.0.1/")

let request4 = HTTPRequest(url: URL(string: "https://127.0.0.1:443")!)
XCTAssertEqual(request4.scheme, "https")
XCTAssertEqual(request4.authority, "127.0.0.1:443")
XCTAssertEqual(request4.path, "/")
XCTAssertEqual(request4.url?.absoluteString, "https://127.0.0.1:443/")
}

func testRequestToFoundation() throws {
let request = HTTPRequest(
method: .get,
Expand Down

0 comments on commit 4f278a0

Please sign in to comment.