module Mongrel2.Parser
( ClientID
, UUID
, messageParser
) where
import Data.Attoparsec
import Data.Attoparsec.Char8 (decimal)
import Data.ByteString (ByteString, uncons)
import Data.CaseInsensitive (CI)
import Data.Map (Map)
import Data.Maybe (catMaybes)
import Data.Text (Text)
import Data.Word (Word8)
import Mongrel2.Tnetstring (parseTnetstring1, TValue(..))
import Mongrel2.Types (ClientID, Connection(..), Request(..), UUID)
import Network.HTTP.Types (HttpVersion, Method, Query, RequestHeaders, decodePath, http09, http10, http11, parseMethod)
import Prelude hiding (take)
import qualified Data.CaseInsensitive as CI
import qualified Data.Map as M
isSpace :: Word8 -> Bool
isSpace 0x20 = True
isSpace _ = False
skipSpace :: Parser ()
skipSpace = skipWhile isSpace
uuidParser :: Parser ByteString
uuidParser = takeWhile1 $ \w -> ((w >= 65) && (w <= 90 )) ||
((w >= 97) && (w <= 122)) ||
((w >= 48) && (w <= 57 )) ||
(w == 45)
messageParser :: Parser (Connection, Request)
messageParser = do
uuid <- uuidParser
skipSpace
clientId <- decimal
skipSpace
rawPath <- takeTill isSpace
skipSpace
rawReqHdr <- parseTnetstring1
rawBody <- parseTnetstring1
let connection = Connection uuid clientId
let reqHdr = unHeaders rawReqHdr
let method = parseMethod $ extractMethod reqHdr
let version = extractVersion reqHdr
let (path,query) = extractQuery reqHdr
return (connection, Request path query rawPath method version (extractHeaders reqHdr) (extractBody rawBody))
unHeaders :: TValue -> Map ByteString TValue
unHeaders (TDictionary m) = M.fromList m
unHeaders _ = error "Invalid headers received from Mongrel2"
extractBody :: TValue -> ByteString
extractBody (TString body) = body
extractBody _ = error "Invalid body received from Mongrel2"
extractQuery :: Map ByteString TValue -> ([Text], Query)
extractQuery hdrs =
case M.lookup "URI" hdrs of
Just (TString uriText) -> decodePath uriText
_ -> decodePath ""
extractMethod :: Map ByteString TValue -> Method
extractMethod hdrs =
case M.lookup "METHOD" hdrs of
Just (TString methodText) -> methodText
_ -> error "Missing/invalid 'METHOD' in headers received from Mongrel2"
extractVersion :: Map ByteString TValue -> Maybe HttpVersion
extractVersion hdrs =
case M.lookup "VERSION" hdrs of
Just (TString "HTTP/0.9") -> Just http09
Just (TString "HTTP/1.0") -> Just http10
Just (TString "HTTP/1.1") -> Just $ http11
Just _ -> error "Unrecognized HTTP version"
Nothing -> Nothing
extractHeaders :: Map ByteString TValue -> RequestHeaders
extractHeaders hdrs = catMaybes $ map extractHdr $ M.toList hdrs
extractHdr :: (ByteString, TValue) -> Maybe (CI ByteString, ByteString)
extractHdr (t, TString v) = handleHdr t v
extractHdr _ = Nothing
handleHdr :: ByteString -> ByteString -> Maybe (CI ByteString, ByteString)
handleHdr k v =
case uncons k of
Just (c, _) | (c >= 65 && c <= 90) -> Nothing
_ -> Just (CI.mk k, v)