diff --git a/Package.resolved b/Package.resolved index c4dbd37bb..093ce9c18 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "0cc15dcb560e0831670e6df8d924507807667ee596277c178fbd13c529de5f6a", + "originHash" : "1dc4985ac96737730729ad6c81bfb43369e2f32bfc1b12ff3952a0d17958bbf8", "pins" : [ { "identity" : "async-http-client", @@ -33,8 +33,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/grpc/grpc-swift-nio-transport.git", "state" : { - "revision" : "f37e0c2d293cea668b11e10e1fb1c24cb40781ff", - "version" : "2.4.4" + "revision" : "2ca31f06658ed288a2560e23ad649acbb3d6b3a3", + "version" : "2.9.0" } }, { diff --git a/Package.swift b/Package.swift index 34d18c7d7..34a89f980 100644 --- a/Package.swift +++ b/Package.swift @@ -62,7 +62,7 @@ let package = Package( .package(url: "https://github.com/apple/swift-protobuf.git", from: "1.36.0"), .package(url: "https://github.com/apple/swift-system.git", from: "1.6.4"), .package(url: "https://github.com/grpc/grpc-swift-2.git", from: "2.3.0"), - .package(url: "https://github.com/grpc/grpc-swift-nio-transport.git", from: "2.4.4"), + .package(url: "https://github.com/grpc/grpc-swift-nio-transport.git", from: "2.9.0"), .package(url: "https://github.com/grpc/grpc-swift-protobuf.git", from: "2.2.0"), .package(url: "https://github.com/swift-server/async-http-client.git", from: "1.20.1"), .package(url: "https://github.com/swiftlang/swift-docc-plugin.git", from: "1.1.0"), diff --git a/Sources/ContainerBuild/Builder.swift b/Sources/ContainerBuild/Builder.swift index 7a0f45209..c43b5ea63 100644 --- a/Sources/ContainerBuild/Builder.swift +++ b/Sources/ContainerBuild/Builder.swift @@ -24,9 +24,6 @@ import GRPCCore import GRPCNIOTransportHTTP2 import Logging import NIO -import NIOCore -import NIOHPACK -import NIOHTTP2 import NIOPosix public struct Builder: Sendable { @@ -39,22 +36,27 @@ public struct Builder: Sendable { let clientTask: Task let logger: Logger - public init(socket: FileHandle, group: EventLoopGroup, logger: Logger) throws { + public init(socket: FileHandle, group: EventLoopGroup, logger: Logger) async throws { try socket.setSendBufSize(4 << 20) try socket.setRecvBufSize(2 << 20) - let channel = try ClientBootstrap(group: group) - .channelInitializer { channel in - channel.eventLoop.makeCompletedFuture(withResultOf: { - try channel.pipeline.syncOperations.addHandler(HTTP2ConnectBufferingHandler()) - }) + let transport = try await HTTP2ClientTransport.WrappedChannel.wrapping( + config: .defaults, + serviceConfig: .init() + ) { configure in + try await withCheckedThrowingContinuation { continuation in + ClientBootstrap(group: group) + .channelInitializer { channel in + configure(channel).map { configured in + continuation.resume(returning: configured) + } + } + .withConnectedSocket(socket.fileDescriptor) + .whenFailure { error in + continuation.resume(throwing: error) + } } - .withConnectedSocket(socket.fileDescriptor) - .wait() - - let transport = HTTP2ClientTransport.WrappedChannel.wrapping( - channel: channel - ) + } let grpcClient = GRPCClient(transport: transport) self.grpcClient = grpcClient @@ -429,49 +431,3 @@ extension FileHandle { } } } - -/// Buffers incoming bytes until the full gRPC HTTP/2 pipeline is configured, then replays them. -/// -/// See the equivalent in Containerization/Vminitd.swift for a full explanation. -private final class HTTP2ConnectBufferingHandler: ChannelDuplexHandler, RemovableChannelHandler { - typealias InboundIn = ByteBuffer - typealias InboundOut = ByteBuffer - typealias OutboundIn = ByteBuffer - typealias OutboundOut = ByteBuffer - - private var removalScheduled = false - private var bufferedReads: [NIOAny] = [] - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - bufferedReads.append(data) - } - - func channelReadComplete(context: ChannelHandlerContext) {} - - func flush(context: ChannelHandlerContext) { - if !removalScheduled { - removalScheduled = true - context.eventLoop.assumeIsolatedUnsafeUnchecked().execute { - context.pipeline.syncOperations.removeHandler(self, promise: nil) - } - } - context.flush() - } - - func removeHandler(context: ChannelHandlerContext, removalToken: ChannelHandlerContext.RemovalToken) { - var didRead = false - while !bufferedReads.isEmpty { - context.fireChannelRead(bufferedReads.removeFirst()) - didRead = true - } - if didRead { - context.fireChannelReadComplete() - } - context.leavePipeline(removalToken: removalToken) - } - - func channelInactive(context: ChannelHandlerContext) { - bufferedReads.removeAll() - context.fireChannelInactive() - } -} diff --git a/Sources/ContainerCommands/BuildCommand.swift b/Sources/ContainerCommands/BuildCommand.swift index 2878f32f4..34ec4356f 100644 --- a/Sources/ContainerCommands/BuildCommand.swift +++ b/Sources/ContainerCommands/BuildCommand.swift @@ -177,7 +177,7 @@ extension Application { let fh = try await client.dial(id: "buildkit", port: vsockPort) let threadGroup: MultiThreadedEventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - let b = try Builder(socket: fh, group: threadGroup, logger: log) + let b = try await Builder(socket: fh, group: threadGroup, logger: log) // If this call succeeds, then BuildKit is running. let _ = try await b.info()