ZeroLengthWriteTests.swift 8.7 KB

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