@@ -8,7 +8,7 @@
88#include "inline.hs"
99
1010-- |
11−-- Module : Streamly.Internal.Network.Client
11+-- Module : Streamly.Internal.Network.Inet.TCP
1212-- Copyright : (c) 2019 Composewell Technologies
1313--
1414-- License : BSD3
@@ -16,18 +16,28 @@
1616-- Stability : experimental
1717-- Portability : GHC
1818--
19−-- Combinators to build network clients.
20−--
21−-- > import qualified Streamly.Network.Socket as Client
22−--
19+-- Combinators to build Inet/TCP clients and servers.
2320
24−module Streamly.Internal.Network.Client
21+module Streamly.Internal.Network.Inet.TCP
2522 (
26− -- * Interact
23+ -- * TCP Servers
24+ -- ** Connected sockets
25+ -- *** Unfolds
26+ listenOnAddr
27+ , listenOnPort
28+ , listenOnPortLocal
29+
30+ -- *** Streams
31+ , connections
32+ , connectionsOnAddr
33+ , connectionsOnAllAddrs
34+ , connectionsOnLocalHost
35+
36+ -- * TCP clients
2737 -- | Socket based reads and writes.
28− withConnection
38+ , withConnection
2939
30− -- * Source
40+ -- ** Source
3141 , read
3242 -- , readUtf8
3343 -- , readLines
@@ -42,7 +52,7 @@ module Streamly.Internal.Network.Client
4252 -- , readArraysOf
4353 -- , readArrays
4454
45− -- * Sink
55+ -- ** Sink
4656 , write
4757 -- , writeUtf8
4858 -- , writeUtf8ByLines
@@ -52,6 +62,27 @@ module Streamly.Internal.Network.Client
5262 -- -- * Array Write
5363 -- , writeArray
5464 , writeArrays
65+ {-
66+ -- ** Sink Servers
67+
68+ -- These abstractions can be applied to any setting where we need to do a
69+ -- sink processing of multiple streams e.g. output from multiple processes
70+ -- or data coming from multiple files.
71+
72+ -- handle connections concurrently using a specified fold
73+ -- , handleConnections
74+
75+ -- handle frames concurrently using a specified fold
76+ , handleFrames
77+
78+ -- merge frames from all connection into a single stream. Frames can be
79+ -- created by a specified fold.
80+ , mergeFrames
81+
82+ -- * UDP Servers
83+ , datagrams
84+ , datagramsOn
85+ -}
5586 )
5687where
5788
@@ -59,24 +90,124 @@ import Control.Monad.Catch (MonadCatch)
5990import Control.Monad.IO.Class (MonadIO(..))
6091import Data.Word (Word8)
6192import Network.Socket
62− (Socket, PortNumber, Family(..), SockAddr(..), SocketType(..),
63− socket, connect, defaultProtocol)
93+ (Socket, PortNumber, SocketOption(..), Family(..), SockAddr(..),
94+ SocketType(..), defaultProtocol, maxListenQueue, tupleToHostAddress,
95+ socket, connect)
6496import Prelude hiding (read)
6597
66−import qualified Network.Socket as Net
67−
6898import Streamly (MonadAsync)
6999import Streamly.Internal.Data.Fold.Types (Fold(..))
100+import Streamly.Internal.Data.Unfold.Types (Unfold(..))
101+import Streamly.Internal.Network.Socket (SockSpec(..), listen, connections)
102+import Streamly.Streams.Serial (SerialT)
70103import Streamly.Internal.Memory.Array.Types (Array(..), defaultChunkSize, writeNUnsafe)
71−-- import Streamly.Streams.Serial (SerialT)
72104import Streamly.Streams.StreamK.Type (IsStream)
73105
106+import qualified Network.Socket as Net
107+import qualified Streamly.Internal.Data.Unfold as UF
74108import qualified Streamly.Internal.Memory.ArrayStream as AS
75109import qualified Streamly.Internal.Data.Fold.Types as FL
76110import qualified Streamly.Prelude as S
77111import qualified Streamly.Network.Socket as SK
78112import qualified Streamly.Internal.Network.Socket as ISK
79113
114+-------------------------------------------------------------------------------
115+-- TCP Servers
116+-------------------------------------------------------------------------------
117+
118+-- | Unfold a tuple @(ipAddr, port)@ into a stream of connected TCP sockets.
119+-- @ipAddr@ is the local IP address and @port@ is the local port on which
120+-- connections are accepted.
121+--
122+-- @since 0.7.0
123+{-# INLINE listenOnAddr #-}
124+listenOnAddr
125+ :: MonadIO m
126+ => Unfold m ((Word8, Word8, Word8, Word8), PortNumber) Socket
127+listenOnAddr = UF.lmap f listen
128+ where
129+ f (addr, port) =
130+ (maxListenQueue
131+ , SockSpec
132+ { sockFamily = AF_INET
133+ , sockType = Stream
134+ , sockProto = defaultProtocol -- TCP
135+ , sockOpts = [(NoDelay,1), (ReuseAddr,1)]
136+ }
137+ , SockAddrInet port (tupleToHostAddress addr)
138+ )
139+
140+-- | Like 'listenOnAddr' but binds on the IPv4 address @0.0.0.0@ i.e. on all
141+-- IPv4 addresses/interfaces of the machine and listens for TCP connections on
142+-- the specified port.
143+--
144+-- > listenOnPort = UF.supplyFirst listenOnAddr (0,0,0,0)
145+--
146+-- @since 0.7.0
147+{-# INLINE listenOnPort #-}
148+listenOnPort :: MonadIO m => Unfold m PortNumber Socket
149+listenOnPort = UF.supplyFirst listenOnAddr (0,0,0,0)
150+
151+-- | Like 'listenOnAddr' but binds on the localhost IPv4 address @127.0.0.1@.
152+-- The server can only be accessed from the local host, it cannot be accessed
153+-- from other hosts on the network.
154+--
155+-- > listenOnPortLocal = UF.supplyFirst listenOnAddr (127,0,0,1)
156+--
157+-- @since 0.7.0
158+{-# INLINE listenOnPortLocal #-}
159+listenOnPortLocal :: MonadIO m => Unfold m PortNumber Socket
160+listenOnPortLocal = UF.supplyFirst listenOnAddr (127,0,0,1)
161+
162+-------------------------------------------------------------------------------
163+-- Listen
164+-------------------------------------------------------------------------------
165+
166+-- | Like 'connections' but binds on the specified IPv4 address of the machine
167+-- and listens for TCP connections on the specified port.
168+--
169+-- /Internal/
170+{-# INLINE connectionsOnAddr #-}
171+connectionsOnAddr
172+ :: MonadAsync m
173+ => (Word8, Word8, Word8, Word8)
174+ -> PortNumber
175+ -> SerialT m Socket
176+connectionsOnAddr addr port =
177+ connections maxListenQueue SockSpec
178+ { sockFamily = AF_INET
179+ , sockType = Stream
180+ , sockProto = defaultProtocol
181+ , sockOpts = [(NoDelay,1), (ReuseAddr,1)]
182+ }
183+ (SockAddrInet port (tupleToHostAddress addr))
184+
185+-- | Like 'connections' but binds on the IPv4 address @0.0.0.0@ i.e. on all
186+-- IPv4 addresses/interfaces of the machine and listens for TCP connections on
187+-- the specified port.
188+--
189+-- > connectionsOnAllAddrs = connectionsOnAddr (0,0,0,0)
190+--
191+-- /Internal/
192+{-# INLINE connectionsOnAllAddrs #-}
193+connectionsOnAllAddrs :: MonadAsync m => PortNumber -> SerialT m Socket
194+connectionsOnAllAddrs = connectionsOnAddr (0,0,0,0)
195+
196+-- | Like 'connections' but binds on the localhost IPv4 address @127.0.0.1@.
197+-- The server can only be accessed from the local host, it cannot be accessed
198+-- from other hosts on the network.
199+--
200+-- > connectionsOnLocalHost = connectionsOnAddr (127,0,0,1)
201+--
202+-- /Internal/
203+{-# INLINE connectionsOnLocalHost #-}
204+connectionsOnLocalHost :: MonadAsync m => PortNumber -> SerialT m Socket
205+connectionsOnLocalHost = connectionsOnAddr (127,0,0,1)
206+
207+-------------------------------------------------------------------------------
208+-- TCP Clients
209+-------------------------------------------------------------------------------
210+
80211-------------------------------------------------------------------------------
81212-- Connect
82213-------------------------------------------------------------------------------