From dc1b10ed88c7f60c4056ccb064622b674affc3f3 Mon Sep 17 00:00:00 2001 From: Greg Cotten Date: Fri, 21 Feb 2025 15:36:22 -0800 Subject: [PATCH 1/8] add request/response history to `HTTPClient.Response`, and worry about "Sendability" --- Sources/AsyncHTTPClient/HTTPHandler.swift | 38 ++++++++++++- .../HTTPClientTests.swift | 56 +++++++++++++++++++ 2 files changed, 91 insertions(+), 3 deletions(-) diff --git a/Sources/AsyncHTTPClient/HTTPHandler.swift b/Sources/AsyncHTTPClient/HTTPHandler.swift index d39c600b5..babf87ca9 100644 --- a/Sources/AsyncHTTPClient/HTTPHandler.swift +++ b/Sources/AsyncHTTPClient/HTTPHandler.swift @@ -377,6 +377,13 @@ extension HTTPClient { public var headers: HTTPHeaders /// Response body. public var body: ByteBuffer? + /// The history of all requests and responses in redirect order. + public var history: [RequestResponse] + + /// The target URL (after redirects) of the response. + public var url: URL? { + self.history.last?.request.url + } /// Create HTTP `Response`. /// @@ -392,6 +399,7 @@ extension HTTPClient { self.version = HTTPVersion(major: 1, minor: 1) self.headers = headers self.body = body + self.history = [] } /// Create HTTP `Response`. @@ -402,18 +410,21 @@ extension HTTPClient { /// - version: Response HTTP version. /// - headers: Reponse HTTP headers. /// - body: Response body. + /// - history: History of all requests and responses in redirect order. public init( host: String, status: HTTPResponseStatus, version: HTTPVersion, headers: HTTPHeaders, - body: ByteBuffer? + body: ByteBuffer?, + history: [RequestResponse] ) { self.host = host self.status = status self.version = version self.headers = headers self.body = body + self.history = history } } @@ -457,6 +468,16 @@ extension HTTPClient { } } } + + public struct RequestResponse { + public var request: Request + public var responseHead: HTTPResponseHead + + public init(request: Request, responseHead: HTTPResponseHead) { + self.request = request + self.responseHead = responseHead + } + } } /// The default ``HTTPClientResponseDelegate``. @@ -485,6 +506,7 @@ public final class ResponseAccumulator: HTTPClientResponseDelegate { } } + var history = [HTTPClient.RequestResponse]() var state = State.idle let requestMethod: HTTPMethod let requestHost: String @@ -521,6 +543,14 @@ public final class ResponseAccumulator: HTTPClientResponseDelegate { self.maxBodySize = maxBodySize } + public func didVisitURL( + task: HTTPClient.Task, + _ request: HTTPClient.Request, + _ head: HTTPResponseHead + ) { + self.history.append(.init(request: request, responseHead: head)) + } + public func didReceiveHead(task: HTTPClient.Task, _ head: HTTPResponseHead) -> EventLoopFuture { switch self.state { case .idle: @@ -596,7 +626,8 @@ public final class ResponseAccumulator: HTTPClientResponseDelegate { status: head.status, version: head.version, headers: head.headers, - body: nil + body: nil, + history: self.history ) case .body(let head, let body): return Response( @@ -604,7 +635,8 @@ public final class ResponseAccumulator: HTTPClientResponseDelegate { status: head.status, version: head.version, headers: head.headers, - body: body + body: body, + history: self.history ) case .end: preconditionFailure("request already processed") diff --git a/Tests/AsyncHTTPClientTests/HTTPClientTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientTests.swift index 0467e9fa9..059e4805c 100644 --- a/Tests/AsyncHTTPClientTests/HTTPClientTests.swift +++ b/Tests/AsyncHTTPClientTests/HTTPClientTests.swift @@ -469,6 +469,14 @@ final class HTTPClientTests: XCTestCaseHTTPClientTestsBaseClass { var response = try localClient.get(url: self.defaultHTTPBinURLPrefix + "redirect/302").wait() XCTAssertEqual(response.status, .ok) + XCTAssertEqual(response.url?.absoluteString, self.defaultHTTPBinURLPrefix + "ok") + XCTAssertEqual( + response.history.map(\.request.url.absoluteString), + [ + self.defaultHTTPBinURLPrefix + "redirect/302", + self.defaultHTTPBinURLPrefix + "ok", + ] + ) response = try localClient.get(url: self.defaultHTTPBinURLPrefix + "redirect/https?port=\(httpsBin.port)") .wait() @@ -501,6 +509,8 @@ final class HTTPClientTests: XCTestCaseHTTPClientTestsBaseClass { var response = try localClient.execute(request: request).wait() XCTAssertEqual(response.status, .found) XCTAssertEqual(response.headers.first(name: "Location"), targetURL) + XCTAssertEqual(response.url, request.url) + XCTAssertEqual(response.history.map(\.request.url), [request.url]) request = try Request( url: "https://localhost:\(httpsBin.port)/redirect/target", @@ -512,6 +522,8 @@ final class HTTPClientTests: XCTestCaseHTTPClientTestsBaseClass { response = try localClient.execute(request: request).wait() XCTAssertEqual(response.status, .found) XCTAssertEqual(response.headers.first(name: "Location"), targetURL) + XCTAssertEqual(response.url, request.url) + XCTAssertEqual(response.history.map(\.request.url), [request.url]) // From HTTP or HTTPS to HTTPS+UNIX should also fail to redirect targetURL = @@ -526,6 +538,8 @@ final class HTTPClientTests: XCTestCaseHTTPClientTestsBaseClass { response = try localClient.execute(request: request).wait() XCTAssertEqual(response.status, .found) XCTAssertEqual(response.headers.first(name: "Location"), targetURL) + XCTAssertEqual(response.url, request.url) + XCTAssertEqual(response.history.map(\.request.url), [request.url]) request = try Request( url: "https://localhost:\(httpsBin.port)/redirect/target", @@ -537,6 +551,8 @@ final class HTTPClientTests: XCTestCaseHTTPClientTestsBaseClass { response = try localClient.execute(request: request).wait() XCTAssertEqual(response.status, .found) XCTAssertEqual(response.headers.first(name: "Location"), targetURL) + XCTAssertEqual(response.url, request.url) + XCTAssertEqual(response.history.map(\.request.url), [request.url]) // ... while HTTP+UNIX to HTTP, HTTPS, or HTTP(S)+UNIX should succeed targetURL = self.defaultHTTPBinURLPrefix + "ok" @@ -550,6 +566,11 @@ final class HTTPClientTests: XCTestCaseHTTPClientTestsBaseClass { response = try localClient.execute(request: request).wait() XCTAssertEqual(response.status, .ok) + XCTAssertEqual(response.url?.absoluteString, targetURL) + XCTAssertEqual( + response.history.map(\.request.url.absoluteString), + [request.url.absoluteString, targetURL] + ) targetURL = "https://localhost:\(httpsBin.port)/ok" request = try Request( @@ -562,6 +583,11 @@ final class HTTPClientTests: XCTestCaseHTTPClientTestsBaseClass { response = try localClient.execute(request: request).wait() XCTAssertEqual(response.status, .ok) + XCTAssertEqual(response.url?.absoluteString, targetURL) + XCTAssertEqual( + response.history.map(\.request.url.absoluteString), + [request.url.absoluteString, targetURL] + ) targetURL = "http+unix://\(httpSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/ok" @@ -575,6 +601,11 @@ final class HTTPClientTests: XCTestCaseHTTPClientTestsBaseClass { response = try localClient.execute(request: request).wait() XCTAssertEqual(response.status, .ok) + XCTAssertEqual(response.url?.absoluteString, targetURL) + XCTAssertEqual( + response.history.map(\.request.url.absoluteString), + [request.url.absoluteString, targetURL] + ) targetURL = "https+unix://\(httpsSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/ok" @@ -588,6 +619,11 @@ final class HTTPClientTests: XCTestCaseHTTPClientTestsBaseClass { response = try localClient.execute(request: request).wait() XCTAssertEqual(response.status, .ok) + XCTAssertEqual(response.url?.absoluteString, targetURL) + XCTAssertEqual( + response.history.map(\.request.url.absoluteString), + [request.url.absoluteString, targetURL] + ) // ... and HTTPS+UNIX to HTTP, HTTPS, or HTTP(S)+UNIX should succeed targetURL = self.defaultHTTPBinURLPrefix + "ok" @@ -601,6 +637,11 @@ final class HTTPClientTests: XCTestCaseHTTPClientTestsBaseClass { response = try localClient.execute(request: request).wait() XCTAssertEqual(response.status, .ok) + XCTAssertEqual(response.url?.absoluteString, targetURL) + XCTAssertEqual( + response.history.map(\.request.url.absoluteString), + [request.url.absoluteString, targetURL] + ) targetURL = "https://localhost:\(httpsBin.port)/ok" request = try Request( @@ -613,6 +654,11 @@ final class HTTPClientTests: XCTestCaseHTTPClientTestsBaseClass { response = try localClient.execute(request: request).wait() XCTAssertEqual(response.status, .ok) + XCTAssertEqual(response.url?.absoluteString, targetURL) + XCTAssertEqual( + response.history.map(\.request.url.absoluteString), + [request.url.absoluteString, targetURL] + ) targetURL = "http+unix://\(httpSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/ok" @@ -626,6 +672,11 @@ final class HTTPClientTests: XCTestCaseHTTPClientTestsBaseClass { response = try localClient.execute(request: request).wait() XCTAssertEqual(response.status, .ok) + XCTAssertEqual(response.url?.absoluteString, targetURL) + XCTAssertEqual( + response.history.map(\.request.url.absoluteString), + [request.url.absoluteString, targetURL] + ) targetURL = "https+unix://\(httpsSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/ok" @@ -639,6 +690,11 @@ final class HTTPClientTests: XCTestCaseHTTPClientTestsBaseClass { response = try localClient.execute(request: request).wait() XCTAssertEqual(response.status, .ok) + XCTAssertEqual(response.url?.absoluteString, targetURL) + XCTAssertEqual( + response.history.map(\.request.url.absoluteString), + [request.url.absoluteString, targetURL] + ) } ) } From 80cdab5c4ca54998a9cd16c218108d76907a5078 Mon Sep 17 00:00:00 2001 From: Greg Cotten Date: Fri, 21 Feb 2025 15:36:53 -0800 Subject: [PATCH 2/8] add request/response history to `FileDownloadDelegate.Progress/Response` --- .../AsyncHTTPClient/FileDownloadDelegate.swift | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/Sources/AsyncHTTPClient/FileDownloadDelegate.swift b/Sources/AsyncHTTPClient/FileDownloadDelegate.swift index 16a5e5251..4bd997804 100644 --- a/Sources/AsyncHTTPClient/FileDownloadDelegate.swift +++ b/Sources/AsyncHTTPClient/FileDownloadDelegate.swift @@ -16,15 +16,25 @@ import NIOCore import NIOHTTP1 import NIOPosix +import struct Foundation.URL + /// Handles a streaming download to a given file path, allowing headers and progress to be reported. public final class FileDownloadDelegate: HTTPClientResponseDelegate { /// The response type for this delegate: the total count of bytes as reported by the response - /// "Content-Length" header (if available), the count of bytes downloaded, and the - /// response head. + /// "Content-Length" header (if available), the count of bytes downloaded, the + /// response head, and a history of requests and responses. public struct Progress: Sendable { public var totalBytes: Int? public var receivedBytes: Int + /// The history of all requests and responses in redirect order. + public var history: [HTTPClient.RequestResponse] = [] + + /// The target URL (after redirects) of the response. + public var url: URL? { + self.history.last?.request.url + } + public var head: HTTPResponseHead { get { assert(self._head != nil) @@ -150,6 +160,10 @@ public final class FileDownloadDelegate: HTTPClientResponseDelegate { ) } + public func didVisitURL(task: HTTPClient.Task, _ request: HTTPClient.Request, _ head: HTTPResponseHead) { + self.progress.history.append(.init(request: request, responseHead: head)) + } + public func didReceiveHead( task: HTTPClient.Task, _ head: HTTPResponseHead From a24471750da7f24197b1d07d5af8025608870fbf Mon Sep 17 00:00:00 2001 From: Greg Cotten Date: Fri, 21 Feb 2025 15:37:09 -0800 Subject: [PATCH 3/8] add request/response history to async/await `HTTPClientResponse` --- .../AsyncAwait/HTTPClient+execute.swift | 20 ++++++++++- .../AsyncAwait/HTTPClientResponse.swift | 35 +++++++++++++++++-- .../AsyncAwaitEndToEndTests.swift | 9 ++++- 3 files changed, 59 insertions(+), 5 deletions(-) diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClient+execute.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClient+execute.swift index 3c3a6030c..5fc1be9f5 100644 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClient+execute.swift +++ b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClient+execute.swift @@ -85,11 +85,29 @@ extension HTTPClient { ) async throws -> HTTPClientResponse { var currentRequest = request var currentRedirectState = redirectState + var history: [HTTPClientRequestResponse] = [] // this loop is there to follow potential redirects while true { let preparedRequest = try HTTPClientRequest.Prepared(currentRequest, dnsOverride: configuration.dnsOverride) - let response = try await self.executeCancellable(preparedRequest, deadline: deadline, logger: logger) + let response = try await { + var response = try await self.executeCancellable(preparedRequest, deadline: deadline, logger: logger) + + history.append( + .init( + request: currentRequest, + responseHead: .init( + version: response.version, + status: response.status, + headers: response.headers + ) + ) + ) + + response.history = history + + return response + }() guard var redirectState = currentRedirectState else { // a `nil` redirectState means we should not follow redirects diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift index 832eb7b41..548f31493 100644 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift +++ b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift @@ -15,6 +15,8 @@ import NIOCore import NIOHTTP1 +import struct Foundation.URL + /// A representation of an HTTP response for the Swift Concurrency HTTPClient API. /// /// This object is similar to ``HTTPClient/Response``, but used for the Swift Concurrency API. @@ -32,16 +34,30 @@ public struct HTTPClientResponse: Sendable { /// The body of this HTTP response. public var body: Body + /// The history of all requests and responses in redirect order. + public var history: [HTTPClientRequestResponse] + + /// The target URL (after redirects) of the response. + public var url: URL? { + guard let lastRequestURL = self.history.last?.request.url else { + return nil + } + + return URL(string: lastRequestURL) + } + @inlinable public init( version: HTTPVersion = .http1_1, status: HTTPResponseStatus = .ok, headers: HTTPHeaders = [:], - body: Body = Body() + body: Body = Body(), + history: [HTTPClientRequestResponse] = [] ) { self.version = version self.status = status self.headers = headers self.body = body + self.history = history } init( @@ -49,7 +65,8 @@ public struct HTTPClientResponse: Sendable { version: HTTPVersion, status: HTTPResponseStatus, headers: HTTPHeaders, - body: TransactionBody + body: TransactionBody, + history: [HTTPClientRequestResponse] = [] ) { self.init( version: version, @@ -64,11 +81,23 @@ public struct HTTPClientResponse: Sendable { status: status ) ) - ) + ), + history: history ) } } +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) +public struct HTTPClientRequestResponse: Sendable { + public var request: HTTPClientRequest + public var responseHead: HTTPResponseHead + + public init(request: HTTPClientRequest, responseHead: HTTPResponseHead) { + self.request = request + self.responseHead = responseHead + } +} + @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension HTTPClientResponse { /// A representation of the response body for an HTTP response. diff --git a/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift b/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift index c580164a0..61e32b966 100644 --- a/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift +++ b/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift @@ -76,6 +76,8 @@ final class AsyncAwaitEndToEndTests: XCTestCase { return } + XCTAssertEqual(response.url?.absoluteString, request.url) + XCTAssertEqual(response.history.map(\.request.url), [request.url]) XCTAssertEqual(response.status, .ok) XCTAssertEqual(response.version, .http2) } @@ -98,6 +100,8 @@ final class AsyncAwaitEndToEndTests: XCTestCase { return } + XCTAssertEqual(response.url?.absoluteString, request.url) + XCTAssertEqual(response.history.map(\.request.url), [request.url]) XCTAssertEqual(response.status, .ok) XCTAssertEqual(response.version, .http2) } @@ -734,9 +738,10 @@ final class AsyncAwaitEndToEndTests: XCTestCase { defer { XCTAssertNoThrow(try client.syncShutdown()) } let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) var request = HTTPClientRequest(url: "https://127.0.0.1:\(bin.port)/redirect/target") + let redirectURL = "https://localhost:\(bin.port)/echohostheader" request.headers.replaceOrAdd( name: "X-Target-Redirect-URL", - value: "https://localhost:\(bin.port)/echohostheader" + value: redirectURL ) guard @@ -753,6 +758,8 @@ final class AsyncAwaitEndToEndTests: XCTestCase { XCTAssertNoThrow(maybeRequestInfo = try JSONDecoder().decode(RequestInfo.self, from: body)) guard let requestInfo = maybeRequestInfo else { return } + XCTAssertEqual(response.url?.absoluteString, redirectURL) + XCTAssertEqual(response.history.map(\.request.url), [request.url, redirectURL]) XCTAssertEqual(response.status, .ok) XCTAssertEqual(response.version, .http2) XCTAssertEqual(requestInfo.data, "localhost:\(bin.port)") From e9577c511f76f55959786980812f046a435cb745 Mon Sep 17 00:00:00 2001 From: Greg Cotten Date: Tue, 25 Feb 2025 12:14:46 -0800 Subject: [PATCH 4/8] mark `HTTPClient` Body and Request as `Sendable` --- Sources/AsyncHTTPClient/HTTPHandler.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/AsyncHTTPClient/HTTPHandler.swift b/Sources/AsyncHTTPClient/HTTPHandler.swift index babf87ca9..33d35ac73 100644 --- a/Sources/AsyncHTTPClient/HTTPHandler.swift +++ b/Sources/AsyncHTTPClient/HTTPHandler.swift @@ -23,7 +23,7 @@ import NIOSSL extension HTTPClient { /// A request body. - public struct Body { + public struct Body: Sendable { /// A streaming uploader. /// /// ``StreamWriter`` abstracts @@ -209,7 +209,7 @@ extension HTTPClient { } /// Represents an HTTP request. - public struct Request { + public struct Request: Sendable { /// Request HTTP method, defaults to `GET`. public let method: HTTPMethod /// Remote URL. @@ -469,7 +469,7 @@ extension HTTPClient { } } - public struct RequestResponse { + public struct RequestResponse: Sendable { public var request: Request public var responseHead: HTTPResponseHead From b35e140ab453e48dba6c421cc9ee3a9067dcf8f3 Mon Sep 17 00:00:00 2001 From: Greg Cotten Date: Thu, 27 Feb 2025 13:10:50 -0800 Subject: [PATCH 5/8] surgical @preconcurrency URL for sendability --- Sources/AsyncHTTPClient/HTTPHandler.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sources/AsyncHTTPClient/HTTPHandler.swift b/Sources/AsyncHTTPClient/HTTPHandler.swift index 33d35ac73..4f2664137 100644 --- a/Sources/AsyncHTTPClient/HTTPHandler.swift +++ b/Sources/AsyncHTTPClient/HTTPHandler.swift @@ -14,6 +14,9 @@ import Algorithms import Foundation +#if compiler(<6.0) +@preconcurrency import struct Foundation.URL +#endif import Logging import NIOConcurrencyHelpers import NIOCore From 747e3b8f28bc6dedd54f17065ced6fb898ed36af Mon Sep 17 00:00:00 2001 From: Greg Cotten Date: Thu, 27 Feb 2025 13:17:47 -0800 Subject: [PATCH 6/8] cleanup api breakage for response initializers --- .../AsyncAwait/HTTPClientResponse.swift | 15 +++++++++++- .../AsyncAwait/Transaction.swift | 3 ++- Sources/AsyncHTTPClient/HTTPHandler.swift | 23 +++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift index 548f31493..fa29e6de4 100644 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift +++ b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift @@ -46,6 +46,19 @@ public struct HTTPClientResponse: Sendable { return URL(string: lastRequestURL) } + @inlinable public init( + version: HTTPVersion = .http1_1, + status: HTTPResponseStatus = .ok, + headers: HTTPHeaders = [:], + body: Body = Body() + ) { + self.version = version + self.status = status + self.headers = headers + self.body = body + self.history = [] + } + @inlinable public init( version: HTTPVersion = .http1_1, status: HTTPResponseStatus = .ok, @@ -66,7 +79,7 @@ public struct HTTPClientResponse: Sendable { status: HTTPResponseStatus, headers: HTTPHeaders, body: TransactionBody, - history: [HTTPClientRequestResponse] = [] + history: [HTTPClientRequestResponse] ) { self.init( version: version, diff --git a/Sources/AsyncHTTPClient/AsyncAwait/Transaction.swift b/Sources/AsyncHTTPClient/AsyncAwait/Transaction.swift index 408ebeeb6..b3d2b97c0 100644 --- a/Sources/AsyncHTTPClient/AsyncAwait/Transaction.swift +++ b/Sources/AsyncHTTPClient/AsyncAwait/Transaction.swift @@ -242,7 +242,8 @@ extension Transaction: HTTPExecutableRequest { version: head.version, status: head.status, headers: head.headers, - body: body + body: body, + history: [] ) continuation.resume(returning: response) } diff --git a/Sources/AsyncHTTPClient/HTTPHandler.swift b/Sources/AsyncHTTPClient/HTTPHandler.swift index 4f2664137..dd51426f1 100644 --- a/Sources/AsyncHTTPClient/HTTPHandler.swift +++ b/Sources/AsyncHTTPClient/HTTPHandler.swift @@ -405,6 +405,29 @@ extension HTTPClient { self.history = [] } + /// Create HTTP `Response`. + /// + /// - parameters: + /// - host: Remote host of the request. + /// - status: Response HTTP status. + /// - version: Response HTTP version. + /// - headers: Reponse HTTP headers. + /// - body: Response body. + public init( + host: String, + status: HTTPResponseStatus, + version: HTTPVersion, + headers: HTTPHeaders, + body: ByteBuffer? + ) { + self.host = host + self.status = status + self.version = version + self.headers = headers + self.body = body + self.history = [] + } + /// Create HTTP `Response`. /// /// - parameters: From c297779fa4516f07257b96d3b93e01d9d0aa4ba0 Mon Sep 17 00:00:00 2001 From: Greg Cotten Date: Thu, 27 Feb 2025 13:20:10 -0800 Subject: [PATCH 7/8] Update HTTPHandler.swift --- Sources/AsyncHTTPClient/HTTPHandler.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/AsyncHTTPClient/HTTPHandler.swift b/Sources/AsyncHTTPClient/HTTPHandler.swift index dd51426f1..d2a690bec 100644 --- a/Sources/AsyncHTTPClient/HTTPHandler.swift +++ b/Sources/AsyncHTTPClient/HTTPHandler.swift @@ -13,9 +13,10 @@ //===----------------------------------------------------------------------===// import Algorithms +#if compiler(>=6.0) import Foundation -#if compiler(<6.0) -@preconcurrency import struct Foundation.URL +#else +@preconcurrency import Foundation #endif import Logging import NIOConcurrencyHelpers From 2ee71ce02e4dbd36345056023b158c32258f78bd Mon Sep 17 00:00:00 2001 From: Greg Cotten Date: Mon, 3 Mar 2025 07:38:59 -0800 Subject: [PATCH 8/8] fix linter complaint --- Sources/AsyncHTTPClient/HTTPHandler.swift | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Sources/AsyncHTTPClient/HTTPHandler.swift b/Sources/AsyncHTTPClient/HTTPHandler.swift index d2a690bec..95bdaeaed 100644 --- a/Sources/AsyncHTTPClient/HTTPHandler.swift +++ b/Sources/AsyncHTTPClient/HTTPHandler.swift @@ -13,11 +13,6 @@ //===----------------------------------------------------------------------===// import Algorithms -#if compiler(>=6.0) -import Foundation -#else -@preconcurrency import Foundation -#endif import Logging import NIOConcurrencyHelpers import NIOCore @@ -25,6 +20,12 @@ import NIOHTTP1 import NIOPosix import NIOSSL +#if compiler(>=6.0) +import Foundation +#else +@preconcurrency import Foundation +#endif + extension HTTPClient { /// A request body. public struct Body: Sendable {