PageRenderTime 64ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/akka-stream/src/main/scala/akka/stream/scaladsl/Tcp.scala

https://github.com/bwmcadams/akka
Scala | 185 lines | 80 code | 22 blank | 83 comment | 0 complexity | cd6a160f248c7e15b44c2e60df50bf8c MD5 | raw file
  1. /**
  2. * Copyright (C) 2009-2016 Typesafe Inc. <http://www.typesafe.com>
  3. */
  4. package akka.stream.scaladsl
  5. import java.net.InetSocketAddress
  6. import akka.NotUsed
  7. import akka.actor._
  8. import akka.io.Inet.SocketOption
  9. import akka.io.{ IO, Tcp IoTcp }
  10. import akka.stream._
  11. import akka.stream.impl.fusing.GraphStages.detacher
  12. import akka.stream.impl.io.{ ConnectionSourceStage, OutgoingConnectionStage }
  13. import akka.util.ByteString
  14. import scala.collection.immutable
  15. import scala.concurrent.Future
  16. import scala.concurrent.duration.{ Duration, FiniteDuration }
  17. object Tcp extends ExtensionId[Tcp] with ExtensionIdProvider {
  18. /**
  19. * * Represents a successful TCP server binding.
  20. */
  21. final case class ServerBinding(localAddress: InetSocketAddress)(private val unbindAction: () Future[Unit]) {
  22. def unbind(): Future[Unit] = unbindAction()
  23. }
  24. /**
  25. * Represents an accepted incoming TCP connection.
  26. */
  27. final case class IncomingConnection(
  28. localAddress: InetSocketAddress,
  29. remoteAddress: InetSocketAddress,
  30. flow: Flow[ByteString, ByteString, NotUsed]) {
  31. /**
  32. * Handles the connection using the given flow, which is materialized exactly once and the respective
  33. * materialized instance is returned.
  34. *
  35. * Convenience shortcut for: `flow.join(handler).run()`.
  36. */
  37. def handleWith[Mat](handler: Flow[ByteString, ByteString, Mat])(implicit materializer: Materializer): Mat =
  38. flow.joinMat(handler)(Keep.right).run()
  39. }
  40. /**
  41. * Represents a prospective outgoing TCP connection.
  42. */
  43. final case class OutgoingConnection(remoteAddress: InetSocketAddress, localAddress: InetSocketAddress)
  44. def apply()(implicit system: ActorSystem): Tcp = super.apply(system)
  45. override def get(system: ActorSystem): Tcp = super.get(system)
  46. def lookup() = Tcp
  47. def createExtension(system: ExtendedActorSystem): Tcp = new Tcp(system)
  48. }
  49. final class Tcp(system: ExtendedActorSystem) extends akka.actor.Extension {
  50. import Tcp._
  51. // TODO maybe this should be a new setting, like `akka.stream.tcp.bind.timeout` / `shutdown-timeout` instead?
  52. val bindShutdownTimeout = ActorMaterializer()(system).settings.subscriptionTimeoutSettings.timeout
  53. /**
  54. * Creates a [[Tcp.ServerBinding]] instance which represents a prospective TCP server binding on the given `endpoint`.
  55. *
  56. * Please note that the startup of the server is asynchronous, i.e. after materializing the enclosing
  57. * [[akka.stream.scaladsl.RunnableGraph]] the server is not immediately available. Only after the materialized future
  58. * completes is the server ready to accept client connections.
  59. *
  60. * @param interface The interface to listen on
  61. * @param port The port to listen on
  62. * @param backlog Controls the size of the connection backlog
  63. * @param options TCP options for the connections, see [[akka.io.Tcp]] for details
  64. * @param halfClose
  65. * Controls whether the connection is kept open even after writing has been completed to the accepted
  66. * TCP connections.
  67. * If set to true, the connection will implement the TCP half-close mechanism, allowing the client to
  68. * write to the connection even after the server has finished writing. The TCP socket is only closed
  69. * after both the client and server finished writing.
  70. * If set to false, the connection will immediately closed once the server closes its write side,
  71. * independently whether the client is still attempting to write. This setting is recommended
  72. * for servers, and therefore it is the default setting.
  73. */
  74. def bind(interface: String,
  75. port: Int,
  76. backlog: Int = 100,
  77. options: immutable.Traversable[SocketOption] = Nil,
  78. halfClose: Boolean = false,
  79. idleTimeout: Duration = Duration.Inf): Source[IncomingConnection, Future[ServerBinding]] =
  80. Source.fromGraph(new ConnectionSourceStage(
  81. IO(IoTcp)(system),
  82. new InetSocketAddress(interface, port),
  83. backlog,
  84. options,
  85. halfClose,
  86. idleTimeout,
  87. bindShutdownTimeout))
  88. /**
  89. * Creates a [[Tcp.ServerBinding]] instance which represents a prospective TCP server binding on the given `endpoint`
  90. * handling the incoming connections using the provided Flow.
  91. *
  92. * Please note that the startup of the server is asynchronous, i.e. after materializing the enclosing
  93. * [[akka.stream.scaladsl.RunnableGraph]] the server is not immediately available. Only after the returned future
  94. * completes is the server ready to accept client connections.
  95. *
  96. * @param handler A Flow that represents the server logic
  97. * @param interface The interface to listen on
  98. * @param port The port to listen on
  99. * @param backlog Controls the size of the connection backlog
  100. * @param options TCP options for the connections, see [[akka.io.Tcp]] for details
  101. * @param halfClose
  102. * Controls whether the connection is kept open even after writing has been completed to the accepted
  103. * TCP connections.
  104. * If set to true, the connection will implement the TCP half-close mechanism, allowing the client to
  105. * write to the connection even after the server has finished writing. The TCP socket is only closed
  106. * after both the client and server finished writing.
  107. * If set to false, the connection will immediately closed once the server closes its write side,
  108. * independently whether the client is still attempting to write. This setting is recommended
  109. * for servers, and therefore it is the default setting.
  110. */
  111. def bindAndHandle(
  112. handler: Flow[ByteString, ByteString, _],
  113. interface: String,
  114. port: Int,
  115. backlog: Int = 100,
  116. options: immutable.Traversable[SocketOption] = Nil,
  117. halfClose: Boolean = false,
  118. idleTimeout: Duration = Duration.Inf)(implicit m: Materializer): Future[ServerBinding] = {
  119. bind(interface, port, backlog, options, halfClose, idleTimeout).to(Sink.foreach { conn: IncomingConnection
  120. conn.flow.join(handler).run()
  121. }).run()
  122. }
  123. /**
  124. * Creates an [[Tcp.OutgoingConnection]] instance representing a prospective TCP client connection to the given endpoint.
  125. *
  126. * @param remoteAddress The remote address to connect to
  127. * @param localAddress Optional local address for the connection
  128. * @param options TCP options for the connections, see [[akka.io.Tcp]] for details
  129. * @param halfClose
  130. * Controls whether the connection is kept open even after writing has been completed to the accepted
  131. * TCP connections.
  132. * If set to true, the connection will implement the TCP half-close mechanism, allowing the server to
  133. * write to the connection even after the client has finished writing. The TCP 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. def outgoingConnection(remoteAddress: InetSocketAddress,
  140. localAddress: Option[InetSocketAddress] = None,
  141. options: immutable.Traversable[SocketOption] = Nil,
  142. halfClose: Boolean = true,
  143. connectTimeout: Duration = Duration.Inf,
  144. idleTimeout: Duration = Duration.Inf): Flow[ByteString, ByteString, Future[OutgoingConnection]] = {
  145. val tcpFlow = Flow.fromGraph(new OutgoingConnectionStage(
  146. IO(IoTcp)(system),
  147. remoteAddress,
  148. localAddress,
  149. options,
  150. halfClose,
  151. connectTimeout)).via(detacher[ByteString]) // must read ahead for proper completions
  152. idleTimeout match {
  153. case d: FiniteDuration tcpFlow.join(BidiFlow.bidirectionalIdleTimeout[ByteString, ByteString](d))
  154. case _ tcpFlow
  155. }
  156. }
  157. /**
  158. * Creates an [[Tcp.OutgoingConnection]] without specifying options.
  159. * It represents a prospective TCP client connection to the given endpoint.
  160. */
  161. def outgoingConnection(host: String, port: Int): Flow[ByteString, ByteString, Future[OutgoingConnection]] =
  162. outgoingConnection(new InetSocketAddress(host, port))
  163. }