web-mongrel2-0.0.3: src/Web/Mongrel2/Parsing.hs
{-# LANGUAGE OverloadedStrings #-}
module Web.Mongrel2.Parsing (m2_parse) where
import qualified Data.Text as T
import Control.Applicative hiding (many)
import Text.Parsec.Text
import Text.Parsec hiding ((<|>))
import Data.Default
import qualified Text.JSON as JS
import Web.Mongrel2.Types
m2_parse :: T.Text -> Either T.Text Request
m2_parse request =
case parse request_split "" request of
Right (uui,seqq,pat,blk) ->
case request_env blk of
Left e -> Left e
Right req ->
Right req { request_uuid = uui
, request_id = seqq
, request_path = pat }
Left a -> Left $ T.pack $ show a
where
request_split :: Parser (T.Text,T.Text,T.Text,T.Text)
request_split = do
uui <- many $ noneOf " "
_ <- space
iid <- many $ noneOf " "
_ <- space
path <- many $ noneOf " "
_ <- space
rest <- many anyToken
return (T.pack uui,T.pack iid,T.pack path,T.pack rest)
request_env :: T.Text -> Either T.Text Request
request_env request_body =
case parse qstr "" request_body of
Left x -> Left $ T.pack $ show x
Right (headers_,query_string_) ->
case JS.decode (T.unpack headers_) of
JS.Ok (JS.JSObject json) -> do
let unjs =
concat $
map (\(x,y') ->
case y' of
JS.JSString y -> [(T.pack x,T.pack $ JS.fromJSString y)]
_ -> []
) $ JS.fromJSObject json
Right $ def { request_path = ml "PATH" json
, request_method = string_to_method $ ml "METHOD" json
, request_version = ml "VERSION" json
, request_uri = ml "URI" json
, request_headers = unjs
, request_pattern = ml "PATTERN" json
, request_accept = ml "Accept" json
, request_host = ml "Host" json
, request_user_agent = ml "User-Agent" json
, request_query_string = query_string_
}
_ -> Left "error parsing the headers."
ml :: T.Text -> JS.JSObject JS.JSValue -> T.Text
ml k b = maybe "" id $ mlookup k b
qstr :: Parser (T.Text,T.Text)
qstr = do
n <- number
_ <- char ':'
x <- count n anyChar
_ <- char ','
nx <- number
_ <- char ':'
xy <- count nx anyChar
return (T.pack x,T.pack xy)
number :: Parser Int
number = many1 digit >>= (return . read)
mlookup :: T.Text -> JS.JSObject JS.JSValue -> Maybe T.Text
mlookup key bndl =
-- TODO: Not so sure that the alternate toLower is needed.
mlookup' key bndl <|> mlookup' (T.toLower key) bndl
where
mlookup' :: T.Text -> JS.JSObject JS.JSValue -> Maybe T.Text
mlookup' k b =
case JS.valFromObj (T.unpack k) b of
JS.Ok v -> Just $ T.pack $ JS.fromJSString v
_ -> Nothing
string_to_method :: T.Text -> RequestMethod
string_to_method "GET" = GET
string_to_method "POST" = POST
string_to_method "PUT" = PUT
string_to_method "DELETE" = DELETE
string_to_method "HEAD" = HEAD
string_to_method lx = error $ "Unknown method: " ++ T.unpack lx