module System.IO.Streams.TCP (
connectSocket
, connect
, connectWithBufferSize
, withConnection
, bindAndListen
, accept
) where
import qualified Control.Exception as E
import Control.Monad (void)
import Data.ByteString.Char8 (ByteString)
import Network.Socket (HostName, PortNumber, Socket)
import qualified Network.Socket as N
import System.IO.Streams (InputStream, OutputStream)
import qualified System.IO.Streams as Stream
import System.IO.Streams.Network (socketToStreamsWithBufferSize)
bUFSIZ :: Int
bUFSIZ = 4096
resolveAddrInfo :: HostName -> PortNumber -> IO (N.Family, N.SocketType, N.ProtocolNumber, N.SockAddr)
resolveAddrInfo host port = do
(addrInfo:_) <- N.getAddrInfo (Just hints) (Just host) (Just $ show port)
let family = N.addrFamily addrInfo
let socketType = N.addrSocketType addrInfo
let protocol = N.addrProtocol addrInfo
let address = N.addrAddress addrInfo
return (family, socketType, protocol, address)
where
hints = N.defaultHints {
N.addrFlags = [N.AI_ADDRCONFIG, N.AI_NUMERICSERV]
, N.addrSocketType = N.Stream
}
connectSocket :: HostName
-> PortNumber
-> IO Socket
connectSocket host port = do
(family, socketType, protocol, address) <- resolveAddrInfo host port
E.bracketOnError (N.socket family socketType protocol)
N.close
(\sock -> do N.connect sock address
E.catch
(N.setSocketOption sock N.NoDelay 1)
(\ (E.SomeException _) -> return ())
return sock
)
connect :: HostName
-> PortNumber
-> IO (InputStream ByteString, OutputStream ByteString, Socket)
connect host port = do
sock <- connectSocket host port
(is, os) <- socketToStreamsWithBufferSize bUFSIZ sock
return (is, os, sock)
connectWithBufferSize :: HostName
-> PortNumber
-> Int
-> IO (InputStream ByteString, OutputStream ByteString, Socket)
connectWithBufferSize host port bufsiz = do
sock <- connectSocket host port
(is, os) <- socketToStreamsWithBufferSize bufsiz sock
return (is, os, sock)
withConnection :: HostName
-> PortNumber
-> ( InputStream ByteString
-> OutputStream ByteString -> Socket -> IO a)
-> IO a
withConnection host port action =
E.bracket (connect host port) cleanup go
where
go (is, os, sock) = action is os sock
cleanup (_, os, sock) = E.mask_ $ do
eatException $! Stream.write Nothing os
eatException $! N.close sock
eatException m = void m `E.catch` (\(_::E.SomeException) -> return ())
bindAndListen :: PortNumber -> Int -> IO Socket
bindAndListen port maxc = do
E.bracketOnError (N.socket N.AF_INET N.Stream 0)
N.close
(\sock -> do
E.catch
(do
N.setSocketOption sock N.ReuseAddr 1
N.setSocketOption sock N.NoDelay 1)
(\ (E.SomeException _) -> return ())
N.bind sock (N.SockAddrInet port N.iNADDR_ANY)
N.listen sock maxc
return sock
)
accept :: Socket -> IO (InputStream ByteString, OutputStream ByteString, N.Socket, N.SockAddr)
accept sock = do
(sock', sockAddr) <- N.accept sock
(is, os) <- socketToStreamsWithBufferSize bUFSIZ sock'
return (is, os, sock', sockAddr)