Backend.hs 4.5 KB

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