module Wobsurv.Util.HTTP.Parser where import BasePrelude hiding (takeWhile, isSpace) import Control.Monad.Trans.Class import Control.Monad.Trans.Except import Data.Attoparsec.ByteString.Char8 import Wobsurv.Util.HTTP.Model import qualified Data.ByteString as ByteString import Data.ByteString (ByteString) type UnknownHeader = (ByteString, ByteString) labeling :: String -> Parser a -> Parser a labeling n p = p n head :: Parser (Method, RelativeURI, Version, [UnknownHeader]) head = labeling "head" $ (,,,) <$> (method <* space) <*> (relativeURI <* space) <*> (version <* endOfLine) <*> (many unknownHeader <* endOfLine) method :: Parser Method method = labeling "method" $ (Left <$> standardMethod) <|> (Right <$> takeWhile isAlpha) standardMethod :: Parser StandardMethod standardMethod = labeling "standardMethod" $ p "OPTIONS" Options <|> p "GET" Get <|> p "HEAD" Head <|> p "POST" Post <|> p "PUT" Put <|> p "DELETE" Delete <|> p "TRACE" Trace <|> p "CONNECT" Connect where p n m = string n *> pure m version :: Parser Version version = labeling "version" $ (,) <$> (string "HTTP/" *> decimal) <*> (char '.' *> decimal) relativeURI :: Parser RelativeURI relativeURI = labeling "relativeURI" $ (,,) <$> path <*> optional query <*> optional fragment where path = char '/' *> optional (takeWhile1 (\c -> c /= '?' && not (isSpace c))) query = char '?' *> takeWhile (\c -> c /= '#' && not (isSpace c)) fragment = char '#' *> takeWhile (not . isSpace) header :: Parser Header header = labeling "header" $ (ConnectionHeader <$> connectionHeader) <|> (ContentLengthHeader <$> contentLengthHeader) <|> (ContentTypeHeader <$> contentTypeHeader) <|> (KeepAliveHeader <$> keepAliveHeader) connectionHeader :: Parser ConnectionHeader connectionHeader = labeling "connectionHeader" $ string "Connection: " *> (keepAlive <|> close) where keepAlive = string "keep-alive" *> pure True close = string "close" *> pure False contentLengthHeader :: Parser ContentLengthHeader contentLengthHeader = labeling "contentLengthHeader" $ string "Content-Length: " *> decimal contentTypeHeader :: Parser ContentTypeHeader contentTypeHeader = labeling "contentTypeHeader" $ undefined keepAliveHeader :: Parser KeepAliveHeader keepAliveHeader = labeling "keepAliveHeader" $ undefined unknownHeader :: Parser UnknownHeader unknownHeader = labeling "unknownHeader" $ (,) <$> (key <* char ':' <* skipSpace) <*> value <* endOfLine where key = takeWhile1 (\c -> isAlphaNum c || c == '-') value = takeWhile1 (/= '\n')