2
0

main.swift 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  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 ArgumentParser
  17. import Dispatch
  18. import EchoModel
  19. import GRPC
  20. import Logging
  21. import NIO
  22. import NIOExtras
  23. // Create a logger.
  24. let logger = Logger(label: "gRPC PCAP Demo")
  25. // Closing file sinks is blocking, it therefore can't be done on an EventLoop.
  26. let fileSinkCloseQueue = DispatchQueue(label: "io.grpc")
  27. let fileSinkCloseGroup = DispatchGroup()
  28. defer {
  29. // Make sure we wait for all file sinks to be closed before we exit.
  30. fileSinkCloseGroup.wait()
  31. logger.info("Done!")
  32. }
  33. /// Adds a `NIOWritePCAPHandler` to the given channel.
  34. ///
  35. /// A file sink will also be created to write the PCAP to `./channel-{ID}.pcap` where `{ID}` is
  36. /// an identifier created from the given `channel`. The file sink will be closed when the channel
  37. /// closes and will notify the `fileSinkCloseGroup` when it has been closed.
  38. ///
  39. /// - Parameter channel: The channel to add the PCAP handler to.
  40. /// - Returns: An `EventLoopFuture` indicating whether the PCAP handler was successfully added.
  41. func addPCAPHandler(toChannel channel: Channel) -> EventLoopFuture<Void> {
  42. // The debug initializer can be called multiple times. We'll use the object ID of the channel
  43. // to disambiguate between the files.
  44. let channelID = ObjectIdentifier(channel)
  45. let path = "./channel-\(channelID).pcap"
  46. logger.info("Creating fileSink for path '\(path)'")
  47. do {
  48. // Create a file sink.
  49. let fileSink = try NIOWritePCAPHandler.SynchronizedFileSink
  50. .fileSinkWritingToFile(path: path) { error in
  51. logger.error("💥 Failed to write with error '\(error)' for path '\(path)'")
  52. }
  53. logger.info("✅ Successfully created fileSink for path '\(path)'")
  54. // We need to close the file sink when we're done. It can't be closed from the event loop so
  55. // we'll use a dispatch queue instead.
  56. fileSinkCloseGroup.enter()
  57. channel.closeFuture.whenComplete { _ in
  58. fileSinkCloseQueue.async {
  59. do {
  60. try fileSink.syncClose()
  61. } catch {
  62. logger.error("💥 Failed to close fileSink with error '\(error)' for path '\(path)'")
  63. }
  64. }
  65. fileSinkCloseGroup.leave()
  66. }
  67. // Add the handler to the pipeline.
  68. let handler = NIOWritePCAPHandler(mode: .client, fileSink: fileSink.write(buffer:))
  69. // We're not using TLS in this example so ".first" is the right place.
  70. return channel.pipeline.addHandler(handler, position: .first)
  71. } catch {
  72. logger.error("💥 Failed to create fileSink with error '\(error)' for path '\(path)'")
  73. return channel.eventLoop.makeFailedFuture(error)
  74. }
  75. }
  76. struct PCAP: ParsableCommand {
  77. @Option(help: "The port to connect to")
  78. var port = 1234
  79. func run() throws {
  80. // Create an `EventLoopGroup`.
  81. let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
  82. defer {
  83. try! group.syncShutdownGracefully()
  84. }
  85. // Create a channel.
  86. let channel = ClientConnection.insecure(group: group)
  87. // Set the debug initializer: it will add a handler to each created channel to write a PCAP when
  88. // the channel is closed.
  89. .withDebugChannelInitializer(addPCAPHandler(toChannel:))
  90. // We're connecting to our own server here; we'll disable connection re-establishment.
  91. .withConnectionReestablishment(enabled: false)
  92. // Connect!
  93. .connect(host: "localhost", port: self.port)
  94. // Create a client.
  95. let echo = Echo_EchoClient(channel: channel)
  96. // Start an RPC.
  97. let update = echo.update { response in
  98. logger.info("Received response '\(response.text)'")
  99. }
  100. // Send some requests.
  101. for text in ["foo", "bar", "baz", "thud", "grunt", "gorp"] {
  102. update.sendMessage(.with { $0.text = text }).whenSuccess {
  103. logger.info("Sent request '\(text)'")
  104. }
  105. }
  106. // Close the request stream.
  107. update.sendEnd(promise: nil)
  108. // Once the RPC finishes close the connection.
  109. let closed = update.status.flatMap { status -> EventLoopFuture<Void> in
  110. if status.isOk {
  111. logger.info("✅ RPC completed successfully")
  112. } else {
  113. logger.error("💥 RPC failed with status '\(status)'")
  114. }
  115. logger.info("Closing channel")
  116. return channel.close()
  117. }
  118. // Wait for the channel to be closed.
  119. try closed.wait()
  120. }
  121. }
  122. PCAP.main()