packages feed

hasbolt-0.1.4.4: src/Database/Bolt/Connection/Connection.hs

{-# LANGUAGE RecordWildCards #-}
module Database.Bolt.Connection.Connection where

import           Control.Applicative    (pure, (<$>))
import           Control.Exception      (throwIO)
import           Control.Monad          (forM_, when)
import           Control.Monad.Trans    (MonadIO (..))
import           Data.ByteString        (ByteString, null)
import           Data.Default           (Default (..))
import           GHC.Stack              (HasCallStack, withFrozenCallStack)
import           Network.Socket         (PortNumber)
import           Network.Connection     (ConnectionParams (..), connectTo, connectionClose,
                                        connectionGetExact, connectionPut, connectionSetSecure,
                                        initConnectionContext)
import           Prelude                hiding (null)
import           System.Timeout         (timeout)

import           Database.Bolt.Connection.Type (BoltError (..), ConnectionWithTimeout (..))

connect
  :: MonadIO m
  => HasCallStack
  => Bool
     -- ^ Use secure connection
  -> String
     -- ^ Hostname
  -> PortNumber
  -> Int
     -- ^ Connection and read timeout in seconds
  -> m ConnectionWithTimeout
connect secure host port timeSec = liftIO $ do
                                      let timeUsec = 1000000 * timeSec
                                      ctx  <- initConnectionContext
                                      conn <- timeoutThrow timeUsec $
                                              connectTo ctx ConnectionParams { connectionHostname  = host
                                                                             , connectionPort      = port
                                                                             , connectionUseSecure = Nothing
                                                                             , connectionUseSocks  = Nothing
                                                                             }
                                      when secure $ connectionSetSecure ctx conn def
                                      pure $ ConnectionWithTimeout conn timeUsec

close :: MonadIO m => HasCallStack => ConnectionWithTimeout -> m ()
close ConnectionWithTimeout{..} = liftIO $ timeoutThrow cwtTimeoutUsec $ connectionClose cwtConnection

recv :: MonadIO m => HasCallStack => ConnectionWithTimeout -> Int -> m (Maybe ByteString)
recv ConnectionWithTimeout{..} = liftIO . (filterMaybe (not . null) <$>) . timeoutThrow cwtTimeoutUsec . connectionGetExact cwtConnection
  where
    filterMaybe :: (a -> Bool) -> a -> Maybe a
    filterMaybe p x | p x       = Just x
                    | otherwise = Nothing

send :: MonadIO m => HasCallStack => ConnectionWithTimeout -> ByteString -> m ()
send ConnectionWithTimeout{..} = liftIO . timeoutThrow cwtTimeoutUsec . connectionPut cwtConnection

sendMany :: MonadIO m => HasCallStack => ConnectionWithTimeout -> [ByteString] -> m ()
sendMany conn@ConnectionWithTimeout{..} chunks = liftIO $ forM_ chunks $ timeoutThrow cwtTimeoutUsec . send conn

timeoutThrow :: HasCallStack => Int -> IO a -> IO a
timeoutThrow timeUsec action = withFrozenCallStack $ do
  res <- timeout timeUsec action
  case res of
    Just a  -> return a
    Nothing -> throwIO TimeOut