/unix-domain-socket/src/main/scala/akka/stream/alpakka/unixdomainsocket/scaladsl/UnixDomainSocket.scala

https://github.com/akka/alpakka · Scala · 177 lines · 55 code · 21 blank · 101 comment · 0 complexity · 5a1e97daa476ec4969896b4c7e6bb89a MD5 · raw file

  1. /*
  2. * Copyright (C) 2016-2020 Lightbend Inc. <https://www.lightbend.com>
  3. */
  4. package akka.stream.alpakka.unixdomainsocket
  5. package scaladsl
  6. import java.nio.file.Path
  7. import akka.NotUsed
  8. import akka.actor.{ClassicActorSystemProvider, ExtendedActorSystem, Extension, ExtensionId, ExtensionIdProvider}
  9. import akka.stream._
  10. import akka.stream.alpakka.unixdomainsocket.impl.UnixDomainSocketImpl
  11. import akka.stream.scaladsl.{Flow, Keep, Sink, Source}
  12. import akka.util.ByteString
  13. import scala.concurrent.Future
  14. import scala.concurrent.duration.Duration
  15. object UnixDomainSocket extends ExtensionId[UnixDomainSocket] with ExtensionIdProvider {
  16. /**
  17. * Get the UnixDomainSocket extension with the classic or new actors API.
  18. */
  19. def apply()(implicit system: ClassicActorSystemProvider): UnixDomainSocket = super.apply(system)
  20. /**
  21. * Get the UnixDomainSocket extension with the classic actors API.
  22. */
  23. override def apply(system: akka.actor.ActorSystem): UnixDomainSocket = super.apply(system)
  24. override def createExtension(system: ExtendedActorSystem) =
  25. new UnixDomainSocket(system)
  26. override def lookup(): ExtensionId[_ <: Extension] =
  27. UnixDomainSocket
  28. /**
  29. * * Represents a successful server binding.
  30. */
  31. final case class ServerBinding(localAddress: UnixSocketAddress)(private val unbindAction: () => Future[Unit]) {
  32. def unbind(): Future[Unit] = unbindAction()
  33. }
  34. /**
  35. * Represents an accepted incoming connection.
  36. */
  37. final case class IncomingConnection(localAddress: UnixSocketAddress,
  38. remoteAddress: UnixSocketAddress,
  39. flow: Flow[ByteString, ByteString, NotUsed]) {
  40. /**
  41. * Handles the connection using the given flow, which is materialized exactly once and the respective
  42. * materialized instance is returned.
  43. *
  44. * Convenience shortcut for: `flow.join(handler).run()`.
  45. */
  46. def handleWith[Mat](handler: Flow[ByteString, ByteString, Mat])(implicit materializer: Materializer): Mat =
  47. flow.joinMat(handler)(Keep.right).run()
  48. }
  49. /**
  50. * Represents a prospective outgoing Unix Domain Socket connection.
  51. */
  52. final case class OutgoingConnection(remoteAddress: UnixSocketAddress, localAddress: UnixSocketAddress)
  53. }
  54. /**
  55. * Provides Unix Domain Socket functionality to Akka Streams with an interface similar to Akka's Tcp class.
  56. */
  57. final class UnixDomainSocket(system: ExtendedActorSystem) extends UnixDomainSocketImpl(system) {
  58. import UnixDomainSocket._
  59. private implicit val materializer: ActorMaterializer = ActorMaterializer()(system)
  60. /**
  61. * Creates a [[UnixDomainSocket.ServerBinding]] instance which represents a prospective Unix Domain Socket
  62. * server binding on the given `endpoint`.
  63. *
  64. * Please note that the startup of the server is asynchronous, i.e. after materializing the enclosing
  65. * [[akka.stream.scaladsl.RunnableGraph]] the server is not immediately available. Only after the materialized future
  66. * completes is the server ready to accept client connections.
  67. *
  68. * TODO: Support idleTimeout as per Tcp.
  69. *
  70. * @param path The path to listen on
  71. * @param backlog Controls the size of the connection backlog
  72. * @param halfClose
  73. * Controls whether the connection is kept open even after writing has been completed to the accepted
  74. * socket connections.
  75. * If set to true, the connection will implement the socket half-close mechanism, allowing the client to
  76. * write to the connection even after the server has finished writing. The socket is only closed
  77. * after both the client and server finished writing.
  78. * If set to false, the connection will immediately closed once the server closes its write side,
  79. * independently whether the client is still attempting to write. This setting is recommended
  80. * for servers, and therefore it is the default setting.
  81. */
  82. override def bind(path: Path,
  83. backlog: Int = 128,
  84. halfClose: Boolean = false): Source[IncomingConnection, Future[ServerBinding]] =
  85. super.bind(path, backlog, halfClose)
  86. /**
  87. * Creates a [[UnixDomainSocket.ServerBinding]] instance which represents a prospective Unix Socket server binding on the given `endpoint`
  88. * handling the incoming connections using the provided Flow.
  89. *
  90. * Please note that the startup of the server is asynchronous, i.e. after materializing the enclosing
  91. * [[akka.stream.scaladsl.RunnableGraph]] the server is not immediately available. Only after the returned future
  92. * completes is the server ready to accept client connections.
  93. *
  94. * TODO: Support idleTimeout as per Tcp.
  95. *
  96. * @param handler A Flow that represents the server logic
  97. * @param path The path to listen on
  98. * @param backlog Controls the size of the connection backlog
  99. * @param halfClose
  100. * Controls whether the connection is kept open even after writing has been completed to the accepted
  101. * socket connections.
  102. * If set to true, the connection will implement the socket half-close mechanism, allowing the client to
  103. * write to the connection even after the server has finished writing. The socket is only closed
  104. * after both the client and server finished writing.
  105. * If set to false, the connection will immediately closed once the server closes its write side,
  106. * independently whether the client is still attempting to write. This setting is recommended
  107. * for servers, and therefore it is the default setting.
  108. */
  109. def bindAndHandle(handler: Flow[ByteString, ByteString, _],
  110. path: Path,
  111. backlog: Int = 128,
  112. halfClose: Boolean = false): Future[ServerBinding] =
  113. bind(path, backlog, halfClose)
  114. .to(Sink.foreach { conn: IncomingConnection =>
  115. conn.flow.join(handler).run()
  116. })
  117. .run()
  118. /**
  119. * Creates an [[UnixDomainSocket.OutgoingConnection]] instance representing a prospective Unix Domain client connection to the given endpoint.
  120. *
  121. * Note that the ByteString chunk boundaries are not retained across the network,
  122. * to achieve application level chunks you have to introduce explicit framing in your streams,
  123. * for example using the [[akka.stream.scaladsl.Framing]] stages.
  124. *
  125. * TODO: Support idleTimeout as per Tcp.
  126. *
  127. * @param remoteAddress The remote address to connect to
  128. * @param localAddress Optional local address for the connection
  129. * @param halfClose
  130. * Controls whether the connection is kept open even after writing has been completed to the accepted
  131. * socket connections.
  132. * If set to true, the connection will implement the socket half-close mechanism, allowing the server to
  133. * write to the connection even after the client has finished writing. The socket is only closed
  134. * after both the client and server finished writing. This setting is recommended for clients and
  135. * therefore it is the default setting.
  136. * If set to false, the connection will immediately closed once the client closes its write side,
  137. * independently whether the server is still attempting to write.
  138. */
  139. override def outgoingConnection(
  140. remoteAddress: UnixSocketAddress,
  141. localAddress: Option[UnixSocketAddress] = None,
  142. halfClose: Boolean = true,
  143. connectTimeout: Duration = Duration.Inf
  144. ): Flow[ByteString, ByteString, Future[OutgoingConnection]] =
  145. super.outgoingConnection(remoteAddress, localAddress, halfClose, connectTimeout)
  146. /**
  147. * Creates an [[UnixDomainSocket.OutgoingConnection]] without specifying options.
  148. * It represents a prospective Unix Domain client connection to the given endpoint.
  149. *
  150. * Note that the ByteString chunk boundaries are not retained across the network,
  151. * to achieve application level chunks you have to introduce explicit framing in your streams,
  152. * for example using the [[akka.stream.scaladsl.Framing]] stages.
  153. */
  154. def outgoingConnection(path: Path): Flow[ByteString, ByteString, Future[OutgoingConnection]] =
  155. super.outgoingConnection(UnixSocketAddress(path))
  156. }