瀏覽代碼

Replace usage of ClientConnection.init with builders

Motivation:

We recently introduced a builder for client connections; this API is
the preferred way to configure connections. We should use it wherever
possible.

Modifications:

Replaces usages of manual configuration using
`ClientConnection.Configuration` with usage of
`ClientConnection.Builder` instead.

Result:

Idiomatic API is more widely used.
George Barnett 5 年之前
父節點
當前提交
b256884908

+ 8 - 11
Sources/Examples/Echo/Runtime/main.swift

@@ -174,11 +174,7 @@ func startEchoServer(group: EventLoopGroup, port: Int, useTLS: Bool) throws {
 }
 
 func makeClient(group: EventLoopGroup, host: String, port: Int, useTLS: Bool) -> Echo_EchoClient {
-  // Configure the connection:
-  var configuration = ClientConnection.Configuration(
-    target: .hostAndPort(host, port),
-    eventLoopGroup: group
-  )
+  let builder: ClientConnection.Builder
 
   if useTLS {
     // We're using some self-signed certs here: check they aren't expired.
@@ -189,15 +185,16 @@ func makeClient(group: EventLoopGroup, host: String, port: Int, useTLS: Bool) ->
       "SSL certificates are expired. Please submit an issue at https://github.com/grpc/grpc-swift."
     )
 
-    configuration.tls = .init(
-      certificateChain: [.certificate(clientCert.certificate)],
-      privateKey: .privateKey(SamplePrivateKey.client),
-      trustRoots: .certificates([caCert.certificate])
-    )
+    builder = ClientConnection.secure(group: group)
+      .withTLS(certificateChain: [clientCert.certificate])
+      .withTLS(privateKey: SamplePrivateKey.client)
+      .withTLS(trustRoots: .certificates([caCert.certificate]))
+  } else {
+    builder = ClientConnection.insecure(group: group)
   }
 
   // Start the connection and create the client:
-  let connection = ClientConnection(configuration: configuration)
+  let connection = builder.connect(host: host, port: port)
   return Echo_EchoClient(channel: connection)
 }
 

+ 4 - 10
Sources/Examples/HelloWorld/Client/main.swift

@@ -65,18 +65,12 @@ func main(args: [String]) {
       try! group.syncShutdownGracefully()
     }
 
-    // Provide some basic configuration for the connection, in this case we connect to an endpoint on
-    // localhost at the given port.
-    let configuration = ClientConnection.Configuration(
-      target: .hostAndPort("localhost", port),
-      eventLoopGroup: group
-    )
-
-    // Create a connection using the configuration.
-    let connection = ClientConnection(configuration: configuration)
+    // Configure the channel, we're not using TLS so the connection is `insecure`.
+    let channel = ClientConnection.insecure(group: group)
+      .connect(host: "localhost", port: port)
 
     // Provide the connection to the generated client.
-    let greeter = Helloworld_GreeterClient(channel: connection)
+    let greeter = Helloworld_GreeterClient(channel: channel)
 
     // Do the greeting.
     greet(name: name, client: greeter)

+ 3 - 6
Sources/Examples/RouteGuide/Client/main.swift

@@ -28,13 +28,10 @@ LoggingSystem.bootstrap {
 
 /// Makes a `RouteGuide` client for a service hosted on "localhost" and listening on the given port.
 func makeClient(port: Int, group: EventLoopGroup) -> Routeguide_RouteGuideClient {
-  let config = ClientConnection.Configuration(
-    target: .hostAndPort("localhost", port),
-    eventLoopGroup: group
-  )
+  let channel = ClientConnection.insecure(group: group)
+    .connect(host: "localhost", port: port)
 
-  let connection = ClientConnection(configuration: config)
-  return Routeguide_RouteGuideClient(channel: connection)
+  return Routeguide_RouteGuideClient(channel: channel)
 }
 
 /// Unary call example. Calls `getFeature` and prints the response.

+ 6 - 2
Sources/GRPC/ClientConnection.swift

@@ -100,7 +100,9 @@ public class ClientConnection {
     return self.channel.eventLoop
   }
 
-  /// Creates a new connection from the given configuration.
+  /// Creates a new connection from the given configuration. Prefer using
+  /// `ClientConnection.secure(group:)` to build a connection secured with TLS or
+  /// `ClientConnection.insecure(group:)` to build a plaintext connection.
   ///
   /// - Important: Users should prefer using `ClientConnection.secure(group:)` to build a connection
   ///   with TLS, or `ClientConnection.insecure(group:)` to build a connection without TLS.
@@ -489,7 +491,9 @@ extension ClientConnection {
       return self.tls == nil ? .http : .https
     }
 
-    /// Create a `Configuration` with some pre-defined defaults.
+    /// Create a `Configuration` with some pre-defined defaults. Prefer using
+    /// `ClientConnection.secure(group:)` to build a connection secured with TLS or
+    /// `ClientConnection.insecure(group:)` to build a plaintext connection.
     ///
     /// - Parameter target: The target to connect to.
     /// - Parameter eventLoopGroup: The event loop group to run the connection on.

+ 5 - 18
Sources/GRPCConnectionBackoffInteropTest/main.swift

@@ -46,29 +46,14 @@ defer {
   try! group.syncShutdownGracefully()
 }
 
-// The client must connect to the control port without TLS.
-let controlConfig = ClientConnection.Configuration(
-  target: .hostAndPort("localhost", controlPort),
-  eventLoopGroup: group,
-  connectionBackoff: .init()
-)
-
-// The client must connect to the retry port with TLS.
-let retryConfig = ClientConnection.Configuration(
-  target: .hostAndPort("localhost", retryPort),
-  eventLoopGroup: group,
-  connectivityStateDelegate: PrintingConnectivityStateDelegate(),
-  tls: .init(),
-  connectionBackoff: .init()
-)
-
 // MARK: - Test Procedure
 
 print("[\(Date())] Starting connection backoff interoperability test...")
 
 // 1. Call 'Start' on server control port with a large deadline or no deadline, wait for it to
 //    finish and check it succeeded.
-let controlConnection = ClientConnection(configuration: controlConfig)
+let controlConnection = ClientConnection.insecure(group: group)
+  .connect(host: "localhost", port: controlPort)
 let controlClient = Grpc_Testing_ReconnectServiceClient(channel: controlConnection)
 print("[\(Date())] Control 'Start' call started")
 let controlStart = controlClient.start(.init(), callOptions: .init(timeout: .infinite))
@@ -80,7 +65,9 @@ print("[\(Date())] Control 'Start' call succeeded")
 //    proper backoffs. A convenient way to achieve this is to call 'Start' with a deadline of 540s.
 //    The rpc should fail with deadline exceeded.
 print("[\(Date())] Retry 'Start' call started")
-let retryConnection = ClientConnection(configuration: retryConfig)
+let retryConnection = ClientConnection.secure(group: group)
+  .withConnectivityStateDelegate(PrintingConnectivityStateDelegate())
+  .connect(host: "localhost", port: retryPort)
 let retryClient = Grpc_Testing_ReconnectServiceClient(
   channel: retryConnection,
   defaultCallOptions: CallOptions(timeout: try! .seconds(540))

+ 13 - 16
Sources/GRPCInteroperabilityTests/main.swift

@@ -45,13 +45,21 @@ enum InteroperabilityTestError: LocalizedError {
 /// - Parameters:
 ///   - instance: `InteroperabilityTest` instance to run.
 ///   - name: the name of the test, use for logging only.
-///   - connection: client connection to use for running the test.
+///   - host: host of the test server.
+///   - port: port of the test server.
+///   - useTLS: whether to use TLS when connecting to the test server.
 /// - Throws: `InteroperabilityTestError` if the test fails.
-func runTest(_ instance: InteroperabilityTest, name: String, defaultConfiguration: ClientConnection.Configuration) throws {
+func runTest(_ instance: InteroperabilityTest, name: String, host: String, port: Int, useTLS: Bool) throws {
+  let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
+  defer {
+    try! group.syncShutdownGracefully()
+  }
+
   do {
     print("Running '\(name)' ... ", terminator: "")
-    let configuration = instance.configure(defaults: defaultConfiguration)
-    let connection = ClientConnection(configuration: configuration)
+    let builder = makeInteroperabilityTestClientBuilder(group: group, useTLS: useTLS)
+    instance.configure(builder: builder)
+    let connection = builder.connect(host: host, port: port)
     defer {
       _ = connection.close()
     }
@@ -172,11 +180,6 @@ func main(args: [String]) {
     }
 
   case let .runTest(name: name, host: host, port: port, useTLS: useTLS):
-    let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
-    defer {
-      try! group.syncShutdownGracefully()
-    }
-
     let test: InteroperabilityTest
     do {
       test = try makeRunnableTest(name: name)
@@ -187,13 +190,7 @@ func main(args: [String]) {
 
     do {
       // Provide some basic configuration. Some tests may override this.
-      let defaults = makeInteroperabilityTestClientConfiguration(
-        host: host,
-        port: port,
-        eventLoopGroup: group,
-        useTLS: useTLS
-      )
-      try runTest(test, name: name, defaultConfiguration: defaults)
+      try runTest(test, name: name, host: host, port: port, useTLS: useTLS)
     } catch {
       print("Error running test: \(error)")
       exit(1)

+ 2 - 3
Sources/GRPCInteroperabilityTestsImplementation/InteroperabilityTestCase.swift

@@ -34,12 +34,11 @@ public protocol InteroperabilityTest {
   /// per-test basis.
   ///
   /// - Parameter defaults: The default configuration for the test run.
-  func configure(defaults: ClientConnection.Configuration) -> ClientConnection.Configuration
+  func configure(builder: ClientConnection.Builder)
 }
 
 extension InteroperabilityTest {
-  func configure(defaults: ClientConnection.Configuration) -> ClientConnection.Configuration {
-    return defaults
+  func configure(builder: ClientConnection.Builder) {
   }
 }
 

+ 10 - 37
Sources/GRPCInteroperabilityTestsImplementation/InteroperabilityTestClientConnection.swift

@@ -18,48 +18,21 @@ import GRPC
 import NIO
 import NIOSSL
 
-public func makeInteroperabilityTestClientConfiguration(
-  host: String,
-  port: Int,
-  eventLoopGroup: EventLoopGroup,
+public func makeInteroperabilityTestClientBuilder(
+  group: EventLoopGroup,
   useTLS: Bool
-) -> ClientConnection.Configuration {
-  var configuration = ClientConnection.Configuration(
-    target: .hostAndPort(host, port),
-    eventLoopGroup: eventLoopGroup
-  )
+) -> ClientConnection.Builder {
+  let builder: ClientConnection.Builder
 
   if useTLS {
     // The CA certificate has a common name of "*.test.google.fr", use the following host override
     // so we can do full certificate verification.
-    configuration.tls = .init(
-      trustRoots: .certificates([InteroperabilityTestCredentials.caCertificate]),
-      hostnameOverride: "foo.test.google.fr"
-    )
+    builder = ClientConnection.secure(group: group)
+      .withTLS(trustRoots: .certificates([InteroperabilityTestCredentials.caCertificate]))
+      .withTLS(serverHostnameOverride: "foo.test.google.fr")
+  } else {
+    builder = ClientConnection.insecure(group: group)
   }
 
-  return configuration
-}
-
-/// Makes a client connections for gRPC interoperability testing.
-///
-/// - Parameters:
-///   - host: The host to connect to.
-///   - port: The port to connect to.
-///   - eventLoopGroup: Event loop group to run client connection on.
-///   - useTLS: Whether to use TLS or not.
-/// - Returns: A future of a `ClientConnection`.
-public func makeInteroperabilityTestClientConnection(
-  host: String,
-  port: Int,
-  eventLoopGroup: EventLoopGroup,
-  useTLS: Bool
-) -> ClientConnection {
-  let configuration = makeInteroperabilityTestClientConfiguration(
-    host: host,
-    port: port,
-    eventLoopGroup: eventLoopGroup,
-    useTLS: useTLS
-  )
-  return ClientConnection(configuration: configuration)
+  return builder
 }

+ 1 - 1
Sources/GRPCPerformanceTests/Benchmarks/ServerProvidingBenchmark.swift

@@ -31,7 +31,7 @@ class ServerProvidingBenchmark: Benchmark {
   func setUp() throws {
     self.group = MultiThreadedEventLoopGroup(numberOfThreads: self.threadCount)
     let configuration = Server.Configuration(
-      target: .hostAndPort("", 0),
+      target: .hostAndPort("127.0.0.1", 0),
       eventLoopGroup: self.group,
       serviceProviders: self.providers
     )

+ 3 - 7
Sources/GRPCPerformanceTests/Benchmarks/UnaryThroughput.swift

@@ -39,14 +39,10 @@ class Unary: ServerProvidingBenchmark {
   override func setUp() throws {
     try super.setUp()
     self.group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
+    let channel = ClientConnection.insecure(group: self.group)
+      .connect(host: "127.0.0.1", port: self.server.channel.localAddress!.port!)
 
-    let configuration = ClientConnection.Configuration(
-      target: .socketAddress(self.server.channel.localAddress!),
-      eventLoopGroup: self.group
-    )
-
-    let connection = ClientConnection(configuration: configuration)
-    self.client = .init(channel: connection)
+    self.client = .init(channel: channel)
   }
 
   override func run() throws {

+ 18 - 6
Tests/GRPCTests/BasicEchoTestCase.swift

@@ -114,11 +114,20 @@ class EchoTestCaseBase: GRPCTestCase {
     return .userDefined(.posix)
   }
 
-  func makeClientConfiguration(port: Int) throws -> ClientConnection.Configuration {
-    return .init(
-      target: .hostAndPort("localhost", port),
-      eventLoopGroup: self.clientEventLoopGroup,
-      tls: self.transportSecurity.makeClientTLSConfiguration())
+  func connectionBuilder() -> ClientConnection.Builder {
+    switch self.transportSecurity {
+    case .none:
+      return ClientConnection.insecure(group: self.clientEventLoopGroup)
+    case .anonymousClient:
+      return ClientConnection.secure(group: self.clientEventLoopGroup)
+        .withTLS(trustRoots: .certificates([SampleCertificate.ca.certificate]))
+
+    case .mutualAuthentication:
+      return ClientConnection.secure(group: self.clientEventLoopGroup)
+        .withTLS(trustRoots: .certificates([SampleCertificate.ca.certificate]))
+        .withTLS(certificateChain: [SampleCertificate.client.certificate])
+        .withTLS(privateKey: SamplePrivateKey.client)
+    }
   }
 
   func makeServerConfiguration() throws -> Server.Configuration {
@@ -135,7 +144,10 @@ class EchoTestCaseBase: GRPCTestCase {
   }
 
   func makeClientConnection(port: Int) throws -> ClientConnection {
-    return try ClientConnection(configuration: self.makeClientConfiguration(port: port))
+    return self.connectionBuilder().connect(
+      host: "localhost",
+      port: port
+    )
   }
 
   func makeEchoProvider() -> Echo_EchoProvider { return EchoProvider() }

+ 14 - 18
Tests/GRPCTests/ClientConnectionBackoffTests.swift

@@ -124,22 +124,19 @@ class ClientConnectionBackoffTests: GRPCTestCase {
     return Server.start(configuration: configuration)
   }
 
-  func makeClientConfiguration() -> ClientConnection.Configuration {
-    return .init(
-      target: .hostAndPort("localhost", self.port),
-      eventLoopGroup: self.clientGroup,
-      connectivityStateDelegate: self.stateDelegate,
-      connectionBackoff: ConnectionBackoff(maximumBackoff: 0.1,
-                                           minimumConnectionTimeout: 0.1))
+  func connectionBuilder() -> ClientConnection.Builder {
+    return ClientConnection.insecure(group: self.clientGroup)
+      .withConnectivityStateDelegate(self.stateDelegate)
+      .withConnectionBackoff(maximum: .milliseconds(100))
+      .withConnectionTimeout(minimum: .milliseconds(100))
   }
 
   func testClientConnectionFailsWithNoBackoff() throws {
-    var configuration = self.makeClientConfiguration()
-    configuration.connectionBackoff = nil
-
     let connectionShutdown = self.expectation(description: "client shutdown")
     self.stateDelegate.expectations[.shutdown] = connectionShutdown
-    self.client = ClientConnection(configuration: configuration)
+    self.client = self.connectionBuilder()
+      .withConnectionReestablishment(enabled: false)
+      .connect(host: "localhost", port: self.port)
 
     self.wait(for: [connectionShutdown], timeout: 1.0)
     XCTAssertEqual(self.stateDelegate.states, [.connecting, .shutdown])
@@ -152,7 +149,8 @@ class ClientConnectionBackoffTests: GRPCTestCase {
     self.stateDelegate.expectations[.ready] = connectionReady
 
     // Start the client first.
-    self.client = ClientConnection(configuration: self.makeClientConfiguration())
+    self.client = self.connectionBuilder()
+      .connect(host: "localhost", port: self.port)
 
     self.wait(for: [transientFailure], timeout: 1.0)
     self.stateDelegate.expectations[.transientFailure] = nil
@@ -173,16 +171,14 @@ class ClientConnectionBackoffTests: GRPCTestCase {
     self.server = self.makeServer()
     let server = try self.server.wait()
 
-    // Configure the client backoff to have a short backoff.
-    var configuration = self.makeClientConfiguration()
-    configuration.connectionBackoff!.maximumBackoff = 2.0
-
     // Prepare the delegate so it expects the connection to hit `.ready`.
     let connectionReady = self.expectation(description: "connection ready")
     self.stateDelegate.expectations[.ready] = connectionReady
 
-    // Start the connection.
-    self.client = ClientConnection(configuration: configuration)
+    // Configure the client backoff to have a short backoff.
+    self.client = self.connectionBuilder()
+      .withConnectionBackoff(maximum: .seconds(2))
+      .connect(host: "localhost", port: self.port)
 
     // Wait for the connection to be ready.
     self.wait(for: [connectionReady], timeout: 1.0)

+ 3 - 5
Tests/GRPCTests/GRPCCustomPayloadTests.swift

@@ -36,12 +36,10 @@ class GRPCCustomPayloadTests: GRPCTestCase {
 
     self.server = try! Server.start(configuration: serverConfig).wait()
 
-    let clientConfig: ClientConnection.Configuration = .init(
-      target: .hostAndPort("localhost", server.channel.localAddress!.port!),
-      eventLoopGroup: self.group
-    )
+    let channel = ClientConnection.insecure(group: self.group)
+      .connect(host: "localhost", port: server.channel.localAddress!.port!)
 
-    self.client = AnyServiceClient(channel: ClientConnection(configuration: clientConfig))
+    self.client = AnyServiceClient(channel: channel)
   }
 
   override func tearDown() {

+ 10 - 10
Tests/GRPCTests/GRPCInteroperabilityTests.swift

@@ -25,10 +25,10 @@ class GRPCInsecureInteroperabilityTests: GRPCTestCase {
 
   var serverEventLoopGroup: EventLoopGroup!
   var server: Server!
+  var serverPort: Int!
 
   var clientEventLoopGroup: EventLoopGroup!
   var clientConnection: ClientConnection!
-  var clientDefaults: ClientConnection.Configuration!
 
   override func setUp() {
     super.setUp()
@@ -46,25 +46,21 @@ class GRPCInsecureInteroperabilityTests: GRPCTestCase {
       return
     }
 
+    self.serverPort = serverPort
+
     self.clientEventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
-    self.clientDefaults = makeInteroperabilityTestClientConfiguration(
-      host: "localhost",
-      port: serverPort,
-      eventLoopGroup: self.clientEventLoopGroup,
-      useTLS: self.useTLS
-    )
   }
 
   override func tearDown() {
     XCTAssertNoThrow(try self.clientConnection?.close().wait())
     XCTAssertNoThrow(try self.clientEventLoopGroup.syncShutdownGracefully())
-    self.clientDefaults = nil
     self.clientConnection = nil
     self.clientEventLoopGroup = nil
 
     XCTAssertNoThrow(try self.server.close().wait())
     XCTAssertNoThrow(try self.serverEventLoopGroup.syncShutdownGracefully())
     self.server = nil
+    self.serverPort = nil
     self.serverEventLoopGroup = nil
 
     super.tearDown()
@@ -80,8 +76,12 @@ class GRPCInsecureInteroperabilityTests: GRPCTestCase {
     }
 
     let test = testCase.makeTest()
-    let configuration = test.configure(defaults: self.clientDefaults)
-    self.clientConnection = ClientConnection(configuration: configuration)
+    let builder = makeInteroperabilityTestClientBuilder(
+      group: self.clientEventLoopGroup,
+      useTLS: self.useTLS
+    )
+    test.configure(builder: builder)
+    self.clientConnection = builder.connect(host: "localhost", port: self.serverPort)
     XCTAssertNoThrow(try test.run(using: self.clientConnection), file: file, line: line)
   }
 

+ 2 - 6
Tests/GRPCTests/HeaderNormalizationTests.swift

@@ -101,12 +101,8 @@ class HeaderNormalizationTests: GRPCTestCase {
 
     self.server = try! Server.start(configuration: serverConfig).wait()
 
-    let clientConfig = ClientConnection.Configuration(
-      target: .hostAndPort("localhost", self.server.channel.localAddress!.port!),
-      eventLoopGroup: self.group
-    )
-
-    self.channel = ClientConnection(configuration: clientConfig)
+    self.channel = ClientConnection.insecure(group: self.group)
+      .connect(host: "localhost", port: self.server.channel.localAddress!.port!)
     self.client = Echo_EchoClient(channel: self.channel)
   }
 

+ 6 - 8
docs/basic-tutorial.md

@@ -471,8 +471,9 @@ service. You can see our complete example client code in
 To call service methods, we first need to create a *stub*. All generated Swift
 stubs are *non-blocking/asynchronous*.
 
-First we need to create a gRPC connection for our stub, specifying the server
-address and port we want to connect to:
+First we need to create a gRPC channel for our stub, we're not using TLS so we
+use the `insecure` builder and specify the server address and port we want to
+connect to:
 
 ```swift
 let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
@@ -480,13 +481,10 @@ defer {
   try? group.syncShutdownGracefully()
 }
 
-let configuration = ClientConnection.Configuration(
-  target: .hostAndPort("localhost", port),
-  eventLoopGroup: group
-)
+let channel = ClientConnection.insecure(group: group)
+  .connect(host: "localhost", port: port)
 
-let connection = ClientConnection(configuration: config)
-let client = Routeguide_RouteGuideServiceClient(connection: connection)
+let client = Routeguide_RouteGuideClient(channel: channel)
 ```
 
 #### Calling service methods