GRPCServerRequestRoutingHandlerTests.swift 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. /*
  2. * Copyright 2019, 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 EchoImplementation
  17. import EchoModel
  18. import Foundation
  19. @testable import GRPC
  20. import Logging
  21. import NIO
  22. import NIOHTTP1
  23. import XCTest
  24. class GRPCServerRequestRoutingHandlerTests: GRPCTestCase {
  25. var channel: EmbeddedChannel!
  26. override func setUp() {
  27. super.setUp()
  28. let provider = EchoProvider()
  29. let handler = GRPCServerRequestRoutingHandler(
  30. servicesByName: [provider.serviceName: provider],
  31. encoding: .disabled,
  32. errorDelegate: nil,
  33. logger: self.logger
  34. )
  35. self.channel = EmbeddedChannel(handler: handler)
  36. }
  37. override func tearDown() {
  38. XCTAssertNoThrow(try self.channel.finish())
  39. super.tearDown()
  40. }
  41. func testInvalidGRPCContentTypeReturnsUnsupportedMediaType() throws {
  42. let requestHead = HTTPRequestHead(
  43. version: .init(major: 2, minor: 0),
  44. method: .POST,
  45. uri: "/echo.Echo/Get",
  46. headers: ["content-type": "not-grpc"]
  47. )
  48. XCTAssertNoThrow(try self.channel.writeInbound(HTTPServerRequestPart.head(requestHead)))
  49. let firstResponsePart = try self.channel.readOutbound(as: HTTPServerResponsePart.self)
  50. switch firstResponsePart {
  51. case let .some(.head(head)):
  52. XCTAssertEqual(head.status, .unsupportedMediaType)
  53. default:
  54. XCTFail("Unexpected response part: \(String(describing: firstResponsePart))")
  55. }
  56. let secondResponsePart = try self.channel.readOutbound(as: HTTPServerResponsePart.self)
  57. switch secondResponsePart {
  58. case .some(.end(nil)):
  59. ()
  60. default:
  61. XCTFail("Unexpected response part: \(String(describing: secondResponsePart))")
  62. }
  63. }
  64. func testUnimplementedMethodReturnsUnimplementedStatus() throws {
  65. let requestHead = HTTPRequestHead(
  66. version: .init(major: 2, minor: 0),
  67. method: .POST,
  68. uri: "/foo/Bar",
  69. headers: ["content-type": "application/grpc"]
  70. )
  71. XCTAssertNoThrow(try self.channel.writeInbound(HTTPServerRequestPart.head(requestHead)))
  72. let firstResponsePart = try self.channel.readOutbound(as: HTTPServerResponsePart.self)
  73. switch firstResponsePart {
  74. case let .some(.head(head)):
  75. XCTAssertEqual(head.status, .ok)
  76. XCTAssertEqual(
  77. head.headers.first(name: "grpc-status"),
  78. "\(GRPCStatus.Code.unimplemented.rawValue)"
  79. )
  80. default:
  81. XCTFail("Unexpected response part: \(String(describing: firstResponsePart))")
  82. }
  83. let secondResponsePart = try self.channel.readOutbound(as: HTTPServerResponsePart.self)
  84. switch secondResponsePart {
  85. case .some(.end(nil)):
  86. ()
  87. default:
  88. XCTFail("Unexpected response part: \(String(describing: secondResponsePart))")
  89. }
  90. }
  91. func testImplementedMethodReconfiguresPipeline() throws {
  92. let requestHead = HTTPRequestHead(
  93. version: .init(major: 2, minor: 0),
  94. method: .POST,
  95. uri: "/echo.Echo/Get",
  96. headers: ["content-type": "application/grpc"]
  97. )
  98. XCTAssertNoThrow(try self.channel.writeInbound(HTTPServerRequestPart.head(requestHead)))
  99. // The router should be removed from the pipeline.
  100. let router = self.channel.pipeline.handler(type: GRPCServerRequestRoutingHandler.self)
  101. XCTAssertThrowsError(try router.wait())
  102. // There should now be a unary call handler.
  103. let unary = self.channel.pipeline.handler(
  104. type: UnaryCallHandler<
  105. ProtobufDeserializer<Echo_EchoRequest>,
  106. ProtobufSerializer<Echo_EchoResponse>
  107. >.self
  108. )
  109. XCTAssertNoThrow(try unary.wait())
  110. }
  111. func testSplitPathNormal() {
  112. let path = "/server/method"
  113. let parsedPath = GRPCServerRequestRoutingHandler.CallPath(requestURI: path)
  114. let splitPath = path.split(separator: "/")
  115. XCTAssertEqual(splitPath[0], String.SubSequence(parsedPath!.service))
  116. XCTAssertEqual(splitPath[1], String.SubSequence(parsedPath!.method))
  117. }
  118. func testSplitPathTooShort() {
  119. let path = "/badPath"
  120. let parsedPath = GRPCServerRequestRoutingHandler.CallPath(requestURI: path)
  121. XCTAssertNil(parsedPath)
  122. }
  123. func testSplitPathTooLong() {
  124. let path = "/server/method/discard"
  125. let parsedPath = GRPCServerRequestRoutingHandler.CallPath(requestURI: path)
  126. let splitPath = path.split(separator: "/")
  127. XCTAssertEqual(splitPath[0], String.SubSequence(parsedPath!.service))
  128. XCTAssertEqual(splitPath[1], String.SubSequence(parsedPath!.method))
  129. }
  130. func testTrimPrefixEmpty() {
  131. var toSplit = "".utf8[...]
  132. let head = toSplit.trimPrefix(to: UInt8(ascii: "/"))
  133. XCTAssertNil(head)
  134. XCTAssertEqual(toSplit.count, 0)
  135. }
  136. func testTrimPrefixAll() {
  137. let source = "words"
  138. var toSplit = source.utf8[...]
  139. let head = toSplit.trimPrefix(to: UInt8(ascii: "/"))
  140. XCTAssertEqual(head?.count, source.utf8.count)
  141. XCTAssertEqual(toSplit.count, 0)
  142. }
  143. func testTrimPrefixAndRest() {
  144. let source = "words/moreWords"
  145. var toSplit = source.utf8[...]
  146. let head = toSplit.trimPrefix(to: UInt8(ascii: "/"))
  147. XCTAssertEqual(head?.count, "words".utf8.count)
  148. XCTAssertEqual(toSplit.count, "moreWords".utf8.count)
  149. }
  150. }