module Network.Monitoring.Riemann.TCP(tcpConnect, isTCPError, TCPState(..), doSendTCPEvent) where
import Network.Monitoring.Riemann.Types
import qualified Data.ByteString as BS
import Data.Default
import Data.IORef (IORef, writeIORef)
import Data.ProtocolBuffers
import Data.Serialize.Get
import Data.Serialize.Put
import Data.Time.Clock.POSIX
import Control.Exception
import Control.Lens
import Control.Monad
import Network.Socket hiding (recv, recvFrom, send,
sendTo)
import Network.Socket.ByteString
data TCPState = CnxClosed
| CnxOpen (Socket, AddrInfo)
| CnxError IOException
deriving Show
isTCPError :: TCPState -> Bool
isTCPError (CnxError _) = True
isTCPError _ = False
tcpConnect :: Hostname -> Port -> TCPState -> IO TCPState
tcpConnect _ _ s@(CnxOpen _) = return s
tcpConnect hn po _ = do
res <- try $ doConnect hn po
return $ case res of
Left e -> CnxError e
Right s -> CnxOpen s
tcpv4 :: AddrInfo -> Bool
tcpv4 addr = addrSocketType addr == Stream &&
addrFamily addr == AF_INET
doConnect :: HostName -> Port -> IO (Socket, AddrInfo)
doConnect hn po = do addrs <- getAddrInfo
(Just $ defaultHints {
addrFlags = [AI_NUMERICSERV] })
(Just hn)
(Just $ show po)
case (filter tcpv4 addrs) of
[] -> fail ("No accessible addresses in " ++ show addrs)
(addy:_) -> do
s <- socket AF_INET
Stream
defaultProtocol
connect s (addrAddress addy)
return (s, addy)
doSendTCPEvent :: IORef TCPState -> Socket -> Event -> IO ()
doSendTCPEvent r s event = do
sending <- try $ do
now <- fmap round getPOSIXTime
let msg = def & events .~ [event & time ?~ now]
bytes = runPut $ encodeMessage msg
bytesWithLen = runPut (putWord32be (fromIntegral $ BS.length bytes) >> putByteString bytes)
void $ send s bytesWithLen
case sending of
Left e -> writeIORef r (CnxError e)
Right _ -> doReceiveAck r s
doReceiveAck :: IORef TCPState -> Socket -> IO ()
doReceiveAck r s = do
bs <- recv s 4096
if BS.length bs == 0
then close s >> writeIORef r CnxClosed
else case decoded bs of
Left _ -> return ()
Right msg -> do
let st = msg ^. msgState
case st of
Ok -> return ()
Error _ -> return ()
Unknown -> return ()
where
decoded bs = runGet decodeMessage bs :: Either String Msg