1{-# OPTIONS_HADDOCK hide #-}
3{-# LANGUAGE BangPatterns #-}
4{-# LANGUAGE FlexibleContexts #-}
5{-# LANGUAGE MagicHash #-}
6{-# LANGUAGE RecordWildCards #-}
7{-# LANGUAGE UnboxedTuples #-}
12-- Module : Streamly.Internal.Network.Inet.TCP
13-- Copyright : (c) 2019 Composewell Technologies
16-- Maintainer : streamly@composewell.com
17-- Stability : experimental
20-- Combinators to build Inet/TCP clients and servers.
22module Streamly.Internal.Network.Inet.TCP
33 , connectionsOnLocalHost
36 -- | IP Address based operations.
74 -- These abstractions can be applied to any setting where we need to do a
75 -- sink processing of multiple streams e.g. output from multiple processes
76 -- or data coming from multiple files.
78 -- handle connections concurrently using a specified fold
79 -- , handleConnections
81 -- handle frames concurrently using a specified fold
84 -- merge frames from all connection into a single stream. Frames can be
85 -- created by a specified fold.
95import Control.Monad.Catch (MonadCatch, MonadMask, bracket)
96import Control.Monad.IO.Class (MonadIO(..))
97import Data.Word (Word8)
99 (Socket, PortNumber, SocketOption(..), Family(..), SockAddr(..),
100 SocketType(..), defaultProtocol, maxListenQueue, tupleToHostAddress,
102import Prelude hiding (read)
104import Streamly (MonadAsync)
105import Streamly.Internal.Data.Fold.Types (Fold(..))
106import Streamly.Internal.Data.Unfold.Types (Unfold(..))
107import Streamly.Internal.Network.Socket (SockSpec(..), accept, connections)
108import Streamly.Streams.Serial (SerialT)
109import Streamly.Internal.Memory.Array.Types (Array(..), defaultChunkSize, writeNUnsafe)
110import Streamly.Streams.StreamK.Type (IsStream)
112import qualified Network.Socket as Net
113import qualified Streamly.Internal.Data.Unfold as UF
114import qualified Streamly.Internal.Memory.Array as A
115import qualified Streamly.Internal.Memory.ArrayStream as AS
116import qualified Streamly.Internal.Data.Fold.Types as FL
117import qualified Streamly.Prelude as S
118import qualified Streamly.Network.Socket as SK
119import qualified Streamly.Internal.Network.Socket as ISK
121-------------------------------------------------------------------------------
123-------------------------------------------------------------------------------
125-- | Unfold a tuple @(ipAddr, port)@ into a stream of connected TCP sockets.
126-- @ipAddr@ is the local IP address and @port@ is the local port on which
127-- connections are accepted.
130{-# INLINE acceptOnAddr #-}
133 => Unfold m ((Word8, Word8, Word8, Word8), PortNumber) Socket
134acceptOnAddr = UF.lmap f accept
139 { sockFamily = AF_INET
141 , sockProto = defaultProtocol -- TCP
142 , sockOpts = [(NoDelay,1), (ReuseAddr,1)]
144 , SockAddrInet port (tupleToHostAddress addr)
147-- | Like 'acceptOnAddr' but binds on the IPv4 address @0.0.0.0@ i.e. on all
148-- IPv4 addresses/interfaces of the machine and listens for TCP connections on
149-- the specified port.
151-- > acceptOnPort = UF.supplyFirst acceptOnAddr (0,0,0,0)
154{-# INLINE acceptOnPort #-}
155acceptOnPort :: MonadIO m => Unfold m PortNumber Socket
156acceptOnPort = UF.supplyFirst acceptOnAddr (0,0,0,0)
158-- | Like 'acceptOnAddr' but binds on the localhost IPv4 address @127.0.0.1@.
159-- The server can only be accessed from the local host, it cannot be accessed
160-- from other hosts on the network.
162-- > acceptOnPortLocal = UF.supplyFirst acceptOnAddr (127,0,0,1)
165{-# INLINE acceptOnPortLocal #-}
166acceptOnPortLocal :: MonadIO m => Unfold m PortNumber Socket
167acceptOnPortLocal = UF.supplyFirst acceptOnAddr (127,0,0,1)
169-------------------------------------------------------------------------------
171-------------------------------------------------------------------------------
173-- | Like 'connections' but binds on the specified IPv4 address of the machine
174-- and listens for TCP connections on the specified port.
177{-# INLINE connectionsOnAddr #-}
180 => (Word8, Word8, Word8, Word8)
183connectionsOnAddr addr port =
184 connections maxListenQueue SockSpec
185 { sockFamily = AF_INET
187 , sockProto = defaultProtocol
188 , sockOpts = [(NoDelay,1), (ReuseAddr,1)]
190 (SockAddrInet port (tupleToHostAddress addr))
192-- | Like 'connections' but binds on the IPv4 address @0.0.0.0@ i.e. on all
193-- IPv4 addresses/interfaces of the machine and listens for TCP connections on
194-- the specified port.
196-- > connectionsOnPort = connectionsOnAddr (0,0,0,0)
199{-# INLINE connectionsOnPort #-}
200connectionsOnPort :: MonadAsync m => PortNumber -> SerialT m Socket
201connectionsOnPort = connectionsOnAddr (0,0,0,0)
203-- | Like 'connections' but binds on the localhost IPv4 address @127.0.0.1@.
204-- The server can only be accessed from the local host, it cannot be accessed
205-- from other hosts on the network.
207-- > connectionsOnLocalHost = connectionsOnAddr (127,0,0,1)
210{-# INLINE connectionsOnLocalHost #-}
211connectionsOnLocalHost :: MonadAsync m => PortNumber -> SerialT m Socket
212connectionsOnLocalHost = connectionsOnAddr (127,0,0,1)
214-------------------------------------------------------------------------------
216-------------------------------------------------------------------------------
218-- | Connect to the specified IP address and port number.
221connect :: (Word8, Word8, Word8, Word8) -> PortNumber -> IO Socket
222connect addr port = do
223 sock <- socket AF_INET Stream defaultProtocol
224 Net.connect sock $ SockAddrInet port (Net.tupleToHostAddress addr)
227-- | Connect to a remote host using IP address and port and run the supplied
228-- action on the resulting socket. 'withServerAddrM' makes sure that the
229-- socket is closed on normal termination or in case of an exception. If
230-- closing the socket raises an exception, then this exception will be raised
231-- by 'withServerAddrM'.
234{-# INLINABLE withServerAddrM #-}
235withServerAddrM :: (MonadMask m, MonadIO m)
236 => (Word8, Word8, Word8, Word8) -> PortNumber -> (Socket -> m ()) -> m ()
237withServerAddrM addr port =
238 bracket (liftIO $ connect addr port) (liftIO . Net.close)
240-------------------------------------------------------------------------------
242-------------------------------------------------------------------------------
244-- | Transform an 'Unfold' from a 'Socket' to an unfold from a remote IP
245-- address and port. The resulting unfold opens a socket, uses it using the
246-- supplied unfold and then makes sure that the socket is closed on normal
247-- termination or in case of an exception. If closing the socket raises an
248-- exception, then this exception will be raised by 'withServerAddr'.
251{-# INLINABLE withServerAddr #-}
252withServerAddr :: (MonadCatch m, MonadIO m)
254 -> Unfold m ((Word8, Word8, Word8, Word8), PortNumber) a
256 UF.bracket (\(addr, port) -> liftIO $ connect addr port)
259-------------------------------------------------------------------------------
261-------------------------------------------------------------------------------
263-- | @'withConnection' addr port act@ opens a connection to the specified IPv4
264-- host address and port and passes the resulting socket handle to the
265-- computation @act@. The handle will be closed on exit from 'withConnection',
266-- whether by normal termination or by raising an exception. If closing the
267-- handle raises an exception, then this exception will be raised by
268-- 'withConnection' rather than any exception raised by 'act'.
271{-# INLINABLE withConnection #-}
272withConnection :: (IsStream t, MonadCatch m, MonadIO m)
273 => (Word8, Word8, Word8, Word8) -> PortNumber -> (Socket -> t m a) -> t m a
274withConnection addr port =
275 S.bracket (liftIO $ connect addr port) (liftIO . Net.close)
277-------------------------------------------------------------------------------
278-- Read Addr to Stream
279-------------------------------------------------------------------------------
281-- | Read a stream from the supplied IPv4 host address and port number.
285read :: (MonadCatch m, MonadIO m)
286 => Unfold m ((Word8, Word8, Word8, Word8), PortNumber) Word8
287read = UF.concat (withServerAddr ISK.readChunks) A.read
289-- | Read a stream from the supplied IPv4 host address and port number.
292{-# INLINE toStream #-}
293toStream :: (IsStream t, MonadCatch m, MonadIO m)
294 => (Word8, Word8, Word8, Word8) -> PortNumber -> t m Word8
295toStream addr port = AS.concat $ withConnection addr port ISK.toChunks
297-------------------------------------------------------------------------------
299-------------------------------------------------------------------------------
302-- | Write a stream of arrays to the supplied IPv4 host address and port
306{-# INLINE writeChunks #-}
308 :: (MonadCatch m, MonadAsync m)
309 => (Word8, Word8, Word8, Word8)
311 -> SerialT m (Array Word8)
313writeChunks addr port xs =
314 S.drain $ withConnection addr port (\sk -> S.yieldM $ SK.writeChunks sk xs)
317-- | Write a stream of arrays to the supplied IPv4 host address and port
321{-# INLINE writeChunks #-}
324 => (Word8, Word8, Word8, Word8)
326 -> Fold m (Array Word8) ()
327writeChunks addr port = Fold step initial extract
330 skt <- liftIO (connect addr port)
331 FL.initialize (SK.writeChunks skt)
333 extract (Fold _ initial1 extract1) = initial1 >>= extract1
336-- | Like 'write' but provides control over the write buffer. Output will
337-- be written to the IO device as soon as we collect the specified number of
341{-# INLINE writeRequestsOf #-}
343 :: (MonadCatch m, MonadAsync m)
345 -> (Word8, Word8, Word8, Word8)
349writeRequestsOf n addr port m = writeChunks addr port $ AS.arraysOf n m
352-- | Like 'write' but provides control over the write buffer. Output will
353-- be written to the IO device as soon as we collect the specified number of
357{-# INLINE writeRequestsOf #-}
361 -> (Word8, Word8, Word8, Word8)
364writeRequestsOf n addr port =
365 FL.lchunksOf n (writeNUnsafe n) (writeChunks addr port)
368-- | Write a stream to the supplied IPv4 host address and port number.
372write :: (MonadCatch m, MonadAsync m)
373 => (Word8, Word8, Word8, Word8) -> PortNumber -> SerialT m Word8 -> m ()
374write = writeRequestsOf defaultChunkSize
377-- | Write a stream to the supplied IPv4 host address and port number.
382 => (Word8, Word8, Word8, Word8) -> PortNumber -> Fold m Word8 ()
383write = writeRequestsOf defaultChunkSize
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list