Backend.hs 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  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 Control.Monad.IO.Class
  16. import Text.StringConvert
  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 = A.choice [
  31. do
  32. tp "tcp"
  33. h <- tillSpace
  34. p <- A.decimal
  35. return $ TCPBackend h p,
  36. do
  37. tp "exec"
  38. f <- qStr
  39. skipHorizontalSpace
  40. pp <- parseParameters
  41. return $ ExecBackend f pp
  42. ]
  43. where
  44. tp t = do
  45. skipHorizontalSpace
  46. A.stringCI t
  47. skipHorizontalSpace
  48. tillSpace :: A.Parser String
  49. tillSpace = s <$> A.takeTill C.isSpace
  50. parseParameters :: A.Parser [String]
  51. parseParameters = do
  52. p <- qStr
  53. skipHorizontalSpace
  54. if null p then return [] else do
  55. pp <- parseParameters
  56. return $ p : pp
  57. qStr :: A.Parser String
  58. qStr = s <$> quotedString '\\' " " ""
  59. callBackend :: Backend -> (Metadata, LBS.ByteString) -> IO (Either String (Metadata, LBS.ByteString))
  60. callBackend b (m, qdt) = do
  61. let rm = renderMetadata m
  62. edt' <- intBk b $ LBS.concat [
  63. s rm,
  64. qdt]
  65. case LA.parse repParse edt' of
  66. LA.Fail _ _ e -> return $ Left e
  67. LA.Done edt m' -> return $ Right (m', edt)
  68. where
  69. intBk :: Backend -> LBS.ByteString -> IO LBS.ByteString
  70. intBk (TCPBackend h p) = runTcp h p
  71. intBk (ExecBackend f aa) = runExec f aa
  72. repParse = parseMetadata
  73. runBackendOnce :: UniformIO u => u -> BackendHandler -> IO (Either String ())
  74. runBackendOnce u f = withTarget u $ do
  75. m' <- runAttoparsec parseMetadata
  76. case m' of
  77. Left e -> return . Left . show $ e
  78. Right m -> do
  79. dt <- recieveN' $ m^.dataSize
  80. r <- liftIO $ f m dt
  81. case r of
  82. Left e -> return . Left $ e
  83. Right (mo, dto) -> do
  84. send $ renderMetadata mo
  85. send' dto
  86. return . Right $ ()
  87. runTcp :: String -> Int -> LBS.ByteString -> IO LBS.ByteString
  88. runTcp host port dt = do
  89. h <- connectTo host (PortNumber . fromIntegral $ port)
  90. bkgTrans h dt
  91. bkgTrans :: Handle -> LBS.ByteString -> IO LBS.ByteString
  92. bkgTrans h dt = do
  93. hSetNewlineMode h noNewlineTranslation
  94. LBS.hPut h dt
  95. hFlush h
  96. LBS.hGetContents h
  97. runExec :: String -> [String] -> LBS.ByteString -> IO LBS.ByteString
  98. runExec f args dt = do
  99. (Just i, Just o, _, _) <- P.createProcess (
  100. (P.proc f args){P.std_in=P.CreatePipe}{
  101. P.std_out=P.CreatePipe}{P.std_err=P.Inherit}
  102. )
  103. mapM_ (`hSetNewlineMode` noNewlineTranslation) [i, o]
  104. LBS.hPut i dt
  105. hFlush i
  106. LBS.hGetContents o