ServerErrorDelegateTests.swift 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  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 EchoImplementation
  17. import EchoModel
  18. import Foundation
  19. @testable import GRPC
  20. import Logging
  21. import NIO
  22. import NIOHPACK
  23. import NIOHTTP2
  24. import XCTest
  25. private class ServerErrorDelegateMock: ServerErrorDelegate {
  26. private let transformLibraryErrorHandler: (Error) -> (GRPCStatusAndTrailers?)
  27. init(transformLibraryErrorHandler: @escaping ((Error) -> (GRPCStatusAndTrailers?))) {
  28. self.transformLibraryErrorHandler = transformLibraryErrorHandler
  29. }
  30. func transformLibraryError(_ error: Error) -> GRPCStatusAndTrailers? {
  31. return self.transformLibraryErrorHandler(error)
  32. }
  33. }
  34. class ServerErrorDelegateTests: GRPCTestCase {
  35. private var channel: EmbeddedChannel!
  36. private var errorDelegate: ServerErrorDelegate!
  37. override func tearDown() {
  38. XCTAssertNoThrow(try self.channel.finish(acceptAlreadyClosed: true))
  39. super.tearDown()
  40. }
  41. func testTransformLibraryError_whenTransformingErrorToStatus_unary() throws {
  42. try self.testTransformLibraryError_whenTransformingErrorToStatus(uri: "/echo.Echo/Get")
  43. }
  44. func testTransformLibraryError_whenTransformingErrorToStatus_clientStreaming() throws {
  45. try self.testTransformLibraryError_whenTransformingErrorToStatus(uri: "/echo.Echo/Collect")
  46. }
  47. func testTransformLibraryError_whenTransformingErrorToStatus_serverStreaming() throws {
  48. try self.testTransformLibraryError_whenTransformingErrorToStatus(uri: "/echo.Echo/Expand")
  49. }
  50. func testTransformLibraryError_whenTransformingErrorToStatus_bidirectionalStreaming() throws {
  51. try self.testTransformLibraryError_whenTransformingErrorToStatus(uri: "/echo.Echo/Update")
  52. }
  53. private func testTransformLibraryError_whenTransformingErrorToStatus(uri: String) throws {
  54. self.setupChannelAndDelegate { _ in
  55. GRPCStatusAndTrailers(status: .init(code: .notFound, message: "some error"))
  56. }
  57. let requestHeaders: HPACKHeaders = [
  58. ":method": "POST",
  59. ":path": uri,
  60. "content-type": "application/grpc",
  61. ]
  62. let headersPayload: HTTP2Frame.FramePayload = .headers(.init(headers: requestHeaders))
  63. XCTAssertNoThrow(try self.channel.writeInbound(headersPayload))
  64. self.channel.pipeline.fireErrorCaught(GRPCStatus(code: .aborted, message: nil))
  65. // Read out the response headers.
  66. XCTAssertNoThrow(try self.channel.readOutbound(as: HTTP2Frame.FramePayload.self))
  67. let end = try self.channel.readOutbound(as: HTTP2Frame.FramePayload.self)
  68. guard case let .some(.headers(trailers)) = end else {
  69. XCTFail("Expected headers but got \(end.debugDescription)")
  70. return
  71. }
  72. XCTAssertEqual(trailers.headers.first(name: "grpc-status"), "5")
  73. XCTAssertEqual(trailers.headers.first(name: "grpc-message"), "some error")
  74. XCTAssertTrue(trailers.endStream)
  75. }
  76. func testTransformLibraryError_whenTransformingErrorToStatusAndMetadata_unary() throws {
  77. try self
  78. .testTransformLibraryError_whenTransformingErrorToStatusAndMetadata(uri: "/echo.Echo/Get")
  79. }
  80. func testTransformLibraryError_whenTransformingErrorToStatusAndMetadata_clientStreaming() throws {
  81. try self
  82. .testTransformLibraryError_whenTransformingErrorToStatusAndMetadata(uri: "/echo.Echo/Collect")
  83. }
  84. func testTransformLibraryError_whenTransformingErrorToStatusAndMetadata_serverStreaming() throws {
  85. try self
  86. .testTransformLibraryError_whenTransformingErrorToStatusAndMetadata(uri: "/echo.Echo/Expand")
  87. }
  88. func testTransformLibraryError_whenTransformingErrorToStatusAndMetadata_bidirectionalStreaming(
  89. ) throws {
  90. try self
  91. .testTransformLibraryError_whenTransformingErrorToStatusAndMetadata(uri: "/echo.Echo/Update")
  92. }
  93. private func testTransformLibraryError_whenTransformingErrorToStatusAndMetadata(
  94. uri: String,
  95. line: UInt = #line
  96. ) throws {
  97. self.setupChannelAndDelegate { _ in
  98. GRPCStatusAndTrailers(
  99. status: .init(code: .notFound, message: "some error"),
  100. trailers: ["some-metadata": "test"]
  101. )
  102. }
  103. let requestHeaders: HPACKHeaders = [
  104. ":method": "POST",
  105. ":path": uri,
  106. "content-type": "application/grpc",
  107. ]
  108. let headersPayload: HTTP2Frame.FramePayload = .headers(.init(headers: requestHeaders))
  109. XCTAssertNoThrow(try self.channel.writeInbound(headersPayload))
  110. self.channel.pipeline.fireErrorCaught(GRPCStatus(code: .aborted, message: nil))
  111. // Read out the response headers.
  112. XCTAssertNoThrow(try self.channel.readOutbound(as: HTTP2Frame.FramePayload.self))
  113. let end = try self.channel.readOutbound(as: HTTP2Frame.FramePayload.self)
  114. guard case let .some(.headers(trailers)) = end else {
  115. XCTFail("Expected headers but got \(end.debugDescription)")
  116. return
  117. }
  118. XCTAssertEqual(trailers.headers.first(name: "grpc-status"), "5", line: line)
  119. XCTAssertEqual(trailers.headers.first(name: "grpc-message"), "some error", line: line)
  120. XCTAssertEqual(trailers.headers.first(name: "some-metadata"), "test", line: line)
  121. XCTAssertTrue(trailers.endStream)
  122. }
  123. private func setupChannelAndDelegate(
  124. transformLibraryErrorHandler: @escaping (Error) -> GRPCStatusAndTrailers?
  125. ) {
  126. let provider = EchoProvider()
  127. self.errorDelegate = ServerErrorDelegateMock(
  128. transformLibraryErrorHandler: transformLibraryErrorHandler
  129. )
  130. let handler = HTTP2ToRawGRPCServerCodec(
  131. servicesByName: [provider.serviceName: provider],
  132. encoding: .disabled,
  133. errorDelegate: self.errorDelegate,
  134. normalizeHeaders: true,
  135. logger: self.logger
  136. )
  137. self.channel = EmbeddedChannel(handler: handler)
  138. }
  139. }