Browse Source

Fix crash when deiniting server without starting (#142)

If a GRPCServer is created using the NIO transport, and it's immediately
discarded without starting it, the NIO transport will crash because the
listening address promise won't be fulfilled/will be leaked. This PR
makes sure the promise is failed on `deinit`.
Gus Cairo 5 days ago
parent
commit
1f247d35f3

+ 12 - 0
Sources/GRPCNIOTransportCore/Server/CommonHTTP2ServerTransport.swift

@@ -153,6 +153,18 @@ package final class CommonHTTP2ServerTransport<
     self.transportSpecificContext = transportSpecificContext
   }
 
+  deinit {
+    // Fail the promise if this transport is deallocated without ever being started.
+    self.listeningAddressState.withLock { state in
+      switch state.close() {
+      case .failPromise(let promise, let error):
+        promise.fail(error)
+      case .doNothing:
+        ()
+      }
+    }
+  }
+
   package func listen(
     streamHandler:
       @escaping @Sendable (

+ 12 - 0
Tests/GRPCNIOTransportHTTP2Tests/HTTP2TransportRegressionTests.swift

@@ -138,4 +138,16 @@ struct HTTP2TransportRegressionTests {
       }
     }
   }
+
+  @Test
+  @available(gRPCSwiftNIOTransport 2.2, *)
+  func testNeverStartingServerDoesNotCrash() async throws {
+    let transport = HTTP2ServerTransport.Posix(
+      address: .ipv4(host: "0.0.0.0", port: 5678),
+      transportSecurity: .plaintext
+    )
+
+    // This should not crash
+    _ = GRPCServer(transport: transport, services: [])
+  }
 }