HTTP2ServerTransport+DebugTests.swift 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. /*
  2. * Copyright 2025, gRPC Authors All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import GRPCCore
  17. import GRPCNIOTransportHTTP2
  18. import NIOCore
  19. import Synchronization
  20. import Testing
  21. @Suite("ChannelDebugCallbacks")
  22. struct ChannelDebugCallbackTests {
  23. @Test(arguments: TransportKind.allCases, TransportKind.allCases)
  24. func debugCallbacksAreCalled(serverKind: TransportKind, clientKind: TransportKind) async throws {
  25. // Validates the callbacks are called appropriately by setting up callbacks which increment
  26. // counters and then returning those stats from a gRPC service. The client's interactions with
  27. // the service drive the callbacks.
  28. let stats = DebugCallbackStats()
  29. let serverDebug = HTTP2ServerTransport.Config.ChannelDebuggingCallbacks(
  30. onBindTCPListener: { channel in
  31. stats.tcpListenersBound.add(1, ordering: .sequentiallyConsistent)
  32. return channel.eventLoop.makeSucceededVoidFuture()
  33. },
  34. onAcceptTCPConnection: { channel in
  35. stats.tcpConnectionsAccepted.add(1, ordering: .sequentiallyConsistent)
  36. return channel.eventLoop.makeSucceededVoidFuture()
  37. },
  38. onAcceptHTTP2Stream: { channel in
  39. stats.http2StreamsAccepted.add(1, ordering: .sequentiallyConsistent)
  40. return channel.eventLoop.makeSucceededVoidFuture()
  41. }
  42. )
  43. let clientDebug = HTTP2ClientTransport.Config.ChannelDebuggingCallbacks(
  44. onCreateTCPConnection: { channel in
  45. stats.tcpConnectionsCreated.add(1, ordering: .sequentiallyConsistent)
  46. return channel.eventLoop.makeSucceededVoidFuture()
  47. },
  48. onCreateHTTP2Stream: { channel in
  49. stats.http2StreamsCreated.add(1, ordering: .sequentiallyConsistent)
  50. return channel.eventLoop.makeSucceededVoidFuture()
  51. }
  52. )
  53. // For each server have the client create this many connections.
  54. let connectionsPerServer = 5
  55. // For each connection have the client create this many streams.
  56. let streamsPerConnection = 3
  57. try await withGRPCServer(
  58. transport: self.makeServerTransport(
  59. kind: serverKind,
  60. address: .ipv4(host: "127.0.0.1", port: 0),
  61. debug: serverDebug
  62. ),
  63. services: [StatsService(stats: stats)]
  64. ) { server in
  65. let address = try await server.listeningAddress!.ipv4!
  66. for connectionNumber in 1 ... connectionsPerServer {
  67. try await withGRPCClient(
  68. transport: self.makeClientTransport(
  69. kind: clientKind,
  70. target: .ipv4(host: address.host, port: address.port),
  71. debug: clientDebug
  72. )
  73. ) { client in
  74. let statsClient = StatsClient(wrapping: client)
  75. // Create a few streams per connection.
  76. for streamNumber in 1 ... streamsPerConnection {
  77. let streamCount = (connectionNumber - 1) * streamsPerConnection + streamNumber
  78. let stats = try await statsClient.getStats()
  79. #expect(stats.server.tcpListenersBound == 1)
  80. #expect(stats.server.tcpConnectionsAccepted == connectionNumber)
  81. #expect(stats.server.http2StreamsAccepted == streamCount)
  82. #expect(stats.client.tcpConnectionsCreated == connectionNumber)
  83. #expect(stats.client.http2StreamsCreated == streamCount)
  84. }
  85. }
  86. }
  87. }
  88. }
  89. private func makeServerTransport(
  90. kind: TransportKind,
  91. address: GRPCNIOTransportCore.SocketAddress,
  92. debug: HTTP2ServerTransport.Config.ChannelDebuggingCallbacks
  93. ) -> NIOServerTransport {
  94. switch kind {
  95. case .posix:
  96. return NIOServerTransport(
  97. .http2NIOPosix(
  98. address: address,
  99. transportSecurity: .plaintext,
  100. config: .defaults {
  101. $0.channelDebuggingCallbacks = debug
  102. }
  103. )
  104. )
  105. #if canImport(Network)
  106. case .transportServices:
  107. return NIOServerTransport(
  108. .http2NIOTS(
  109. address: address,
  110. transportSecurity: .plaintext,
  111. config: .defaults {
  112. $0.channelDebuggingCallbacks = debug
  113. }
  114. )
  115. )
  116. #endif
  117. }
  118. }
  119. private func makeClientTransport(
  120. kind: TransportKind,
  121. target: any ResolvableTarget,
  122. debug: HTTP2ClientTransport.Config.ChannelDebuggingCallbacks
  123. ) throws -> NIOClientTransport {
  124. switch kind {
  125. case .posix:
  126. return NIOClientTransport(
  127. try .http2NIOPosix(
  128. target: target,
  129. transportSecurity: .plaintext,
  130. config: .defaults {
  131. $0.channelDebuggingCallbacks = debug
  132. }
  133. )
  134. )
  135. #if canImport(Network)
  136. case .transportServices:
  137. return NIOClientTransport(
  138. try .http2NIOTS(
  139. target: target,
  140. transportSecurity: .plaintext,
  141. config: .defaults {
  142. $0.channelDebuggingCallbacks = debug
  143. }
  144. )
  145. )
  146. #endif
  147. }
  148. }
  149. }