{-# LANGUAGE ScopedTypeVariables #-} -- |Run processed connected to Top module Network.Top.Run ( runAppForever, runApp, runAppWith ) where import Data.ByteString (ByteString) import Network.Top.Types import Network.Top.Util import Network.Top.WebSockets import ZM -- |Permanently connect an application to a typed channel. -- |Restart application in case of network or application failure. -- |NOTE: does not provide a way to preserve application's state runAppForever :: (Model (router a), Flat (router a),Show (router a),Flat a,Show a) => Config -- ^ Top configuration -> router a -- ^ Routing protocol -> App a r -- ^ Application to connect -> IO r -- ^ Value returned from the application runAppForever cfg router app = forever $ do Left (ex :: SomeException) <- try $ runApp cfg router $ \conn -> do liftIO $ dbgS "connected" app conn -- Something went wrong, wait a few seconds and restart dbg ["Exited loop with error",concat ["'",show ex,"'"],"retrying in a bit."] threadDelay $ seconds 5 -- |Connect an application to a typed channel. runApp :: (Model (router a), Flat (router a),Show (router a),Flat a,Show a) => Config -- ^ Top configuration -> router a -- ^ Routing protocol -> App a r -- ^ Application to connect -> IO r -- ^ Value returned from the application runApp cfg router app = do dbg ["run",show router] runAppWith cfg (typedBLOB router) (\conn -> app (Connection (receive conn) (send conn) (close conn))) -- |Connect an application to a typed channel runAppWith :: Config -- ^ Top configuration -> TypedBLOB -- ^ Routing protocol -> App ByteString r -- ^ Application to connect -> IO r -- ^ Value returned from the application runAppWith cfg routerBin app = run cfg 1 where run _ 4 = errIn "Too many redirects" run cfg n = do res <- runWSApp cfg $ \conn -> do send conn routerBin r::WSChannelResult <- receive conn dbgS $ unwords ["Received CHATS answer",show r] case r of Failure why -> errIn why Success -> Right <$> app conn -- (Connection (receive conn) (send conn) (close conn)) --Success -> Right <$> app (Connection (receive conn) (send conn) (close conn)) RetryAt addr -> return (Left $ Config addr) case res of Left cfg' -> run cfg' (n+1) Right r -> return r errIn msg = dbg ["Failure",msg] >> error msg -- |Send a value on a typed connection send :: (Show a,Flat a) => WSConnection -> a -> IO () send conn v = do let e = flat v output conn e --dbg ["sent",show v,"as",show $ L.unpack e] -- |Receive a value from a typed connection receive :: (Show a,Flat a) => WSConnection -> IO a receive conn = do e <- input conn either (\ex -> error $ unwords ["receive error",show ex]) return $ unflat e -- receive :: Flat a => WSConnection -> IO (Maybe a) -- receive conn = do -- mbs <- input conn -- return $ case mbs of -- Nothing -> Nothing -- Just bs -> eitherToMaybe $ unflat bs -- -- |Setup a connection by sending a value specifying the routing protocol to be used -- protocol :: (Show r, Model r, Flat r) => Connection ByteString -> r -> IO () -- --protocol = setProtocol (return ()) -- protocol conn routerType = do -- dbg ["protocol",show routerType] -- send conn . typedBLOB $ routerType -- r::WSChannelResult <- receive conn -- case r of -- Failure why -> err $ T.unpack why -- Success -> return () -- RetryAt addr -> err (show $ RetryAt addr) -- where -- err msg = error msg -- unwords ["Failed to establish connection:",msg] -- setProtocol onSuccess onFailure conn router = do -- dbg ["protocol",show router] -- send conn . typedBLOB $ router -- r::WSChannelResult <- receive conn -- case r of -- Failure why -> onFailure err $ T.unpack why -- Success -> onSuccess -- -- RetryAt addr -> err (show $ RetryAt addr) -- where -- err msg = error msg -- unwords ["Failed to establish connection:",msg] -- setProtocol conn routerType = run 1 -- where -- run 4 = errIn "Too many redirects" -- run n = do -- dbg ["protocol",show routerType,"attempt",show n] -- send conn . typedBLOB $ routerType -- r::WSChannelResult <- receive conn -- case r of -- Failure why -> errIn (T.unpack why) -- Success -> Right <$> app conn -- (Connection (receive conn) (send conn) (close conn)) -- --Success -> Right <$> app (Connection (receive conn) (send conn) (close conn)) -- RetryAt addr -> return (Left $ Config addr) -- case res of -- Left cfg' -> run cfg' (n+1) -- Right r -> return r -- errIn msg = dbg ["Failure",msg] >> error msg