{-# LANGUAGE LambdaCase, OverloadedStrings, RankNTypes #-}

{- A simple TCP client, used to communicate with the Pulsar server -}
module Pulsar.Internal.TCPClient
  ( acquireSocket
  )
where

import           Control.Monad.Catch            ( bracket
                                                , bracketOnError
                                                )
import           Control.Monad.IO.Class         ( MonadIO
                                                , liftIO
                                                )
import           Control.Monad.Managed          ( MonadManaged
                                                , managed
                                                , using
                                                )
import qualified Network.Socket                as NS

acquireSocket
  :: (MonadIO m, MonadManaged m) => NS.HostName -> NS.ServiceName -> m NS.Socket
acquireSocket :: HostName -> HostName -> m Socket
acquireSocket host :: HostName
host port :: HostName
port = do
  AddrInfo
addr <- IO AddrInfo -> m AddrInfo
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO AddrInfo
resolve
  Managed Socket -> m Socket
forall (m :: * -> *) a. MonadManaged m => Managed a -> m a
using (Managed Socket -> m Socket) -> Managed Socket -> m Socket
forall a b. (a -> b) -> a -> b
$ (forall r. (Socket -> IO r) -> IO r) -> Managed Socket
forall a. (forall r. (a -> IO r) -> IO r) -> Managed a
managed (IO Socket -> (Socket -> IO ()) -> (Socket -> IO r) -> IO r
forall (m :: * -> *) a c b.
MonadMask m =>
m a -> (a -> m c) -> (a -> m b) -> m b
bracket (AddrInfo -> IO Socket
acquire AddrInfo
addr) Socket -> IO ()
release)
 where
  acquire :: AddrInfo -> IO Socket
acquire = (HostName -> IO ()
putStrLn "[ Establishing connection with Pulsar ]" IO () -> IO Socket -> IO Socket
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>>) (IO Socket -> IO Socket)
-> (AddrInfo -> IO Socket) -> AddrInfo -> IO Socket
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AddrInfo -> IO Socket
open
  release :: Socket -> IO ()
release = (HostName -> IO ()
putStrLn "[ Closing Pulsar connection ]" IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>>) (IO () -> IO ()) -> (Socket -> IO ()) -> Socket -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Socket -> IO ()
NS.close
  resolve :: IO AddrInfo
resolve = do
    let hints :: AddrInfo
hints = AddrInfo
NS.defaultHints { addrSocketType :: SocketType
NS.addrSocketType = SocketType
NS.Stream }
    Maybe AddrInfo -> Maybe HostName -> Maybe HostName -> IO [AddrInfo]
NS.getAddrInfo (AddrInfo -> Maybe AddrInfo
forall a. a -> Maybe a
Just AddrInfo
hints) (HostName -> Maybe HostName
forall a. a -> Maybe a
Just HostName
host) (HostName -> Maybe HostName
forall a. a -> Maybe a
Just HostName
port) IO [AddrInfo] -> ([AddrInfo] -> IO AddrInfo) -> IO AddrInfo
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
      [addr :: AddrInfo
addr] -> AddrInfo -> IO AddrInfo
forall (f :: * -> *) a. Applicative f => a -> f a
pure AddrInfo
addr
      _      -> IOError -> IO AddrInfo
forall a. IOError -> IO a
ioError (IOError -> IO AddrInfo) -> IOError -> IO AddrInfo
forall a b. (a -> b) -> a -> b
$ HostName -> IOError
userError "Could not resolve socket address"
  open :: AddrInfo -> IO Socket
open addr :: AddrInfo
addr = IO Socket
-> (Socket -> IO ()) -> (Socket -> IO Socket) -> IO Socket
forall (m :: * -> *) a c b.
MonadMask m =>
m a -> (a -> m c) -> (a -> m b) -> m b
bracketOnError (AddrInfo -> IO Socket
NS.openSocket AddrInfo
addr) Socket -> IO ()
NS.close ((Socket -> IO Socket) -> IO Socket)
-> (Socket -> IO Socket) -> IO Socket
forall a b. (a -> b) -> a -> b
$ \sock :: Socket
sock -> do
    Socket -> SockAddr -> IO ()
NS.connect Socket
sock (SockAddr -> IO ()) -> SockAddr -> IO ()
forall a b. (a -> b) -> a -> b
$ AddrInfo -> SockAddr
NS.addrAddress AddrInfo
addr
    Socket -> IO Socket
forall (m :: * -> *) a. Monad m => a -> m a
return Socket
sock