module Network.HTTP.Lucu.Headers
( Headers
, HasHeaders(..)
)
where
import Control.Applicative hiding (empty)
import Control.Applicative.Unicode hiding ((∅))
import Control.Monad
import Data.Ascii (Ascii, AsciiBuilder, CIAscii)
import qualified Data.Ascii as A
import Data.Attoparsec.Char8
import qualified Data.Collections.Newtype.TH as C
import Data.Convertible.Base
import Data.Convertible.Instances.Ascii ()
import Data.Convertible.Utils
import Data.Default
import Data.List (intersperse)
import qualified Data.Map as M (Map)
import Data.Collections
import Data.Collections.BaseInstances ()
import Data.Monoid
import Data.Monoid.Unicode
import Network.HTTP.Lucu.Parser.Http
import Prelude hiding (lookup, null)
import Prelude.Unicode
newtype Headers
= Headers (M.Map CIAscii Ascii)
deriving (Eq, Show)
class HasHeaders a where
getHeaders ∷ a → Headers
setHeaders ∷ a → Headers → a
modifyHeaders ∷ (Headers → Headers) → a → a
modifyHeaders = (setHeaders ⊛) ∘ (∘ getHeaders)
getHeader ∷ CIAscii → a → Maybe Ascii
getHeader = (∘ getHeaders) ∘ lookup
hasHeader ∷ CIAscii → a → Bool
hasHeader = (∘ getHeaders) ∘ member
getCIHeader ∷ CIAscii → a → Maybe CIAscii
getCIHeader = ((cs <$>) ∘) ∘ getHeader
deleteHeader ∷ CIAscii → a → a
deleteHeader = modifyHeaders ∘ delete
setHeader ∷ CIAscii → Ascii → a → a
setHeader = (modifyHeaders ∘) ∘ insertWith const
instance HasHeaders Headers where
getHeaders = id
setHeaders _ = id
C.derive [d| instance Foldable Headers (CIAscii, Ascii)
instance Collection Headers (CIAscii, Ascii)
instance Indexed Headers CIAscii Ascii
instance Map Headers CIAscii Ascii
instance SortingCollection Headers (CIAscii, Ascii)
|]
instance Unfoldable Headers (CIAscii, Ascii) where
insert (key, val) (Headers m)
= Headers $ insertWith merge key val m
empty = Headers empty
singleton = Headers ∘ singleton
instance Monoid Headers where
mempty = empty
mappend = insertMany
merge ∷ Ascii → Ascii → Ascii
merge a b
| nullA a ∧ nullA b = (∅)
| nullA a = b
| nullA b = a
| otherwise = a ⊕ ", " ⊕ b
where
nullA ∷ Ascii → Bool
nullA = null ∘ A.toByteString
instance ConvertSuccess Headers Ascii where
convertSuccess = convertSuccessVia ((⊥) ∷ AsciiBuilder)
instance ConvertSuccess Headers AsciiBuilder where
convertSuccess (Headers m)
= mconcat (header <$> fromFoldable m) ⊕ cs ("\x0D\x0A" ∷ Ascii)
where
header ∷ (CIAscii, Ascii) → AsciiBuilder
header (name, value)
= cs name ⊕
cs (": " ∷ Ascii) ⊕
cs value ⊕
cs ("\x0D\x0A" ∷ Ascii)
deriveAttempts [ ([t| Headers |], [t| Ascii |])
, ([t| Headers |], [t| AsciiBuilder |])
]
instance Default (Parser Headers) where
def = do xs ← many header
crlf
return $ fromFoldable xs
where
header ∷ Parser (CIAscii, Ascii)
header = do name ← cs <$> token
void $ char ':'
skipMany lws
values ← content `sepBy` try lws
skipMany (try lws)
crlf
return (name, joinValues values)
content ∷ Parser Ascii
content = A.unsafeFromByteString
<$>
takeWhile1 (\c → isText c ∧ c ≢ '\x20')
joinValues ∷ [Ascii] → Ascii
joinValues = cs
∘ mconcat
∘ intersperse (cs ("\x20" ∷ Ascii) ∷ AsciiBuilder)
∘ (cs <$>)