Backend.hs 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. {-# LANGUAGE OverloadedStrings #-}
  2. module Walrus.Backend (
  3. Backend(..),
  4. LazyHandler,
  5. UIOHandler,
  6. module Walrus.Backend.Metadata,
  7. parseBackend,
  8. callBackend,
  9. runLazyOnce,
  10. runUIOOnce
  11. ) where
  12. import qualified Data.Attoparsec.ByteString.Char8 as A
  13. import qualified Data.Attoparsec.ByteString.Lazy as LA
  14. import Data.Attoparsec.ByteString.Char8.Extras
  15. import qualified Data.ByteString.Lazy as LBS
  16. import qualified Data.Char as C
  17. import Control.Monad.IO.Class
  18. import Text.StringConvert
  19. import Control.Lens
  20. import Walrus.Backend.Metadata
  21. import Network
  22. import System.IO
  23. import System.IO.Uniform
  24. import qualified System.IO.Uniform.Streamline as S
  25. --import System.IO.Uniform.HandlePair as HP
  26. --import System.IO.Uniform.Network as Net
  27. import qualified System.Process as P
  28. data Backend = TCPBackend String Int |
  29. ExecBackend String [String] deriving (Show, Read, Ord, Eq)
  30. type LazyHandler = Metadata -> LBS.ByteString -> IO (Either String (Metadata, LBS.ByteString))
  31. type UIOHandler = Metadata -> S.Streamline IO (Either String (Metadata, LBS.ByteString))
  32. parseBackend :: A.Parser Backend
  33. parseBackend = A.choice [
  34. do
  35. tp "tcp"
  36. h <- tillSpace
  37. p <- A.decimal
  38. return $ TCPBackend h p,
  39. do
  40. tp "exec"
  41. f <- qStr
  42. skipHorizontalSpace
  43. pp <- parseParameters
  44. return $ ExecBackend f pp
  45. ]
  46. where
  47. tp t = do
  48. skipHorizontalSpace
  49. A.stringCI t
  50. skipHorizontalSpace
  51. tillSpace :: A.Parser String
  52. tillSpace = s <$> A.takeTill C.isSpace
  53. parseParameters :: A.Parser [String]
  54. parseParameters = do
  55. p <- qStr
  56. skipHorizontalSpace
  57. if null p then return [] else do
  58. pp <- parseParameters
  59. return $ p : pp
  60. qStr :: A.Parser String
  61. qStr = s <$> quotedString '\\' " " ""
  62. callBackend :: Backend -> (Metadata, LBS.ByteString) -> IO (Either String (Metadata, LBS.ByteString))
  63. callBackend b (m, qdt) = do
  64. let rm = renderMetadata m
  65. edt' <- intBk b $ LBS.concat [
  66. s rm,
  67. qdt]
  68. case LA.parse repParse edt' of
  69. LA.Fail _ _ e -> return $ Left e
  70. LA.Done edt m' -> return $ Right (m', edt)
  71. where
  72. intBk :: Backend -> LBS.ByteString -> IO LBS.ByteString
  73. intBk (TCPBackend h p) = runTcp h p
  74. intBk (ExecBackend f aa) = runExec f aa
  75. repParse = parseMetadata
  76. runLazyOnce :: UniformIO u => u -> LazyHandler -> IO (Either String ())
  77. runLazyOnce u f = S.withTarget u $ do
  78. m' <- S.runAttoparsec parseMetadata
  79. case m' of
  80. Left e -> return . Left . show $ e
  81. Right m -> do
  82. dt <- S.recieveN' $ m^.dataSize
  83. r <- liftIO $ f m dt
  84. case r of
  85. Left e -> return . Left $ e
  86. Right (mo, dto) -> do
  87. S.send $ renderMetadata mo
  88. S.send' dto
  89. return . Right $ ()
  90. runUIOOnce :: UniformIO u => u -> UIOHandler -> IO (Either String ())
  91. runUIOOnce u f = S.withTarget u $ do
  92. m' <- S.runAttoparsec parseMetadata
  93. case m' of
  94. Left e -> return . Left . show $ e
  95. Right m -> do
  96. r <- f m
  97. case r of
  98. Left e -> return . Left $ e
  99. Right (mo, dto) -> do
  100. S.send $ renderMetadata mo
  101. S.send' dto
  102. return . Right $ ()
  103. runTcp :: String -> Int -> LBS.ByteString -> IO LBS.ByteString
  104. runTcp host port dt = do
  105. h <- connectTo host (PortNumber . fromIntegral $ port)
  106. bkgTrans h dt
  107. bkgTrans :: Handle -> LBS.ByteString -> IO LBS.ByteString
  108. bkgTrans h dt = do
  109. hSetNewlineMode h noNewlineTranslation
  110. LBS.hPut h dt
  111. hFlush h
  112. LBS.hGetContents h
  113. runExec :: String -> [String] -> LBS.ByteString -> IO LBS.ByteString
  114. runExec f args dt = do
  115. (Just i, Just o, _, _) <- P.createProcess (
  116. (P.proc f args){P.std_in=P.CreatePipe}{
  117. P.std_out=P.CreatePipe}{P.std_err=P.Inherit}
  118. )
  119. mapM_ (`hSetNewlineMode` noNewlineTranslation) [i, o]
  120. LBS.hPut i dt
  121. hFlush i
  122. LBS.hGetContents o