Backend.hs 2.8 KB

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