Backend.hs 3.3 KB

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