ZeroLengthWriteTests.swift 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. /*
  2. * Copyright 2020, 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. #if canImport(NIOSSL)
  17. import Dispatch
  18. import EchoImplementation
  19. import EchoModel
  20. import Foundation
  21. import GRPC
  22. import GRPCSampleData
  23. import NIOCore
  24. import NIOSSL
  25. import NIOTransportServices
  26. import XCTest
  27. final class ZeroLengthWriteTests: GRPCTestCase {
  28. func clientBuilder(
  29. group: EventLoopGroup,
  30. secure: Bool,
  31. debugInitializer: @escaping GRPCChannelInitializer
  32. ) -> ClientConnection.Builder {
  33. if secure {
  34. return ClientConnection.usingTLSBackedByNIOSSL(on: group)
  35. .withTLS(trustRoots: .certificates([SampleCertificate.ca.certificate]))
  36. .withDebugChannelInitializer(debugInitializer)
  37. } else {
  38. return ClientConnection.insecure(group: group)
  39. .withDebugChannelInitializer(debugInitializer)
  40. }
  41. }
  42. func serverBuilder(
  43. group: EventLoopGroup,
  44. secure: Bool,
  45. debugInitializer: @escaping (Channel) -> EventLoopFuture<Void>
  46. ) -> Server.Builder {
  47. if secure {
  48. return Server.usingTLSBackedByNIOSSL(
  49. on: group,
  50. certificateChain: [SampleCertificate.server.certificate],
  51. privateKey: SamplePrivateKey.server
  52. ).withTLS(trustRoots: .certificates([SampleCertificate.ca.certificate]))
  53. .withDebugChannelInitializer(debugInitializer)
  54. } else {
  55. return Server.insecure(group: group)
  56. .withDebugChannelInitializer(debugInitializer)
  57. }
  58. }
  59. func makeServer(
  60. group: EventLoopGroup,
  61. secure: Bool,
  62. debugInitializer: @escaping (Channel) -> EventLoopFuture<Void>
  63. ) throws -> Server {
  64. return try self.serverBuilder(group: group, secure: secure, debugInitializer: debugInitializer)
  65. .withServiceProviders([self.makeEchoProvider()])
  66. .withLogger(self.serverLogger)
  67. .bind(host: "127.0.0.1", port: 0)
  68. .wait()
  69. }
  70. func makeClientConnection(
  71. group: EventLoopGroup,
  72. secure: Bool,
  73. port: Int,
  74. debugInitializer: @escaping GRPCChannelInitializer
  75. ) throws -> ClientConnection {
  76. return self.clientBuilder(group: group, secure: secure, debugInitializer: debugInitializer)
  77. .withBackgroundActivityLogger(self.clientLogger)
  78. .withConnectionReestablishment(enabled: false)
  79. .connect(host: "127.0.0.1", port: port)
  80. }
  81. func makeEchoProvider() -> Echo_EchoProvider { return EchoProvider() }
  82. func makeEchoClient(
  83. group: EventLoopGroup,
  84. secure: Bool,
  85. port: Int,
  86. debugInitializer: @escaping GRPCChannelInitializer
  87. ) throws -> Echo_EchoNIOClient {
  88. return Echo_EchoNIOClient(
  89. channel: try self.makeClientConnection(
  90. group: group,
  91. secure: secure,
  92. port: port,
  93. debugInitializer: debugInitializer
  94. ),
  95. defaultCallOptions: self.callOptionsWithLogger
  96. )
  97. }
  98. func zeroLengthWriteExpectation() -> XCTestExpectation {
  99. let expectation = self.expectation(description: "Expecting zero length write workaround")
  100. expectation.expectedFulfillmentCount = 1
  101. expectation.assertForOverFulfill = true
  102. return expectation
  103. }
  104. func noZeroLengthWriteExpectation() -> XCTestExpectation {
  105. let expectation = self.expectation(description: "Not expecting zero length write workaround")
  106. expectation.expectedFulfillmentCount = 1
  107. expectation.assertForOverFulfill = true
  108. return expectation
  109. }
  110. func debugPipelineExpectation(
  111. _ callback: @escaping (Result<NIOFilterEmptyWritesHandler, Error>) -> Void
  112. ) -> GRPCChannelInitializer {
  113. return { channel in
  114. channel.pipeline.handler(type: NIOFilterEmptyWritesHandler.self).always { result in
  115. callback(result)
  116. }.map { _ in () }.recover { _ in () }
  117. }
  118. }
  119. private func _runTest(
  120. networkPreference: NetworkPreference,
  121. secure: Bool,
  122. clientHandlerCallback: @escaping (Result<NIOFilterEmptyWritesHandler, Error>) -> Void,
  123. serverHandlerCallback: @escaping (Result<NIOFilterEmptyWritesHandler, Error>) -> Void
  124. ) {
  125. // We can only run this test on platforms where the zero-length write workaround _could_ be added.
  126. #if canImport(Network)
  127. guard #available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) else { return }
  128. let group = PlatformSupport.makeEventLoopGroup(
  129. loopCount: 1,
  130. networkPreference: networkPreference
  131. )
  132. let server = try! self.makeServer(
  133. group: group,
  134. secure: secure,
  135. debugInitializer: self.debugPipelineExpectation(serverHandlerCallback)
  136. )
  137. defer {
  138. XCTAssertNoThrow(try server.close().wait())
  139. XCTAssertNoThrow(try group.syncShutdownGracefully())
  140. }
  141. let port = server.channel.localAddress!.port!
  142. let client = try! self.makeEchoClient(
  143. group: group,
  144. secure: secure,
  145. port: port,
  146. debugInitializer: self.debugPipelineExpectation(clientHandlerCallback)
  147. )
  148. defer {
  149. XCTAssertNoThrow(try client.channel.close().wait())
  150. }
  151. // We need to wait here to confirm that the RPC completes. All expectations should have completed by then.
  152. let call = client.get(Echo_EchoRequest(text: "foo bar baz"))
  153. XCTAssertNoThrow(try call.status.wait())
  154. self.waitForExpectations(timeout: 1.0)
  155. #endif
  156. }
  157. func testZeroLengthWriteTestPosixSecure() throws {
  158. // We can only run this test on platforms where the zero-length write workaround _could_ be added.
  159. #if canImport(Network)
  160. guard #available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) else { return }
  161. let serverExpectation = self.noZeroLengthWriteExpectation()
  162. let clientExpectation = self.noZeroLengthWriteExpectation()
  163. self._runTest(
  164. networkPreference: .userDefined(.posix),
  165. secure: true,
  166. clientHandlerCallback: { result in
  167. if case .failure = result {
  168. clientExpectation.fulfill()
  169. }
  170. },
  171. serverHandlerCallback: { result in
  172. if case .failure = result {
  173. serverExpectation.fulfill()
  174. }
  175. }
  176. )
  177. #endif
  178. }
  179. func testZeroLengthWriteTestPosixInsecure() throws {
  180. // We can only run this test on platforms where the zero-length write workaround _could_ be added.
  181. #if canImport(Network)
  182. guard #available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) else { return }
  183. let serverExpectation = self.noZeroLengthWriteExpectation()
  184. let clientExpectation = self.noZeroLengthWriteExpectation()
  185. self._runTest(
  186. networkPreference: .userDefined(.posix),
  187. secure: false,
  188. clientHandlerCallback: { result in
  189. if case .failure = result {
  190. clientExpectation.fulfill()
  191. }
  192. },
  193. serverHandlerCallback: { result in
  194. if case .failure = result {
  195. serverExpectation.fulfill()
  196. }
  197. }
  198. )
  199. #endif
  200. }
  201. func testZeroLengthWriteTestNetworkFrameworkSecure() throws {
  202. // We can only run this test on platforms where the zero-length write workaround _could_ be added.
  203. #if canImport(Network)
  204. guard #available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) else { return }
  205. let serverExpectation = self.noZeroLengthWriteExpectation()
  206. let clientExpectation = self.noZeroLengthWriteExpectation()
  207. self._runTest(
  208. networkPreference: .userDefined(.networkFramework),
  209. secure: true,
  210. clientHandlerCallback: { result in
  211. if case .failure = result {
  212. clientExpectation.fulfill()
  213. }
  214. },
  215. serverHandlerCallback: { result in
  216. if case .failure = result {
  217. serverExpectation.fulfill()
  218. }
  219. }
  220. )
  221. #endif
  222. }
  223. func testZeroLengthWriteTestNetworkFrameworkInsecure() throws {
  224. // We can only run this test on platforms where the zero-length write workaround _could_ be added.
  225. #if canImport(Network)
  226. guard #available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) else { return }
  227. let serverExpectation = self.zeroLengthWriteExpectation()
  228. let clientExpectation = self.zeroLengthWriteExpectation()
  229. self._runTest(
  230. networkPreference: .userDefined(.networkFramework),
  231. secure: false,
  232. clientHandlerCallback: { result in
  233. if case .success = result {
  234. clientExpectation.fulfill()
  235. }
  236. },
  237. serverHandlerCallback: { result in
  238. if case .success = result {
  239. serverExpectation.fulfill()
  240. }
  241. }
  242. )
  243. #endif
  244. }
  245. }
  246. #endif // canImport(NIOSSL)