{-# LANGUAGE LambdaCase, OverloadedStrings #-}

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

import qualified Control.Exception             as E
import           Control.Monad.IO.Class
import           Control.Monad.Managed
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 a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
E.bracket
      (HostName -> IO ()
putStrLn "[ Establishing connection with Pulsar ]" IO () -> IO Socket -> IO Socket
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> AddrInfo -> IO Socket
open AddrInfo
addr)
      (\s :: Socket
s -> HostName -> IO ()
putStrLn "[ Closing Pulsar connection ]" IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Socket -> IO ()
NS.close Socket
s)
    )
 where
  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
E.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 a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
E.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