2
0

TestService.swift 9.9 KB

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