{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
module Web.Cookie
(
SetCookie
, setCookieName
, setCookieValue
, setCookiePath
, setCookieExpires
, setCookieMaxAge
, setCookieDomain
, setCookieHttpOnly
, setCookieSecure
, setCookieSameSite
, SameSiteOption
, sameSiteLax
, sameSiteStrict
, sameSiteNone
, parseSetCookie
, renderSetCookie
, renderSetCookieBS
, defaultSetCookie
, def
, Cookies
, parseCookies
, renderCookies
, renderCookiesBS
, CookiesText
, parseCookiesText
, renderCookiesText
, expiresFormat
, formatCookieExpires
, parseCookieExpires
) where
import qualified Data.ByteString as S
import qualified Data.ByteString.Char8 as S8
import qualified Data.ByteString.Lazy as L
import Data.Char (toLower, isDigit)
import Data.ByteString.Builder (Builder, byteString, char8, toLazyByteString)
import Data.ByteString.Builder.Extra (byteStringCopy)
#if !(MIN_VERSION_base(4,8,0))
import Data.Monoid (mempty, mappend, mconcat)
#endif
import Data.Word (Word8)
import Data.Ratio (numerator, denominator)
import Data.Time (UTCTime (UTCTime), toGregorian, fromGregorian, formatTime, parseTimeM, defaultTimeLocale)
import Data.Time.Clock (DiffTime, secondsToDiffTime)
import Control.Arrow (first, (***))
import Data.Text (Text)
import Data.Text.Encoding (encodeUtf8Builder, decodeUtf8With)
import Data.Text.Encoding.Error (lenientDecode)
import Data.Maybe (isJust)
import Data.Default.Class (Default (def))
import Control.DeepSeq (NFData (rnf))
type CookiesText = [(Text, Text)]
parseCookiesText :: S.ByteString -> CookiesText
parseCookiesText :: ByteString -> CookiesText
parseCookiesText =
((ByteString, ByteString) -> (Text, Text))
-> [(ByteString, ByteString)] -> CookiesText
forall a b. (a -> b) -> [a] -> [b]
map (ByteString -> Text
go (ByteString -> Text)
-> (ByteString -> Text) -> (ByteString, ByteString) -> (Text, Text)
forall b c b' c'. (b -> c) -> (b' -> c') -> (b, b') -> (c, c')
forall (a :: * -> * -> *) b c b' c'.
Arrow a =>
a b c -> a b' c' -> a (b, b') (c, c')
*** ByteString -> Text
go) ([(ByteString, ByteString)] -> CookiesText)
-> (ByteString -> [(ByteString, ByteString)])
-> ByteString
-> CookiesText
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> [(ByteString, ByteString)]
parseCookies
where
go :: ByteString -> Text
go = OnDecodeError -> ByteString -> Text
decodeUtf8With OnDecodeError
lenientDecode
renderCookiesText :: CookiesText -> Builder
renderCookiesText :: CookiesText -> Builder
renderCookiesText = [CookieBuilder] -> Builder
renderCookiesBuilder ([CookieBuilder] -> Builder)
-> (CookiesText -> [CookieBuilder]) -> CookiesText -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Text, Text) -> CookieBuilder) -> CookiesText -> [CookieBuilder]
forall a b. (a -> b) -> [a] -> [b]
map (Text -> Builder
encodeUtf8Builder (Text -> Builder)
-> (Text -> Builder) -> (Text, Text) -> CookieBuilder
forall b c b' c'. (b -> c) -> (b' -> c') -> (b, b') -> (c, c')
forall (a :: * -> * -> *) b c b' c'.
Arrow a =>
a b c -> a b' c' -> a (b, b') (c, c')
*** Text -> Builder
encodeUtf8Builder)
type Cookies = [(S.ByteString, S.ByteString)]
parseCookies :: S.ByteString -> Cookies
parseCookies :: ByteString -> [(ByteString, ByteString)]
parseCookies ByteString
s
| ByteString -> Bool
S.null ByteString
s = []
| Bool
otherwise =
let (ByteString
x, ByteString
y) = Word8 -> ByteString -> (ByteString, ByteString)
breakDiscard Word8
59 ByteString
s
in ByteString -> (ByteString, ByteString)
parseCookie ByteString
x (ByteString, ByteString)
-> [(ByteString, ByteString)] -> [(ByteString, ByteString)]
forall a. a -> [a] -> [a]
: ByteString -> [(ByteString, ByteString)]
parseCookies ByteString
y
parseCookie :: S.ByteString -> (S.ByteString, S.ByteString)
parseCookie :: ByteString -> (ByteString, ByteString)
parseCookie ByteString
s =
let (ByteString
key, ByteString
value) = Word8 -> ByteString -> (ByteString, ByteString)
breakDiscard Word8
61 ByteString
s
key' :: ByteString
key' = (Word8 -> Bool) -> ByteString -> ByteString
S.dropWhile (Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
32) ByteString
key
value' :: ByteString
value' = Word8 -> ByteString -> ByteString
dropEnds Word8
34 ByteString
value
in (ByteString
key', ByteString
value')
breakDiscard :: Word8 -> S.ByteString -> (S.ByteString, S.ByteString)
breakDiscard :: Word8 -> ByteString -> (ByteString, ByteString)
breakDiscard Word8
w ByteString
s =
let (ByteString
x, ByteString
y) = (Word8 -> Bool) -> ByteString -> (ByteString, ByteString)
S.break (Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
w) ByteString
s
in (ByteString
x, Int -> ByteString -> ByteString
S.drop Int
1 ByteString
y)
dropEnds :: Word8 -> S.ByteString -> S.ByteString
dropEnds :: Word8 -> ByteString -> ByteString
dropEnds Word8
w ByteString
s =
case ByteString -> Maybe (ByteString, Word8)
S.unsnoc ByteString
s of
Just (ByteString
s', Word8
w') | Word8
w' Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
w -> case ByteString -> Maybe (Word8, ByteString)
S.uncons ByteString
s' of
Just (Word8
w'', ByteString
s'') | Word8
w'' Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
w -> ByteString
s''
Maybe (Word8, ByteString)
_ -> ByteString
s
Maybe (ByteString, Word8)
_ -> ByteString
s
type CookieBuilder = (Builder, Builder)
renderCookiesBuilder :: [CookieBuilder] -> Builder
renderCookiesBuilder :: [CookieBuilder] -> Builder
renderCookiesBuilder [] = Builder
forall a. Monoid a => a
mempty
renderCookiesBuilder [CookieBuilder]
cs =
(Builder -> Builder -> Builder) -> [Builder] -> Builder
forall a. (a -> a -> a) -> [a] -> a
forall (t :: * -> *) a. Foldable t => (a -> a -> a) -> t a -> a
foldr1 Builder -> Builder -> Builder
go ([Builder] -> Builder) -> [Builder] -> Builder
forall a b. (a -> b) -> a -> b
$ (CookieBuilder -> Builder) -> [CookieBuilder] -> [Builder]
forall a b. (a -> b) -> [a] -> [b]
map CookieBuilder -> Builder
renderCookie [CookieBuilder]
cs
where
go :: Builder -> Builder -> Builder
go Builder
x Builder
y = Builder
x Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend` Char -> Builder
char8 Char
';' Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend` Builder
y
renderCookie :: CookieBuilder -> Builder
renderCookie :: CookieBuilder -> Builder
renderCookie (Builder
k, Builder
v) = Builder
k Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend` Char -> Builder
char8 Char
'=' Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend` Builder
v
renderCookies :: Cookies -> Builder
renderCookies :: [(ByteString, ByteString)] -> Builder
renderCookies = [CookieBuilder] -> Builder
renderCookiesBuilder ([CookieBuilder] -> Builder)
-> ([(ByteString, ByteString)] -> [CookieBuilder])
-> [(ByteString, ByteString)]
-> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((ByteString, ByteString) -> CookieBuilder)
-> [(ByteString, ByteString)] -> [CookieBuilder]
forall a b. (a -> b) -> [a] -> [b]
map (ByteString -> Builder
byteString (ByteString -> Builder)
-> (ByteString -> Builder)
-> (ByteString, ByteString)
-> CookieBuilder
forall b c b' c'. (b -> c) -> (b' -> c') -> (b, b') -> (c, c')
forall (a :: * -> * -> *) b c b' c'.
Arrow a =>
a b c -> a b' c' -> a (b, b') (c, c')
*** ByteString -> Builder
byteString)
renderCookiesBS :: Cookies -> S.ByteString
renderCookiesBS :: [(ByteString, ByteString)] -> ByteString
renderCookiesBS = ByteString -> ByteString
L.toStrict (ByteString -> ByteString)
-> ([(ByteString, ByteString)] -> ByteString)
-> [(ByteString, ByteString)]
-> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> ByteString
toLazyByteString (Builder -> ByteString)
-> ([(ByteString, ByteString)] -> Builder)
-> [(ByteString, ByteString)]
-> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(ByteString, ByteString)] -> Builder
renderCookies
data SetCookie = SetCookie
{ SetCookie -> ByteString
setCookieName :: S.ByteString
, SetCookie -> ByteString
setCookieValue :: S.ByteString
, SetCookie -> Maybe ByteString
setCookiePath :: Maybe S.ByteString
, SetCookie -> Maybe UTCTime
setCookieExpires :: Maybe UTCTime
, SetCookie -> Maybe DiffTime
setCookieMaxAge :: Maybe DiffTime
, SetCookie -> Maybe ByteString
setCookieDomain :: Maybe S.ByteString
, SetCookie -> Bool
setCookieHttpOnly :: Bool
, SetCookie -> Bool
setCookieSecure :: Bool
, SetCookie -> Maybe SameSiteOption
setCookieSameSite :: Maybe SameSiteOption
}
deriving (SetCookie -> SetCookie -> Bool
(SetCookie -> SetCookie -> Bool)
-> (SetCookie -> SetCookie -> Bool) -> Eq SetCookie
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: SetCookie -> SetCookie -> Bool
== :: SetCookie -> SetCookie -> Bool
$c/= :: SetCookie -> SetCookie -> Bool
/= :: SetCookie -> SetCookie -> Bool
Eq, Int -> SetCookie -> ShowS
[SetCookie] -> ShowS
SetCookie -> String
(Int -> SetCookie -> ShowS)
-> (SetCookie -> String)
-> ([SetCookie] -> ShowS)
-> Show SetCookie
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SetCookie -> ShowS
showsPrec :: Int -> SetCookie -> ShowS
$cshow :: SetCookie -> String
show :: SetCookie -> String
$cshowList :: [SetCookie] -> ShowS
showList :: [SetCookie] -> ShowS
Show)
data SameSiteOption = Lax
| Strict
| None
deriving (Int -> SameSiteOption -> ShowS
[SameSiteOption] -> ShowS
SameSiteOption -> String
(Int -> SameSiteOption -> ShowS)
-> (SameSiteOption -> String)
-> ([SameSiteOption] -> ShowS)
-> Show SameSiteOption
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SameSiteOption -> ShowS
showsPrec :: Int -> SameSiteOption -> ShowS
$cshow :: SameSiteOption -> String
show :: SameSiteOption -> String
$cshowList :: [SameSiteOption] -> ShowS
showList :: [SameSiteOption] -> ShowS
Show, SameSiteOption -> SameSiteOption -> Bool
(SameSiteOption -> SameSiteOption -> Bool)
-> (SameSiteOption -> SameSiteOption -> Bool) -> Eq SameSiteOption
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: SameSiteOption -> SameSiteOption -> Bool
== :: SameSiteOption -> SameSiteOption -> Bool
$c/= :: SameSiteOption -> SameSiteOption -> Bool
/= :: SameSiteOption -> SameSiteOption -> Bool
Eq)
instance NFData SameSiteOption where
rnf :: SameSiteOption -> ()
rnf SameSiteOption
x = SameSiteOption
x SameSiteOption -> () -> ()
forall a b. a -> b -> b
`seq` ()
sameSiteLax :: SameSiteOption
sameSiteLax :: SameSiteOption
sameSiteLax = SameSiteOption
Lax
sameSiteStrict :: SameSiteOption
sameSiteStrict :: SameSiteOption
sameSiteStrict = SameSiteOption
Strict
sameSiteNone :: SameSiteOption
sameSiteNone :: SameSiteOption
sameSiteNone = SameSiteOption
None
instance NFData SetCookie where
rnf :: SetCookie -> ()
rnf (SetCookie ByteString
a ByteString
b Maybe ByteString
c Maybe UTCTime
d Maybe DiffTime
e Maybe ByteString
f Bool
g Bool
h Maybe SameSiteOption
i) =
ByteString
a ByteString -> () -> ()
forall a b. a -> b -> b
`seq`
ByteString
b ByteString -> () -> ()
forall a b. a -> b -> b
`seq`
Maybe ByteString -> ()
forall {a}. Maybe a -> ()
rnfMBS Maybe ByteString
c () -> () -> ()
forall a b. a -> b -> b
`seq`
Maybe UTCTime -> ()
forall a. NFData a => a -> ()
rnf Maybe UTCTime
d () -> () -> ()
forall a b. a -> b -> b
`seq`
Maybe DiffTime -> ()
forall a. NFData a => a -> ()
rnf Maybe DiffTime
e () -> () -> ()
forall a b. a -> b -> b
`seq`
Maybe ByteString -> ()
forall {a}. Maybe a -> ()
rnfMBS Maybe ByteString
f () -> () -> ()
forall a b. a -> b -> b
`seq`
Bool -> ()
forall a. NFData a => a -> ()
rnf Bool
g () -> () -> ()
forall a b. a -> b -> b
`seq`
Bool -> ()
forall a. NFData a => a -> ()
rnf Bool
h () -> () -> ()
forall a b. a -> b -> b
`seq`
Maybe SameSiteOption -> ()
forall a. NFData a => a -> ()
rnf Maybe SameSiteOption
i
where
rnfMBS :: Maybe a -> ()
rnfMBS Maybe a
Nothing = ()
rnfMBS (Just a
bs) = a
bs a -> () -> ()
forall a b. a -> b -> b
`seq` ()
instance Default SetCookie where
def :: SetCookie
def = SetCookie
defaultSetCookie
defaultSetCookie :: SetCookie
defaultSetCookie :: SetCookie
defaultSetCookie = SetCookie
{ setCookieName :: ByteString
setCookieName = ByteString
"name"
, setCookieValue :: ByteString
setCookieValue = ByteString
"value"
, setCookiePath :: Maybe ByteString
setCookiePath = Maybe ByteString
forall a. Maybe a
Nothing
, setCookieExpires :: Maybe UTCTime
setCookieExpires = Maybe UTCTime
forall a. Maybe a
Nothing
, setCookieMaxAge :: Maybe DiffTime
setCookieMaxAge = Maybe DiffTime
forall a. Maybe a
Nothing
, setCookieDomain :: Maybe ByteString
setCookieDomain = Maybe ByteString
forall a. Maybe a
Nothing
, setCookieHttpOnly :: Bool
setCookieHttpOnly = Bool
False
, setCookieSecure :: Bool
setCookieSecure = Bool
False
, setCookieSameSite :: Maybe SameSiteOption
setCookieSameSite = Maybe SameSiteOption
forall a. Maybe a
Nothing
}
renderSetCookie :: SetCookie -> Builder
renderSetCookie :: SetCookie -> Builder
renderSetCookie SetCookie
sc = [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat
[ ByteString -> Builder
byteString (SetCookie -> ByteString
setCookieName SetCookie
sc)
, Char -> Builder
char8 Char
'='
, ByteString -> Builder
byteString (SetCookie -> ByteString
setCookieValue SetCookie
sc)
, case SetCookie -> Maybe ByteString
setCookiePath SetCookie
sc of
Maybe ByteString
Nothing -> Builder
forall a. Monoid a => a
mempty
Just ByteString
path -> ByteString -> Builder
byteStringCopy ByteString
"; Path="
Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend` ByteString -> Builder
byteString ByteString
path
, case SetCookie -> Maybe UTCTime
setCookieExpires SetCookie
sc of
Maybe UTCTime
Nothing -> Builder
forall a. Monoid a => a
mempty
Just UTCTime
e -> ByteString -> Builder
byteStringCopy ByteString
"; Expires=" Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend`
ByteString -> Builder
byteString (UTCTime -> ByteString
formatCookieExpires UTCTime
e)
, case SetCookie -> Maybe DiffTime
setCookieMaxAge SetCookie
sc of
Maybe DiffTime
Nothing -> Builder
forall a. Monoid a => a
mempty
Just DiffTime
ma -> ByteString -> Builder
byteStringCopyByteString
"; Max-Age=" Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend`
ByteString -> Builder
byteString (DiffTime -> ByteString
formatCookieMaxAge DiffTime
ma)
, case SetCookie -> Maybe ByteString
setCookieDomain SetCookie
sc of
Maybe ByteString
Nothing -> Builder
forall a. Monoid a => a
mempty
Just ByteString
d -> ByteString -> Builder
byteStringCopy ByteString
"; Domain=" Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend`
ByteString -> Builder
byteString ByteString
d
, if SetCookie -> Bool
setCookieHttpOnly SetCookie
sc
then ByteString -> Builder
byteStringCopy ByteString
"; HttpOnly"
else Builder
forall a. Monoid a => a
mempty
, if SetCookie -> Bool
setCookieSecure SetCookie
sc
then ByteString -> Builder
byteStringCopy ByteString
"; Secure"
else Builder
forall a. Monoid a => a
mempty
, case SetCookie -> Maybe SameSiteOption
setCookieSameSite SetCookie
sc of
Maybe SameSiteOption
Nothing -> Builder
forall a. Monoid a => a
mempty
Just SameSiteOption
Lax -> ByteString -> Builder
byteStringCopy ByteString
"; SameSite=Lax"
Just SameSiteOption
Strict -> ByteString -> Builder
byteStringCopy ByteString
"; SameSite=Strict"
Just SameSiteOption
None -> ByteString -> Builder
byteStringCopy ByteString
"; SameSite=None"
]
renderSetCookieBS :: SetCookie -> S.ByteString
renderSetCookieBS :: SetCookie -> ByteString
renderSetCookieBS = ByteString -> ByteString
L.toStrict (ByteString -> ByteString)
-> (SetCookie -> ByteString) -> SetCookie -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> ByteString
toLazyByteString (Builder -> ByteString)
-> (SetCookie -> Builder) -> SetCookie -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SetCookie -> Builder
renderSetCookie
parseSetCookie :: S.ByteString -> SetCookie
parseSetCookie :: ByteString -> SetCookie
parseSetCookie ByteString
a = SetCookie
{ setCookieName :: ByteString
setCookieName = ByteString
name
, setCookieValue :: ByteString
setCookieValue = Word8 -> ByteString -> ByteString
dropEnds Word8
34 ByteString
value
, setCookiePath :: Maybe ByteString
setCookiePath = ByteString -> [(ByteString, ByteString)] -> Maybe ByteString
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup ByteString
"path" [(ByteString, ByteString)]
flags
, setCookieExpires :: Maybe UTCTime
setCookieExpires =
ByteString -> [(ByteString, ByteString)] -> Maybe ByteString
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup ByteString
"expires" [(ByteString, ByteString)]
flags Maybe ByteString -> (ByteString -> Maybe UTCTime) -> Maybe UTCTime
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= ByteString -> Maybe UTCTime
parseCookieExpires
, setCookieMaxAge :: Maybe DiffTime
setCookieMaxAge =
ByteString -> [(ByteString, ByteString)] -> Maybe ByteString
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup ByteString
"max-age" [(ByteString, ByteString)]
flags Maybe ByteString
-> (ByteString -> Maybe DiffTime) -> Maybe DiffTime
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= ByteString -> Maybe DiffTime
parseCookieMaxAge
, setCookieDomain :: Maybe ByteString
setCookieDomain = ByteString -> [(ByteString, ByteString)] -> Maybe ByteString
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup ByteString
"domain" [(ByteString, ByteString)]
flags
, setCookieHttpOnly :: Bool
setCookieHttpOnly = Maybe ByteString -> Bool
forall a. Maybe a -> Bool
isJust (Maybe ByteString -> Bool) -> Maybe ByteString -> Bool
forall a b. (a -> b) -> a -> b
$ ByteString -> [(ByteString, ByteString)] -> Maybe ByteString
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup ByteString
"httponly" [(ByteString, ByteString)]
flags
, setCookieSecure :: Bool
setCookieSecure = Maybe ByteString -> Bool
forall a. Maybe a -> Bool
isJust (Maybe ByteString -> Bool) -> Maybe ByteString -> Bool
forall a b. (a -> b) -> a -> b
$ ByteString -> [(ByteString, ByteString)] -> Maybe ByteString
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup ByteString
"secure" [(ByteString, ByteString)]
flags
, setCookieSameSite :: Maybe SameSiteOption
setCookieSameSite = case ByteString -> [(ByteString, ByteString)] -> Maybe ByteString
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup ByteString
"samesite" [(ByteString, ByteString)]
flags of
Just ByteString
"Lax" -> SameSiteOption -> Maybe SameSiteOption
forall a. a -> Maybe a
Just SameSiteOption
Lax
Just ByteString
"Strict" -> SameSiteOption -> Maybe SameSiteOption
forall a. a -> Maybe a
Just SameSiteOption
Strict
Just ByteString
"None" -> SameSiteOption -> Maybe SameSiteOption
forall a. a -> Maybe a
Just SameSiteOption
None
Maybe ByteString
_ -> Maybe SameSiteOption
forall a. Maybe a
Nothing
}
where
pairs :: [(ByteString, ByteString)]
pairs = (ByteString -> (ByteString, ByteString))
-> [ByteString] -> [(ByteString, ByteString)]
forall a b. (a -> b) -> [a] -> [b]
map (ByteString -> (ByteString, ByteString)
parsePair (ByteString -> (ByteString, ByteString))
-> (ByteString -> ByteString)
-> ByteString
-> (ByteString, ByteString)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
dropSpace) ([ByteString] -> [(ByteString, ByteString)])
-> [ByteString] -> [(ByteString, ByteString)]
forall a b. (a -> b) -> a -> b
$ Word8 -> ByteString -> [ByteString]
S.split Word8
59 ByteString
a [ByteString] -> [ByteString] -> [ByteString]
forall a. [a] -> [a] -> [a]
++ [ByteString
S8.empty]
(ByteString
name, ByteString
value) = [(ByteString, ByteString)] -> (ByteString, ByteString)
forall a. HasCallStack => [a] -> a
head [(ByteString, ByteString)]
pairs
flags :: [(ByteString, ByteString)]
flags = ((ByteString, ByteString) -> (ByteString, ByteString))
-> [(ByteString, ByteString)] -> [(ByteString, ByteString)]
forall a b. (a -> b) -> [a] -> [b]
map ((ByteString -> ByteString)
-> (ByteString, ByteString) -> (ByteString, ByteString)
forall b c d. (b -> c) -> (b, d) -> (c, d)
forall (a :: * -> * -> *) b c d.
Arrow a =>
a b c -> a (b, d) (c, d)
first ((Char -> Char) -> ByteString -> ByteString
S8.map Char -> Char
toLower)) ([(ByteString, ByteString)] -> [(ByteString, ByteString)])
-> [(ByteString, ByteString)] -> [(ByteString, ByteString)]
forall a b. (a -> b) -> a -> b
$ [(ByteString, ByteString)] -> [(ByteString, ByteString)]
forall a. HasCallStack => [a] -> [a]
tail [(ByteString, ByteString)]
pairs
parsePair :: ByteString -> (ByteString, ByteString)
parsePair = Word8 -> ByteString -> (ByteString, ByteString)
breakDiscard Word8
61
dropSpace :: ByteString -> ByteString
dropSpace = (Word8 -> Bool) -> ByteString -> ByteString
S.dropWhile (Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
32)
expiresFormat :: String
expiresFormat :: String
expiresFormat = String
"%a, %d-%b-%Y %X GMT"
formatCookieExpires :: UTCTime -> S.ByteString
formatCookieExpires :: UTCTime -> ByteString
formatCookieExpires =
String -> ByteString
S8.pack (String -> ByteString)
-> (UTCTime -> String) -> UTCTime -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TimeLocale -> String -> UTCTime -> String
forall t. FormatTime t => TimeLocale -> String -> t -> String
formatTime TimeLocale
defaultTimeLocale String
expiresFormat
parseCookieExpires :: S.ByteString -> Maybe UTCTime
parseCookieExpires :: ByteString -> Maybe UTCTime
parseCookieExpires =
(UTCTime -> UTCTime) -> Maybe UTCTime -> Maybe UTCTime
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap UTCTime -> UTCTime
fuzzYear (Maybe UTCTime -> Maybe UTCTime)
-> (ByteString -> Maybe UTCTime) -> ByteString -> Maybe UTCTime
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> TimeLocale -> String -> String -> Maybe UTCTime
forall (m :: * -> *) t.
(MonadFail m, ParseTime t) =>
Bool -> TimeLocale -> String -> String -> m t
parseTimeM Bool
True TimeLocale
defaultTimeLocale String
expiresFormat (String -> Maybe UTCTime)
-> (ByteString -> String) -> ByteString -> Maybe UTCTime
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> String
S8.unpack
where
fuzzYear :: UTCTime -> UTCTime
fuzzYear orig :: UTCTime
orig@(UTCTime Day
day DiffTime
diff)
| Integer
x Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
>= Integer
70 Bool -> Bool -> Bool
&& Integer
x Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Integer
99 = Integer -> UTCTime
addYear Integer
1900
| Integer
x Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
>= Integer
0 Bool -> Bool -> Bool
&& Integer
x Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Integer
69 = Integer -> UTCTime
addYear Integer
2000
| Bool
otherwise = UTCTime
orig
where
(Integer
x, Int
y, Int
z) = Day -> (Integer, Int, Int)
toGregorian Day
day
addYear :: Integer -> UTCTime
addYear Integer
x' = Day -> DiffTime -> UTCTime
UTCTime (Integer -> Int -> Int -> Day
fromGregorian (Integer
x Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
x') Int
y Int
z) DiffTime
diff
formatCookieMaxAge :: DiffTime -> S.ByteString
formatCookieMaxAge :: DiffTime -> ByteString
formatCookieMaxAge DiffTime
difftime = String -> ByteString
S8.pack (String -> ByteString) -> String -> ByteString
forall a b. (a -> b) -> a -> b
$ Integer -> String
forall a. Show a => a -> String
show (Integer
num Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
denom)
where rational :: Rational
rational = DiffTime -> Rational
forall a. Real a => a -> Rational
toRational DiffTime
difftime
num :: Integer
num = Rational -> Integer
forall a. Ratio a -> a
numerator Rational
rational
denom :: Integer
denom = Rational -> Integer
forall a. Ratio a -> a
denominator Rational
rational
parseCookieMaxAge :: S.ByteString -> Maybe DiffTime
parseCookieMaxAge :: ByteString -> Maybe DiffTime
parseCookieMaxAge ByteString
bs
| (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isDigit String
unpacked = DiffTime -> Maybe DiffTime
forall a. a -> Maybe a
Just (DiffTime -> Maybe DiffTime) -> DiffTime -> Maybe DiffTime
forall a b. (a -> b) -> a -> b
$ Integer -> DiffTime
secondsToDiffTime (Integer -> DiffTime) -> Integer -> DiffTime
forall a b. (a -> b) -> a -> b
$ String -> Integer
forall a. Read a => String -> a
read String
unpacked
| Bool
otherwise = Maybe DiffTime
forall a. Maybe a
Nothing
where unpacked :: String
unpacked = ByteString -> String
S8.unpack ByteString
bs