ZeroLengthWriteTests.swift 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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 (Channel) -> EventLoopFuture<Void>
  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: "localhost", port: 0)
  68. .wait()
  69. }
  70. func makeClientConnection(
  71. group: EventLoopGroup,
  72. secure: Bool,
  73. port: Int,
  74. debugInitializer: @escaping (Channel) -> EventLoopFuture<Void>
  75. ) throws -> ClientConnection {
  76. return self.clientBuilder(group: group, secure: secure, debugInitializer: debugInitializer)
  77. .withBackgroundActivityLogger(self.clientLogger)
  78. .connect(host: "localhost", port: port)
  79. }
  80. func makeEchoProvider() -> Echo_EchoProvider { return EchoProvider() }
  81. func makeEchoClient(
  82. group: EventLoopGroup,
  83. secure: Bool,
  84. port: Int,
  85. debugInitializer: @escaping (Channel) -> EventLoopFuture<Void>
  86. ) throws -> Echo_EchoClient {
  87. return Echo_EchoClient(
  88. channel: try self.makeClientConnection(
  89. group: group,
  90. secure: secure,
  91. port: port,
  92. debugInitializer: debugInitializer
  93. ),
  94. defaultCallOptions: self.callOptionsWithLogger
  95. )
  96. }
  97. func zeroLengthWriteExpectation() -> XCTestExpectation {
  98. let expectation = self.expectation(description: "Expecting zero length write workaround")
  99. expectation.expectedFulfillmentCount = 1
  100. expectation.assertForOverFulfill = true
  101. return expectation
  102. }
  103. func noZeroLengthWriteExpectation() -> XCTestExpectation {
  104. let expectation = self.expectation(description: "Not expecting zero length write workaround")
  105. expectation.expectedFulfillmentCount = 1
  106. expectation.assertForOverFulfill = true
  107. return expectation
  108. }
  109. func debugPipelineExpectation(_ callback: @escaping (Result<NIOFilterEmptyWritesHandler, Error>)
  110. -> Void) -> (Channel) -> EventLoopFuture<Void> {
  111. return { channel in
  112. channel.pipeline.handler(type: NIOFilterEmptyWritesHandler.self).always { result in
  113. callback(result)
  114. }.map { _ in () }.recover { _ in () }
  115. }
  116. }
  117. private func _runTest(
  118. networkPreference: NetworkPreference,
  119. secure: Bool,
  120. clientHandlerCallback: @escaping (Result<NIOFilterEmptyWritesHandler, Error>) -> Void,
  121. serverHandlerCallback: @escaping (Result<NIOFilterEmptyWritesHandler, Error>) -> Void
  122. ) {
  123. // We can only run this test on platforms where the zero-length write workaround _could_ be added.
  124. #if canImport(Network)
  125. guard #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) else { return }
  126. let group = PlatformSupport.makeEventLoopGroup(
  127. loopCount: 1,
  128. networkPreference: networkPreference
  129. )
  130. let server = try! self.makeServer(
  131. group: group,
  132. secure: secure,
  133. debugInitializer: self.debugPipelineExpectation(serverHandlerCallback)
  134. )
  135. defer {
  136. XCTAssertNoThrow(try server.close().wait())
  137. XCTAssertNoThrow(try group.syncShutdownGracefully())
  138. }
  139. let port = server.channel.localAddress!.port!
  140. let client = try! self.makeEchoClient(
  141. group: group,
  142. secure: secure,
  143. port: port,
  144. debugInitializer: self.debugPipelineExpectation(clientHandlerCallback)
  145. )
  146. defer {
  147. XCTAssertNoThrow(try client.channel.close().wait())
  148. }
  149. // We need to wait here to confirm that the RPC completes. All expectations should have completed by then.
  150. let call = client.get(Echo_EchoRequest(text: "foo bar baz"))
  151. XCTAssertNoThrow(try call.status.wait())
  152. self.waitForExpectations(timeout: 1.0)
  153. #endif
  154. }
  155. func testZeroLengthWriteTestPosixSecure() throws {
  156. // We can only run this test on platforms where the zero-length write workaround _could_ be added.
  157. #if canImport(Network)
  158. guard #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) else { return }
  159. let serverExpectation = self.noZeroLengthWriteExpectation()
  160. let clientExpectation = self.noZeroLengthWriteExpectation()
  161. self._runTest(
  162. networkPreference: .userDefined(.posix),
  163. secure: true,
  164. clientHandlerCallback: { result in
  165. if case .failure = result {
  166. clientExpectation.fulfill()
  167. }
  168. },
  169. serverHandlerCallback: { result in
  170. if case .failure = result {
  171. serverExpectation.fulfill()
  172. }
  173. }
  174. )
  175. #endif
  176. }
  177. func testZeroLengthWriteTestPosixInsecure() throws {
  178. // We can only run this test on platforms where the zero-length write workaround _could_ be added.
  179. #if canImport(Network)
  180. guard #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) else { return }
  181. let serverExpectation = self.noZeroLengthWriteExpectation()
  182. let clientExpectation = self.noZeroLengthWriteExpectation()
  183. self._runTest(
  184. networkPreference: .userDefined(.posix),
  185. secure: false,
  186. clientHandlerCallback: { result in
  187. if case .failure = result {
  188. clientExpectation.fulfill()
  189. }
  190. },
  191. serverHandlerCallback: { result in
  192. if case .failure = result {
  193. serverExpectation.fulfill()
  194. }
  195. }
  196. )
  197. #endif
  198. }
  199. func testZeroLengthWriteTestNetworkFrameworkSecure() throws {
  200. // We can only run this test on platforms where the zero-length write workaround _could_ be added.
  201. #if canImport(Network)
  202. guard #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) else { return }
  203. let serverExpectation = self.noZeroLengthWriteExpectation()
  204. let clientExpectation = self.noZeroLengthWriteExpectation()
  205. self._runTest(
  206. networkPreference: .userDefined(.networkFramework),
  207. secure: true,
  208. clientHandlerCallback: { result in
  209. if case .failure = result {
  210. clientExpectation.fulfill()
  211. }
  212. },
  213. serverHandlerCallback: { result in
  214. if case .failure = result {
  215. serverExpectation.fulfill()
  216. }
  217. }
  218. )
  219. #endif
  220. }
  221. func testZeroLengthWriteTestNetworkFrameworkInsecure() throws {
  222. // We can only run this test on platforms where the zero-length write workaround _could_ be added.
  223. #if canImport(Network)
  224. guard #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) else { return }
  225. let serverExpectation = self.zeroLengthWriteExpectation()
  226. let clientExpectation = self.zeroLengthWriteExpectation()
  227. self._runTest(
  228. networkPreference: .userDefined(.networkFramework),
  229. secure: false,
  230. clientHandlerCallback: { result in
  231. if case .success = result {
  232. clientExpectation.fulfill()
  233. }
  234. },
  235. serverHandlerCallback: { result in
  236. if case .success = result {
  237. serverExpectation.fulfill()
  238. }
  239. }
  240. )
  241. #endif
  242. }
  243. }
  244. #endif // canImport(NIOSSL)