Metadata+GRPC.swift 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. /*
  2. * Copyright 2023, 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. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  17. extension Metadata {
  18. @inlinable
  19. var previousRPCAttempts: Int? {
  20. get {
  21. self.firstString(forKey: .previousRPCAttempts).flatMap { Int($0) }
  22. }
  23. set {
  24. if let newValue = newValue {
  25. self.replaceOrAddString(String(describing: newValue), forKey: .previousRPCAttempts)
  26. } else {
  27. self.removeAllValues(forKey: .previousRPCAttempts)
  28. }
  29. }
  30. }
  31. @inlinable
  32. var retryPushback: RetryPushback? {
  33. return self.firstString(forKey: .retryPushbackMs).map {
  34. RetryPushback(milliseconds: $0)
  35. }
  36. }
  37. }
  38. extension Metadata {
  39. @usableFromInline
  40. enum GRPCKey: String, Sendable, Hashable {
  41. case retryPushbackMs = "grpc-retry-pushback-ms"
  42. case previousRPCAttempts = "grpc-previous-rpc-attempts"
  43. }
  44. @inlinable
  45. func firstString(forKey key: GRPCKey) -> String? {
  46. self[stringValues: key.rawValue].first(where: { _ in true })
  47. }
  48. @inlinable
  49. mutating func replaceOrAddString(_ value: String, forKey key: GRPCKey) {
  50. self.replaceOrAddString(value, forKey: key.rawValue)
  51. }
  52. @inlinable
  53. mutating func removeAllValues(forKey key: GRPCKey) {
  54. self.removeAllValues(forKey: key.rawValue)
  55. }
  56. }
  57. extension Metadata {
  58. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  59. @usableFromInline
  60. enum RetryPushback: Hashable, Sendable {
  61. case retryAfter(Duration)
  62. case stopRetrying
  63. @inlinable
  64. init(milliseconds value: String) {
  65. if let milliseconds = Int64(value), milliseconds >= 0 {
  66. let (seconds, remainingMilliseconds) = milliseconds.quotientAndRemainder(dividingBy: 1000)
  67. // 1e18 attoseconds per second
  68. // 1e15 attoseconds per millisecond.
  69. let attoseconds = Int64(remainingMilliseconds) * 1_000_000_000_000_000
  70. self = .retryAfter(Duration(secondsComponent: seconds, attosecondsComponent: attoseconds))
  71. } else {
  72. // Negative or not parseable means stop trying.
  73. // Source: https://github.com/grpc/proposal/blob/master/A6-client-retries.md
  74. self = .stopRetrying
  75. }
  76. }
  77. }
  78. }