{-# LANGUAGE LambdaCase #-}
module Haskell.Debug.Adapter.Response where
import Control.Monad.IO.Class
import Data.Conduit
import Control.Lens
import Data.Aeson
import Control.Concurrent (threadDelay)
import qualified Data.ByteString.Lazy as B
import Control.Concurrent.MVar
import Control.Monad.State.Lazy
import qualified System.Log.Logger as L
import Control.Monad.Except
import qualified System.IO as S
import qualified Data.List as L
import Haskell.Debug.Adapter.Type
import Haskell.Debug.Adapter.Utility
import Haskell.Debug.Adapter.Constant
run :: AppStores -> IO ()
run appData = do
L.debugM _LOG_RESPONSE "start response app"
_ <- runApp appData app
L.debugM _LOG_RESPONSE "end response app"
app :: AppContext ()
app = flip catchError errHdl $ do
_ <- runConduit pipeline
return ()
where
pipeline :: ConduitM () Void AppContext ()
pipeline = src .| res2lbs .| sink
errHdl msg = do
criticalEV _LOG_REQUEST msg
addEvent CriticalExitEvent
src :: ConduitT () Response AppContext ()
src = do
liftIO $ L.debugM _LOG_RESPONSE $ "src start waiting."
res <- lift goApp
yield res
src
where
goApp :: AppContext Response
goApp = get >>= liftIO . getResponse
getResponse :: AppStores -> IO Response
getResponse appDat = takeResponse appDat >>= \case
Just res -> return res
Nothing -> do
threadDelay _1_MILLI_SEC
getResponse appDat
takeResponse :: AppStores -> IO (Maybe Response)
takeResponse appData = do
let ressMVar = appData^.resStoreAppStores
isExists ressMVar >>= \case
False -> return Nothing
True -> take1 ressMVar
where
isExists ressMVar = readMVar ressMVar >>= \case
[] -> return False
_ -> return True
take1 ressMVar = takeMVar ressMVar >>= \case
[] -> do
putMVar ressMVar []
return Nothing
(x:xs) -> do
putMVar ressMVar xs
return $ Just x
res2lbs :: ConduitT Response B.ByteString AppContext ()
res2lbs = do
liftIO $ L.debugM _LOG_RESPONSE $ "res2lbs start waiting."
await >>= \case
Nothing -> do
throwError $ "[CRITICAL][response][res2lbs] unexpectHed Nothing."
return ()
Just res -> do
liftIO $ L.debugM _LOG_RESPONSE $ "res2lbs get data. " ++ show res
bs <- lift $ goApp res
yield bs
res2lbs
where
goApp :: Response -> AppContext B.ByteString
goApp = return . encode
sink :: ConduitT B.ByteString Void AppContext ()
sink = do
liftIO $ L.debugM _LOG_RESPONSE $ "sink start start."
await >>= \case
Nothing -> do
throwError $ "[CRITICAL][response][sink] unexpectHed Nothing."
return ()
Just bs -> do
liftIO $ L.debugM _LOG_RESPONSE $ "sink get data. " ++ lbs2str bs
lift $ goApp bs
cont $ lbs2str bs
where
goApp bs = do
wHdl <- view outHandleAppStores <$> get
liftIO $ sendResponse wHdl bs
cont str
| L.isInfixOf _KEY_DISCONNECT_RESPONCE str = do
liftIO $ L.infoM _LOG_RESPONSE $ "disconnect. end of response thread."
| otherwise = sink
_KEY_DISCONNECT_RESPONCE :: String
_KEY_DISCONNECT_RESPONCE = "\"command\":\"disconnect\""
sendResponse :: S.Handle -> B.ByteString -> IO ()
sendResponse hdl str = do
B.hPut hdl $ str2lbs $ _CONTENT_LENGTH ++ (show (B.length str))
B.hPut hdl $ str2lbs _TWO_CRLF
B.hPut hdl str
S.hFlush hdl