From a479e45acfa81732a23190ee501553f7b83da502 Mon Sep 17 00:00:00 2001 From: Vimal Date: Mon, 4 Apr 2022 07:51:15 -0700 Subject: [PATCH] format and review comments --- Sources/GRPC/ClientConnection.swift | 2 +- .../GRPC/GRPCChannel/GRPCChannelBuilder.swift | 2 +- Sources/GRPC/PlatformSupport.swift | 12 ++++ Sources/GRPC/ServerBuilder.swift | 6 ++ .../GRPCTests/WithConnectedSocketTests.swift | 72 +++++++++++++++++++ 5 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 Tests/GRPCTests/WithConnectedSocketTests.swift diff --git a/Sources/GRPC/ClientConnection.swift b/Sources/GRPC/ClientConnection.swift index a3150dfda..384be70eb 100644 --- a/Sources/GRPC/ClientConnection.swift +++ b/Sources/GRPC/ClientConnection.swift @@ -308,7 +308,7 @@ public struct ConnectionTarget { return address.host case let .socketAddress(.v6(address)): return address.host - case .unixDomainSocket, .socketAddress(.unixDomainSocket), .connectedSocket(_): + case .unixDomainSocket, .socketAddress(.unixDomainSocket), .connectedSocket: return "localhost" } } diff --git a/Sources/GRPC/GRPCChannel/GRPCChannelBuilder.swift b/Sources/GRPC/GRPCChannel/GRPCChannelBuilder.swift index de438104a..3bb0f71a5 100644 --- a/Sources/GRPC/GRPCChannel/GRPCChannelBuilder.swift +++ b/Sources/GRPC/GRPCChannel/GRPCChannelBuilder.swift @@ -86,7 +86,7 @@ extension ClientConnection { return ClientConnection(configuration: self.configuration) } - public func withConnectedSocket(socket: NIOBSDSocket.Handle) -> ClientConnection { + public func withConnectedSocket(_ socket: NIOBSDSocket.Handle) -> ClientConnection { precondition( !PlatformSupport.isTransportServicesEventLoopGroup(self.configuration.eventLoopGroup), "'\(#function)' requires 'group' to not be a 'NIOTransportServices.NIOTSEventLoopGroup' or 'NIOTransportServices.QoSEventLoop' (but was '\(type(of: self.configuration.eventLoopGroup))'" diff --git a/Sources/GRPC/PlatformSupport.swift b/Sources/GRPC/PlatformSupport.swift index d5393a88c..f18996af5 100644 --- a/Sources/GRPC/PlatformSupport.swift +++ b/Sources/GRPC/PlatformSupport.swift @@ -124,6 +124,12 @@ public protocol ClientBootstrapProtocol { func channelInitializer(_ handler: @escaping (Channel) -> EventLoopFuture) -> Self } +extension ClientBootstrapProtocol { + public func withConnectedSocket(_ socket: NIOBSDSocket.Handle) -> EventLoopFuture { + preconditionFailure("withConnectedSocket(_:) is not implemented") + } +} + extension ClientBootstrap: ClientBootstrapProtocol {} #if canImport(Network) @@ -150,6 +156,12 @@ public protocol ServerBootstrapProtocol { func childChannelOption(_ option: T, value: T.Value) -> Self where T: ChannelOption } +extension ServerBootstrapProtocol { + public func withBoundSocket(_ connectedSocket: NIOBSDSocket.Handle) -> EventLoopFuture { + preconditionFailure("withBoundSocket(_:) is not implemented") + } +} + extension ServerBootstrap: ServerBootstrapProtocol {} #if canImport(Network) diff --git a/Sources/GRPC/ServerBuilder.swift b/Sources/GRPC/ServerBuilder.swift index 672f046ef..3082d48c0 100644 --- a/Sources/GRPC/ServerBuilder.swift +++ b/Sources/GRPC/ServerBuilder.swift @@ -54,6 +54,12 @@ extension Server { self.configuration.tlsConfiguration = self.maybeTLS return Server.start(configuration: self.configuration) } + + public func bind(unixDomainSocketPath path: String) -> EventLoopFuture { + self.configuration.target = .unixDomainSocket(path) + self.configuration.tlsConfiguration = self.maybeTLS + return Server.start(configuration: self.configuration) + } } } diff --git a/Tests/GRPCTests/WithConnectedSocketTests.swift b/Tests/GRPCTests/WithConnectedSocketTests.swift new file mode 100644 index 000000000..5826dff3e --- /dev/null +++ b/Tests/GRPCTests/WithConnectedSocketTests.swift @@ -0,0 +1,72 @@ +/* + * Copyright 2020, gRPC Authors All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import EchoImplementation +import EchoModel +@testable import GRPC +import NIOCore +@testable import NIOPosix +import XCTest + +class WithConnectedSockettests: GRPCTestCase { + func testKeepaliveTimeoutFiresBeforeConnectionIsReady() throws { + let group = NIOPosix.MultiThreadedEventLoopGroup(numberOfThreads: 1) + defer { + XCTAssertNoThrow(try group.syncShutdownGracefully()) + } + + let path = "/tmp/grpc-\(getpid()).sock" + // Setup a server. + let server = try Server.insecure(group: group) + .withServiceProviders([EchoProvider()]) + .withLogger(self.serverLogger) + .bind(unixDomainSocketPath: path) + .wait() + defer { + XCTAssertNoThrow(try server.close().wait()) + } + + let socket = try Socket(protocolFamily: .unix, type: .stream) + XCTAssert(try socket.connect(to: .init(unixDomainSocketPath: path))) + + // Setup a connection. We'll add a handler to drop all reads, this is somewhat equivalent to + // simulating bad network conditions and allows us to setup a connection and have our keepalive + // timeout expire. + let connection = ClientConnection.insecure(group: group) + .withBackgroundActivityLogger(self.clientLogger) + // See above comments for why we need this. + .withCallStartBehavior(.fastFailure) + .withKeepalive(.init(interval: .seconds(1), timeout: .milliseconds(100))) + .withDebugChannelInitializer { channel in + channel.pipeline.addHandler(ReadDroppingHandler(), position: .first) + } + .withConnectedSocket(try socket.takeDescriptorOwnership()) + defer { + XCTAssertNoThrow(try connection.close().wait()) + } + + let client = Echo_EchoClient(channel: connection) + let get = client.get(.with { $0.text = "Hello" }) + XCTAssertThrowsError(try get.response.wait()) + XCTAssertEqual(try get.status.map { $0.code }.wait(), .unavailable) + } + + class ReadDroppingHandler: ChannelDuplexHandler { + typealias InboundIn = Any + typealias OutboundIn = Any + + func channelRead(context: ChannelHandlerContext, data: NIOAny) {} + } +}