TestService.swift 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. /*
  2. * Copyright 2024, 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 Foundation
  17. import GRPCCore
  18. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  19. internal struct TestService: Grpc_Testing_TestService.ServiceProtocol {
  20. internal func unimplementedCall(
  21. request: ServerRequest.Single<Grpc_Testing_TestService.Method.UnimplementedCall.Input>
  22. ) async throws
  23. -> ServerResponse.Single<Grpc_Testing_TestService.Method.UnimplementedCall.Output>
  24. {
  25. throw RPCError(code: .unimplemented, message: "The RPC is not implemented.")
  26. }
  27. /// Server implements `emptyCall` which immediately returns the empty message.
  28. internal func emptyCall(
  29. request: ServerRequest.Single<Grpc_Testing_TestService.Method.EmptyCall.Input>
  30. ) async throws -> ServerResponse.Single<Grpc_Testing_TestService.Method.EmptyCall.Output> {
  31. let message = Grpc_Testing_TestService.Method.EmptyCall.Output()
  32. let (initialMetadata, trailingMetadata) = request.metadata.makeInitialAndTrailingMetadata()
  33. return ServerResponse.Single(
  34. message: message,
  35. metadata: initialMetadata,
  36. trailingMetadata: trailingMetadata
  37. )
  38. }
  39. /// Server implements `unaryCall` which immediately returns a `SimpleResponse` with a payload
  40. /// body of size `SimpleRequest.responseSize` bytes and type as appropriate for the
  41. /// `SimpleRequest.responseType`.
  42. ///
  43. /// If the server does not support the `responseType`, then it should fail the RPC with
  44. /// `INVALID_ARGUMENT`.
  45. internal func unaryCall(
  46. request: ServerRequest.Single<Grpc_Testing_TestService.Method.UnaryCall.Input>
  47. ) async throws -> ServerResponse.Single<Grpc_Testing_TestService.Method.UnaryCall.Output> {
  48. // If the request has a responseStatus set, the server should return that status.
  49. // If the code is an error code, the server will throw an error containing that code
  50. // and the message set in the responseStatus.
  51. // If the code is `ok`, the server will automatically send back an `ok` status.
  52. if request.message.responseStatus.isInitialized {
  53. guard let code = Status.Code(rawValue: Int(request.message.responseStatus.code)) else {
  54. throw RPCError(code: .invalidArgument, message: "The response status code is invalid.")
  55. }
  56. let status = Status(
  57. code: code,
  58. message: request.message.responseStatus.message
  59. )
  60. if let error = RPCError(status: status) {
  61. throw error
  62. }
  63. }
  64. if case .UNRECOGNIZED = request.message.responseType {
  65. throw RPCError(code: .invalidArgument, message: "The response type is not recognized.")
  66. }
  67. let responseMessage = Grpc_Testing_TestService.Method.UnaryCall.Output.with { response in
  68. response.payload = Grpc_Testing_Payload.with { payload in
  69. payload.body = Data(repeating: 0, count: Int(request.message.responseSize))
  70. payload.type = request.message.responseType
  71. }
  72. }
  73. let (initialMetadata, trailingMetadata) = request.metadata.makeInitialAndTrailingMetadata()
  74. return ServerResponse.Single(
  75. message: responseMessage,
  76. metadata: initialMetadata,
  77. trailingMetadata: trailingMetadata
  78. )
  79. }
  80. /// Server gets the default `SimpleRequest` proto as the request. The content of the request is
  81. /// ignored. It returns the `SimpleResponse` proto with the payload set to current timestamp.
  82. /// The timestamp is an integer representing current time with nanosecond resolution. This
  83. /// integer is formated as ASCII decimal in the response. The format is not really important as
  84. /// long as the response payload is different for each request. In addition it adds cache control
  85. /// headers such that the response can be cached by proxies in the response path. Server should
  86. /// be behind a caching proxy for this test to pass. Currently we set the max-age to 60 seconds.
  87. internal func cacheableUnaryCall(
  88. request: ServerRequest.Single<Grpc_Testing_TestService.Method.CacheableUnaryCall.Input>
  89. ) async throws
  90. -> ServerResponse.Single<Grpc_Testing_TestService.Method.CacheableUnaryCall.Output>
  91. {
  92. throw RPCError(code: .unimplemented, message: "The RPC is not implemented.")
  93. }
  94. /// Server implements `streamingOutputCall` by replying, in order, with one
  95. /// `StreamingOutputCallResponse` for each `ResponseParameter`s in `StreamingOutputCallRequest`.
  96. /// Each `StreamingOutputCallResponse` should have a payload body of size `ResponseParameter.size`
  97. /// bytes, as specified by its respective `ResponseParameter`. After sending all responses, it
  98. /// closes with OK.
  99. internal func streamingOutputCall(
  100. request: ServerRequest.Single<
  101. Grpc_Testing_TestService.Method.StreamingOutputCall.Input
  102. >
  103. ) async throws
  104. -> ServerResponse.Stream<Grpc_Testing_TestService.Method.StreamingOutputCall.Output>
  105. {
  106. let (initialMetadata, trailingMetadata) = request.metadata.makeInitialAndTrailingMetadata()
  107. return ServerResponse.Stream(metadata: initialMetadata) { writer in
  108. for responseParameter in request.message.responseParameters {
  109. let response = Grpc_Testing_StreamingOutputCallResponse.with { response in
  110. response.payload = Grpc_Testing_Payload.with { payload in
  111. payload.body = Data(repeating: 0, count: Int(responseParameter.size))
  112. }
  113. }
  114. try await writer.write(response)
  115. // We convert the `intervalUs` value from microseconds to nanoseconds.
  116. try await Task.sleep(nanoseconds: UInt64(responseParameter.intervalUs) * 1000)
  117. }
  118. return trailingMetadata
  119. }
  120. }
  121. /// Server implements `streamingInputCall` which upon half close immediately returns a
  122. /// `StreamingInputCallResponse` where `aggregatedPayloadSize` is the sum of all request payload
  123. /// bodies received.
  124. internal func streamingInputCall(
  125. request: ServerRequest.Stream<Grpc_Testing_TestService.Method.StreamingInputCall.Input>
  126. ) async throws
  127. -> ServerResponse.Single<Grpc_Testing_TestService.Method.StreamingInputCall.Output>
  128. {
  129. var aggregatedPayloadSize = 0
  130. for try await message in request.messages {
  131. aggregatedPayloadSize += message.payload.body.count
  132. }
  133. let responseMessage = Grpc_Testing_TestService.Method.StreamingInputCall.Output.with {
  134. $0.aggregatedPayloadSize = Int32(aggregatedPayloadSize)
  135. }
  136. let (initialMetadata, trailingMetadata) = request.metadata.makeInitialAndTrailingMetadata()
  137. return ServerResponse.Single(
  138. message: responseMessage,
  139. metadata: initialMetadata,
  140. trailingMetadata: trailingMetadata
  141. )
  142. }
  143. /// Server implements `fullDuplexCall` by replying, in order, with one
  144. /// `StreamingOutputCallResponse` for each `ResponseParameter`s in each
  145. /// `StreamingOutputCallRequest`. Each `StreamingOutputCallResponse` should have a payload body
  146. /// of size `ResponseParameter.size` bytes, as specified by its respective `ResponseParameter`s.
  147. /// After receiving half close and sending all responses, it closes with OK.
  148. internal func fullDuplexCall(
  149. request: ServerRequest.Stream<Grpc_Testing_TestService.Method.FullDuplexCall.Input>
  150. ) async throws
  151. -> ServerResponse.Stream<Grpc_Testing_TestService.Method.FullDuplexCall.Output>
  152. {
  153. let (initialMetadata, trailingMetadata) = request.metadata.makeInitialAndTrailingMetadata()
  154. return ServerResponse.Stream(metadata: initialMetadata) { writer in
  155. for try await message in request.messages {
  156. // If a request message has a responseStatus set, the server should return that status.
  157. // If the code is an error code, the server will throw an error containing that code
  158. // and the message set in the responseStatus.
  159. // If the code is `ok`, the server will automatically send back an `ok` status with the response.
  160. if message.responseStatus.isInitialized {
  161. guard let code = Status.Code(rawValue: Int(message.responseStatus.code)) else {
  162. throw RPCError(code: .invalidArgument, message: "The response status code is invalid.")
  163. }
  164. let status = Status(code: code, message: message.responseStatus.message)
  165. if let error = RPCError(status: status) {
  166. throw error
  167. }
  168. }
  169. for responseParameter in message.responseParameters {
  170. let response = Grpc_Testing_StreamingOutputCallResponse.with { response in
  171. response.payload = Grpc_Testing_Payload.with {
  172. $0.body = Data(count: Int(responseParameter.size))
  173. }
  174. }
  175. try await writer.write(response)
  176. }
  177. }
  178. return trailingMetadata
  179. }
  180. }
  181. /// This is not implemented as it is not described in the specification.
  182. ///
  183. /// See: https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md
  184. internal func halfDuplexCall(
  185. request: ServerRequest.Stream<Grpc_Testing_TestService.Method.HalfDuplexCall.Input>
  186. ) async throws
  187. -> ServerResponse.Stream<Grpc_Testing_TestService.Method.HalfDuplexCall.Output>
  188. {
  189. throw RPCError(code: .unimplemented, message: "The RPC is not implemented.")
  190. }
  191. }
  192. extension Metadata {
  193. fileprivate func makeInitialAndTrailingMetadata() -> (Metadata, Metadata) {
  194. var initialMetadata = Metadata()
  195. var trailingMetadata = Metadata()
  196. for value in self[stringValues: "x-grpc-test-echo-initial"] {
  197. initialMetadata.addString(value, forKey: "x-grpc-test-echo-initial")
  198. }
  199. for value in self[binaryValues: "x-grpc-test-echo-trailing-bin"] {
  200. trailingMetadata.addBinary(value, forKey: "x-grpc-test-echo-trailing-bin")
  201. }
  202. return (initialMetadata, trailingMetadata)
  203. }
  204. }