{-# LANGUAGE OverloadedStrings #-} module RFC2616 ( Request(..) , Header(..) , isToken , requestLine , messageHeader , request ) where import Control.Applicative hiding (many) import Data.Attoparsec as P import Data.Attoparsec.Char8 (char8, endOfLine, isEndOfLine, isHorizontalSpace) import Data.Word (Word8) import qualified Data.ByteString.Char8 as B isToken :: Word8 -> Bool isToken w = notInClass "\0-\31()<>@,;:\\\"/[]?={} \t\128-\255" w skipHSpaces :: Parser () skipHSpaces = satisfy isHorizontalSpace *> skipWhile isHorizontalSpace data Request = Request { requestMethod :: !B.ByteString , requestUri :: !B.ByteString , requestProtocol :: !B.ByteString } deriving (Eq, Ord, Show) requestLine :: Parser Request requestLine = do method <- P.takeWhile1 isToken <* skipHSpaces uri <- P.takeWhile1 (not . isHorizontalSpace) <* skipHSpaces <* string "HTTP/" proto <- P.takeWhile1 isHttpVersion <* endOfLine return $! Request method uri proto where isHttpVersion w = w == 46 || w == 48 || w == 49 data Header = Header { headerName :: !B.ByteString , headerValue :: [B.ByteString] } deriving (Eq, Ord, Show) messageHeader :: Parser Header messageHeader = do header <- P.takeWhile1 isToken <* char8 ':' <* skipHSpaces body <- takeTill isEndOfLine <* endOfLine conts <- many $ skipHSpaces *> takeTill isEndOfLine <* endOfLine return $! Header header (body:conts) request :: Parser (Request, [Header]) request = (,) <$> requestLine <*> many messageHeader <* endOfLine