CompressionTests.swift 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  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 GRPC
  17. import EchoImplementation
  18. import EchoModel
  19. import NIO
  20. import NIOHPACK
  21. import XCTest
  22. class MessageCompressionTests: GRPCTestCase {
  23. var group: EventLoopGroup!
  24. var server: Server!
  25. var client: ClientConnection!
  26. var defaultTimeout: TimeInterval = 0.1
  27. var echo: Echo_EchoServiceClient!
  28. override func setUp() {
  29. self.group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
  30. }
  31. override func tearDown() {
  32. XCTAssertNoThrow(try self.client.close().wait())
  33. XCTAssertNoThrow(try self.server.close().wait())
  34. XCTAssertNoThrow(try self.group.syncShutdownGracefully())
  35. }
  36. func setupServer(encoding: Server.Configuration.MessageEncoding) throws {
  37. let configuration = Server.Configuration(
  38. target: .hostAndPort("localhost", 0),
  39. eventLoopGroup: self.group,
  40. serviceProviders: [EchoProvider()],
  41. messageEncoding: encoding
  42. )
  43. self.server = try Server.start(configuration: configuration).wait()
  44. }
  45. func setupClient(encoding: CallOptions.MessageEncoding) {
  46. let configuration = ClientConnection.Configuration(
  47. target: .hostAndPort("localhost", self.server.channel.localAddress!.port!),
  48. eventLoopGroup: self.group
  49. )
  50. self.client = ClientConnection(configuration: configuration)
  51. self.echo = Echo_EchoServiceClient(
  52. channel: self.client,
  53. defaultCallOptions: CallOptions(messageEncoding: encoding)
  54. )
  55. }
  56. func doUnaryRPC() -> UnaryCall<Echo_EchoRequest, Echo_EchoResponse> {
  57. let get = self.echo.get(.with { $0.text = "foo" })
  58. return get
  59. }
  60. func testCompressedRequestsUncompressedResponses() throws {
  61. try self.setupServer(encoding: .none)
  62. self.setupClient(encoding: .init(forRequests: .gzip, acceptableForResponses: [.deflate, .gzip]))
  63. let get = self.echo.get(.with { $0.text = "foo" })
  64. let initialMetadata = self.expectation(description: "received initial metadata")
  65. get.initialMetadata.map {
  66. $0.contains(name: "grpc-encoding")
  67. }.assertEqual(false, fulfill: initialMetadata)
  68. let status = self.expectation(description: "received status")
  69. get.status.map {
  70. $0.code
  71. }.assertEqual(.ok, fulfill: status)
  72. self.wait(for: [initialMetadata, status], timeout: self.defaultTimeout)
  73. }
  74. func testUncompressedRequestsCompressedResponses() throws {
  75. try self.setupServer(encoding: .enabled)
  76. self.setupClient(encoding: .init(forRequests: .none, acceptableForResponses: [.deflate, .gzip]))
  77. let get = self.echo.get(.with { $0.text = "foo" })
  78. let initialMetadata = self.expectation(description: "received initial metadata")
  79. get.initialMetadata.map {
  80. $0.first(name: "grpc-encoding")
  81. }.assertEqual("deflate", fulfill: initialMetadata)
  82. let status = self.expectation(description: "received status")
  83. get.status.map {
  84. $0.code
  85. }.assertEqual(.ok, fulfill: status)
  86. self.wait(for: [initialMetadata, status], timeout: self.defaultTimeout)
  87. }
  88. func testServerCanDecompressNonAdvertisedButSupportedCompression() throws {
  89. // Server should be able to decompress a format it supports but does not advertise. In doing
  90. // so it must also return a "grpc-accept-encoding" header which includes the value it did not
  91. // advertise.
  92. try self.setupServer(encoding: .init(enabled: [.gzip]))
  93. self.setupClient(encoding: .init(forRequests: .deflate, acceptableForResponses: []))
  94. let get = self.echo.get(.with { $0.text = "foo" })
  95. let initialMetadata = self.expectation(description: "received initial metadata")
  96. get.initialMetadata.map {
  97. $0[canonicalForm: "grpc-accept-encoding"]
  98. }.assertEqual(["gzip", "deflate"], fulfill: initialMetadata)
  99. let status = self.expectation(description: "received status")
  100. get.status.map {
  101. $0.code
  102. }.assertEqual(.ok, fulfill: status)
  103. self.wait(for: [initialMetadata, status], timeout: self.defaultTimeout)
  104. }
  105. func testServerCompressesResponseWithDifferentAlgorithmToRequest() throws {
  106. // Server should be able to compress responses with a different method to the client, providing
  107. // the client supports it.
  108. try self.setupServer(encoding: .init(enabled: [.gzip]))
  109. self.setupClient(encoding: .init(forRequests: .deflate, acceptableForResponses: [.deflate, .gzip]))
  110. let get = self.echo.get(.with { $0.text = "foo" })
  111. let initialMetadata = self.expectation(description: "received initial metadata")
  112. get.initialMetadata.map {
  113. $0.first(name: "grpc-encoding")
  114. }.assertEqual("gzip", fulfill: initialMetadata)
  115. let status = self.expectation(description: "received status")
  116. get.status.map {
  117. $0.code
  118. }.assertEqual(.ok, fulfill: status)
  119. self.wait(for: [initialMetadata, status], timeout: self.defaultTimeout)
  120. }
  121. func testCompressedRequestWithCompressionNotSupportedOnServer() throws {
  122. try self.setupServer(encoding: .init(enabled: [.gzip, .deflate]))
  123. // We can't specify a compression we don't support, so we'll specify no compression and then
  124. // send a 'grpc-encoding' with our initial metadata.
  125. self.setupClient(encoding: .init(forRequests: .none, acceptableForResponses: [.deflate, .gzip]))
  126. let headers: HPACKHeaders = ["grpc-encoding": "you-don't-support-this"]
  127. let get = self.echo.get(.with { $0.text = "foo" }, callOptions: CallOptions(customMetadata: headers))
  128. let response = self.expectation(description: "received response")
  129. get.response.assertError(fulfill: response)
  130. let trailers = self.expectation(description: "received trailing metadata")
  131. get.trailingMetadata.map {
  132. $0[canonicalForm: "grpc-accept-encoding"]
  133. }.assertEqual(["gzip", "deflate"], fulfill: trailers)
  134. let status = self.expectation(description: "received status")
  135. get.status.map {
  136. $0.code
  137. }.assertEqual(.unimplemented, fulfill: status)
  138. self.wait(for: [response, trailers, status], timeout: self.defaultTimeout)
  139. }
  140. }