Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Pull request

on:
pull_request:
types: [opened, reopened, synchronize]

jobs:
# tests:
# name: Test
# uses: swiftlang/github-workflows/.github/workflows/swift_package_test.yml@main
# with:
# linux_pre_build_command: apt-get update && apt-get install -y locales locales-all libsqlite3-dev
soundness:
name: Soundness
uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main
with:
license_header_check_project_name: "Swift.org"
1 change: 1 addition & 0 deletions .swiftformatignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Sources/Helpers/Vendor/*
2 changes: 2 additions & 0 deletions .unacceptablelanguageignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Sources/AsyncProcess/ProcessExecutor.swift
Tests/AsyncProcessTests/IntegrationTests.swift
189 changes: 100 additions & 89 deletions Sources/SwiftSDKGenerator/SystemUtils/HTTPClient+Download.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ public protocol HTTPClientProtocol: Sendable {
}

extension HTTPClientProtocol {
static func with<Result: Sendable>(_ body: @Sendable (any HTTPClientProtocol) async throws -> Result) async throws
static func with<Result: Sendable>(
_ body: @Sendable (any HTTPClientProtocol) async throws -> Result
) async throws
-> Result
{
try await self.with(http1Only: false, body)
Expand All @@ -63,107 +65,112 @@ extension HTTPClientProtocol {
extension FilePath: @unchecked Sendable {}

#if canImport(AsyncHTTPClient)
import AsyncHTTPClient

extension FileDownloadDelegate.Progress: @unchecked Sendable {}

extension HTTPClient: HTTPClientProtocol {
public static func with<Result: Sendable>(
http1Only: Bool, _ body: @Sendable (any HTTPClientProtocol) async throws -> Result
) async throws -> Result {
var configuration = HTTPClient.Configuration(redirectConfiguration: .follow(max: 5, allowCycles: false))
if http1Only {
configuration.httpVersion = .http1Only
}
let client = HTTPClient(eventLoopGroupProvider: .singleton, configuration: configuration)
return try await withAsyncThrowing {
try await body(client)
} defer: {
try await client.shutdown()
import AsyncHTTPClient

extension FileDownloadDelegate.Progress: @unchecked Sendable {}

extension HTTPClient: HTTPClientProtocol {
public static func with<Result: Sendable>(
http1Only: Bool, _ body: @Sendable (any HTTPClientProtocol) async throws -> Result
) async throws -> Result {
var configuration = HTTPClient.Configuration(
redirectConfiguration: .follow(max: 5, allowCycles: false))
if http1Only {
configuration.httpVersion = .http1Only
}
let client = HTTPClient(eventLoopGroupProvider: .singleton, configuration: configuration)
return try await withAsyncThrowing {
try await body(client)
} defer: {
try await client.shutdown()
}
}
}

public func get(url: String) async throws -> (status: NIOHTTP1.HTTPResponseStatus, body: NIOCore.ByteBuffer?) {
let response = try await self.get(url: url).get()
return (status: response.status, body: response.body)
}
public func get(url: String) async throws -> (
status: NIOHTTP1.HTTPResponseStatus, body: NIOCore.ByteBuffer?
) {
let response = try await self.get(url: url).get()
return (status: response.status, body: response.body)
}

public func head(url: String, headers: NIOHTTP1.HTTPHeaders) async throws -> Bool {
var headRequest = HTTPClientRequest(url: url)
headRequest.method = .HEAD
headRequest.headers = ["Accept": "*/*", "User-Agent": "Swift SDK Generator"]
return try await self.execute(headRequest, deadline: .distantFuture).status == .ok
}
public func head(url: String, headers: NIOHTTP1.HTTPHeaders) async throws -> Bool {
var headRequest = HTTPClientRequest(url: url)
headRequest.method = .HEAD
headRequest.headers = ["Accept": "*/*", "User-Agent": "Swift SDK Generator"]
return try await self.execute(headRequest, deadline: .distantFuture).status == .ok
}

public func downloadFile(
from url: URL,
to path: FilePath
) async throws {
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<(), Error>) in
do {
let delegate = try FileDownloadDelegate(
path: path.string,
reportHead: { task, responseHead in
if responseHead.status != .ok {
task.fail(reason: GeneratorError.fileDownloadFailed(url, responseHead.status.description))
public func downloadFile(
from url: URL,
to path: FilePath
) async throws {
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<(), Error>) in
do {
let delegate = try FileDownloadDelegate(
path: path.string,
reportHead: { task, responseHead in
if responseHead.status != .ok {
task.fail(
reason: GeneratorError.fileDownloadFailed(url, responseHead.status.description))
}
}
)
let request = try HTTPClient.Request(url: url)

execute(request: request, delegate: delegate).futureResult.whenComplete {
switch $0 {
case let .failure(error):
continuation.resume(throwing: error)
case .success:
continuation.resume(returning: ())
}
}
)
let request = try HTTPClient.Request(url: url)

execute(request: request, delegate: delegate).futureResult.whenComplete {
switch $0 {
case let .failure(error):
continuation.resume(throwing: error)
case .success:
continuation.resume(returning: ())
}
} catch {
continuation.resume(throwing: error)
}
} catch {
continuation.resume(throwing: error)
}
}
}

public func streamDownloadProgress(
from url: URL,
to path: FilePath
) -> AsyncThrowingStream<DownloadProgress, any Error> {
.init { continuation in
do {
let delegate = try FileDownloadDelegate(
path: path.string,
reportHead: {
if $0.status != .ok {
continuation
.finish(throwing: FileOperationError.downloadFailed(url, $0.status.description))
public func streamDownloadProgress(
from url: URL,
to path: FilePath
) -> AsyncThrowingStream<DownloadProgress, any Error> {
.init { continuation in
do {
let delegate = try FileDownloadDelegate(
path: path.string,
reportHead: {
if $0.status != .ok {
continuation
.finish(throwing: FileOperationError.downloadFailed(url, $0.status.description))
}
},
reportProgress: {
continuation.yield(
DownloadProgress(totalBytes: $0.totalBytes, receivedBytes: $0.receivedBytes)
)
}
)
let request = try HTTPClient.Request(url: url)

execute(request: request, delegate: delegate).futureResult.whenComplete {
switch $0 {
case let .failure(error):
continuation.finish(throwing: error)
case let .success(finalProgress):
continuation.yield(
DownloadProgress(
totalBytes: finalProgress.totalBytes, receivedBytes: finalProgress.receivedBytes)
)
continuation.finish()
}
},
reportProgress: {
continuation.yield(
DownloadProgress(totalBytes: $0.totalBytes, receivedBytes: $0.receivedBytes)
)
}
)
let request = try HTTPClient.Request(url: url)

execute(request: request, delegate: delegate).futureResult.whenComplete {
switch $0 {
case let .failure(error):
continuation.finish(throwing: error)
case let .success(finalProgress):
continuation.yield(
DownloadProgress(totalBytes: finalProgress.totalBytes, receivedBytes: finalProgress.receivedBytes)
)
continuation.finish()
}
} catch {
continuation.finish(throwing: error)
}
} catch {
continuation.finish(throwing: error)
}
}
}
}
#endif

struct OfflineHTTPClient: HTTPClientProtocol {
Expand All @@ -189,11 +196,15 @@ struct OfflineHTTPClient: HTTPClientProtocol {
}
}

public func get(url: String) async throws -> (status: NIOHTTP1.HTTPResponseStatus, body: NIOCore.ByteBuffer?) {
throw FileOperationError.downloadFailed(URL(string: url)!, "Cannot fetch file with offline client")
public func get(url: String) async throws -> (
status: NIOHTTP1.HTTPResponseStatus, body: NIOCore.ByteBuffer?
) {
throw FileOperationError.downloadFailed(
URL(string: url)!, "Cannot fetch file with offline client")
}

public func head(url: String, headers: NIOHTTP1.HTTPHeaders) async throws -> Bool {
throw FileOperationError.downloadFailed(URL(string: url)!, "Cannot fetch file with offline client")
throw FileOperationError.downloadFailed(
URL(string: url)!, "Cannot fetch file with offline client")
}
}
2 changes: 1 addition & 1 deletion Tests/AsyncProcessTests/IntegrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1020,7 +1020,7 @@ final class IntegrationTests: XCTestCase {
}

#if os(macOS)
// This test will hang on anything that uses swift-corelibs-foundation because of
// This test will deadlock on anything that uses swift-corelibs-foundation because of
// https://github.com/apple/swift-corelibs-foundation/issues/4795
// Foundation.Process on Linux doesn't correctly detect when child process dies (creating zombie processes)
func testCanDealWithRunawayChildProcesses() async throws {
Expand Down
Loading
Loading