ZeroLengthWriteTests.swift 8.6 KB

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