Backend.hs 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. {-# LANGUAGE OverloadedStrings #-}
  2. module Walrus.Backend (
  3. Backend(..),
  4. module Walrus.Backend.Metadata,
  5. parseBackend,
  6. runBackend
  7. ) where
  8. import qualified Data.Attoparsec.ByteString.Char8 as A
  9. import qualified Data.Attoparsec.ByteString.Lazy as LA
  10. import Data.Attoparsec.ByteString.Char8.Extras
  11. import qualified Data.ByteString.Lazy as LBS
  12. import qualified Data.Char as C
  13. import Data.Text.IsText
  14. import Walrus.Backend.Metadata
  15. import Control.Applicative
  16. import Network
  17. import System.IO
  18. import qualified System.Process as P
  19. data Backend = TCPBackend String Int | UnixSocketBackend String |
  20. ExecBackend String [String] deriving (Show, Read, Ord, Eq)
  21. parseBackend :: A.Parser Backend
  22. parseBackend = do
  23. A.choice [
  24. do
  25. tp "tcp"
  26. h <- tillSpace
  27. p <- A.decimal
  28. return $ TCPBackend h p,
  29. do
  30. tp "unix"
  31. tp "socket"
  32. p <- qStr
  33. return $ UnixSocketBackend p,
  34. do
  35. tp "exec"
  36. f <- qStr
  37. pp <- parseParameters
  38. return $ ExecBackend f pp
  39. ]
  40. where
  41. tp t = do
  42. skipHorizontalSpace
  43. A.stringCI t
  44. skipHorizontalSpace
  45. tillSpace :: A.Parser String
  46. tillSpace = fromText <$> A.takeTill C.isSpace
  47. qStr :: A.Parser String
  48. qStr = fromText <$> quotedString '\\' " " ""
  49. parseParameters = A.many' $ do
  50. p <- qStr
  51. skipHorizontalSpace
  52. return p
  53. runBackend :: Backend -> (Metadata, LBS.ByteString) -> IO (Either String (Metadata, LBS.ByteString))
  54. runBackend b (m, qdt) = case renderMetadata m of
  55. Nothing -> return $ Left "Metadata error"
  56. Just rm -> do
  57. edt' <- intBk b $ LBS.concat [
  58. fromText rm,
  59. "\r\n",
  60. qdt]
  61. case LA.parse repParse edt' of
  62. LA.Fail _ _ e -> return $ Left e
  63. LA.Done edt m' -> return $ Right (m', edt)
  64. where
  65. intBk :: Backend -> LBS.ByteString -> IO LBS.ByteString
  66. intBk (TCPBackend h p) = runTcp h p
  67. intBk (UnixSocketBackend f) = runUnix f
  68. intBk (ExecBackend f aa) = runExec f aa
  69. repParse = do
  70. m' <- parseMetadata
  71. A.endOfLine
  72. return m'
  73. runTcp :: String -> Int -> LBS.ByteString -> IO LBS.ByteString
  74. runTcp host port dt = do
  75. h <- connectTo host (PortNumber . fromIntegral $ port)
  76. bkgTrans h dt
  77. runUnix :: String -> LBS.ByteString -> IO LBS.ByteString
  78. runUnix path dt = do
  79. h <- connectTo "localhost" (UnixSocket path)
  80. bkgTrans h dt
  81. bkgTrans :: Handle -> LBS.ByteString -> IO LBS.ByteString
  82. bkgTrans h dt = do
  83. hSetNewlineMode h noNewlineTranslation
  84. LBS.hPut h dt
  85. hFlush h
  86. LBS.hGetContents h
  87. runExec :: String -> [String] -> LBS.ByteString -> IO LBS.ByteString
  88. runExec f args dt = do
  89. (Just i, Just o, _, _) <- P.createProcess (
  90. (P.proc f args){P.std_in=P.CreatePipe}{
  91. P.std_out=P.CreatePipe}{P.std_err=P.Inherit}
  92. )
  93. mapM (\h -> hSetNewlineMode h noNewlineTranslation) [i, o]
  94. LBS.hPut i dt
  95. hFlush i
  96. LBS.hGetContents o