module Mongrel2.Parser ( ClientID , Message , Path , RequestHeaders , RequestBody , UUID , messageParser ) where import Data.Aeson (Value(..), json) import Data.Attoparsec import Data.Attoparsec.Char8 (decimal) import Data.ByteString (ByteString) import Data.Text (Text) import Data.Text.Encoding (encodeUtf8) import Data.Word (Word8) import Mongrel2.Types (UUID, ClientID) import Network.HTTP.Types (Query, decodePath) import Prelude hiding (take) import qualified Data.Map as M type Path = ByteString type RequestHeaders = Value type RequestBody = ByteString type Message = (UUID, ClientID, Path, RequestHeaders, RequestBody, ([Text], Query)) -- Predicates for parsing. isSpace :: Word8 -> Bool isSpace 0x20 = True isSpace _ = False isColon :: Word8 -> Bool isColon 0x3a = True isColon _ = False isComma :: Word8 -> Bool isComma 0x2c = True isComma _ = False -- Skip spaces. skipSpace :: Parser () skipSpace = skipWhile isSpace -- Parse a UUID. uuidParser :: Parser ByteString uuidParser = takeWhile1 $ \w -> ((w >= 65) && (w <= 90 )) || -- A-Z ((w >= 97) && (w <= 122)) || -- a-z ((w >= 48) && (w <= 57 )) || -- 0-9 (w == 45) -- Dash netstringParser :: (Int -> Parser a) -> Parser a netstringParser contentParser = do len <- decimal skip isColon str <- contentParser len return str messageParser :: Parser Message messageParser = do uuid <- uuidParser skipSpace clientId <- decimal skipSpace path <- takeTill isSpace skipSpace reqHdr <- netstringParser $ \_ -> json skip isComma reqBody <- netstringParser take return (uuid,clientId,path,reqHdr,reqBody,extractQuery reqHdr) extractQuery :: Value -> ([Text], Query) extractQuery (Object hdrs) = case M.lookup "URI" hdrs of Just (String uriText) -> decodePath $ encodeUtf8 uriText _ -> fail "Missing/invalid 'URI' in headers received from Mongrel2" extractQuery _ = fail "Invalid headers received from Mongrel2"