mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-21 22:41:43 +03:00
bdacf1bd23
I spent half the day reducing a weird compile failure here https://github.com/hasura/graphql-engine-mono/pull/1593/files#r713102990 to this https://gitlab.haskell.org/ghc/ghc/-/issues/17768#note_378004. Seems ApplicativeDo makes a mess of non-applicative monadic do in some cases. Given our rather localized use of ApplicativeDo, seemed a good idea to remove it from the list of default extensions. It appears that ApplicativeDo also buries some unused return value warnings, so this PR also silences those. We should check that none of those warnings were warranted though. https://github.com/hasura/graphql-engine-mono/pull/2413 GitOrigin-RevId: 1874c1a82230431849265755b1407beebc947041
169 lines
5.8 KiB
Haskell
169 lines
5.8 KiB
Haskell
{-| Functions related to parsing @Cache-Control@ header as defined in
|
|
https://tools.ietf.org/html/rfc7234#section-5.2
|
|
|
|
To get @max-age@/@s-maxage@ from @Cache-Control@ header, use 'parseMaxAge'. If you need to check
|
|
other directives use 'parseCacheControl'.
|
|
|
|
Rules which starts with @obs-@ is not required to implement because they are maked as "obsolete" as
|
|
per https://tools.ietf.org/html/rfc7230#section-1.2
|
|
-}
|
|
|
|
module Data.Parser.CacheControl
|
|
( CacheControl
|
|
, CacheControlDirective (..)
|
|
, parseCacheControl
|
|
, parseMaxAge
|
|
)
|
|
where
|
|
|
|
import Hasura.Prelude
|
|
|
|
import Hasura.Server.Utils (fmapL)
|
|
|
|
import qualified Data.Attoparsec.Text as AT
|
|
import qualified Data.Text as T
|
|
|
|
|
|
type CacheControl = [CacheControlDirective]
|
|
|
|
data CacheControlDirective
|
|
= CCDOnlyToken !Text
|
|
| CCDTokenWithVal !Text !Text
|
|
deriving (Show, Eq)
|
|
|
|
-- | Tries to parse the @max-age@ or @s-maxage@ present in the value of @Cache-Control@ header
|
|
parseMaxAge :: Integral a => Text -> Either String a
|
|
parseMaxAge t = do
|
|
cc <- parseCacheControl t
|
|
case find checkMaxAgeToken cc of
|
|
Nothing -> Left parseErr
|
|
Just d -> case d of
|
|
CCDOnlyToken _ -> Left parseErr
|
|
CCDTokenWithVal _ val -> fmapL (const parseErr) $ AT.parseOnly AT.decimal val
|
|
where
|
|
parseErr = "could not find max-age/s-maxage"
|
|
checkMaxAgeToken = \case
|
|
CCDOnlyToken token -> token == "max-age" || token == "s-maxage"
|
|
CCDTokenWithVal token _ -> token == "max-age" || token == "s-maxage"
|
|
|
|
|
|
-- | Parses a @Cache-Control@ header and returns a list of directives
|
|
parseCacheControl :: Text -> Either String CacheControl
|
|
parseCacheControl = AT.parseOnly cacheControlParser
|
|
|
|
-- ABNF: cache-control = *( "," OWS) cache-directive *( OWS "," [OWS cache-directive])
|
|
-- https://tools.ietf.org/html/rfc7234#appendix-C
|
|
cacheControlParser :: AT.Parser CacheControl
|
|
cacheControlParser = do
|
|
void $ AT.many' ("," *> optionalWhitespaceParser)
|
|
cd <- cacheDirectiveParser
|
|
cds <- AT.many' $ optionalWhitespaceParser *> "," *> AT.option Nothing (pure <$> optionalDirective)
|
|
return $ cd : catMaybes cds
|
|
where
|
|
optionalDirective = optionalWhitespaceParser *> cacheDirectiveParser
|
|
|
|
-- ABNF: OWS = *( SP / HTAB ) ; optional whitespace
|
|
-- https://tools.ietf.org/html/rfc7230#section-3.2.3
|
|
optionalWhitespaceParser :: AT.Parser (Maybe Char)
|
|
optionalWhitespaceParser = AT.option Nothing (pure <$> AT.space)
|
|
|
|
-- ABNF: cache-directive = token [ "=" ( token / quoted-string ) ]
|
|
-- https://tools.ietf.org/html/rfc7230#section-3.2.6
|
|
cacheDirectiveParser :: AT.Parser CacheControlDirective
|
|
cacheDirectiveParser = tokenWithValue <|> onlyToken
|
|
where
|
|
onlyToken = CCDOnlyToken <$> tokenParser
|
|
tokenWithValue = do
|
|
tok <- tokenParser
|
|
void $ AT.char '='
|
|
val <- tokenParser <|> quotedStringParser
|
|
return $ CCDTokenWithVal tok val
|
|
|
|
-- ABNF: 1*tchar
|
|
-- https://tools.ietf.org/html/rfc7230#section-3.2.6
|
|
tokenParser :: AT.Parser Text
|
|
tokenParser = T.pack <$> AT.many1 tcharParser
|
|
|
|
-- ABNF: tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / "^" / "_" / "`" / "|"
|
|
-- / "~" / DIGIT / ALPHA ; any VCHAR, except delimiters
|
|
-- https://tools.ietf.org/html/rfc7230#section-3.2.6
|
|
tcharParser :: AT.Parser Char
|
|
tcharParser = AT.char '!'
|
|
<|> AT.char '#'
|
|
<|> AT.char '$'
|
|
<|> AT.char '%'
|
|
<|> AT.char '&'
|
|
<|> AT.char '\''
|
|
<|> AT.char '*'
|
|
<|> AT.char '+'
|
|
<|> AT.char '-'
|
|
<|> AT.char '.'
|
|
<|> AT.char '^'
|
|
<|> AT.char '_'
|
|
<|> AT.char '`'
|
|
<|> AT.char '|'
|
|
<|> AT.char '~'
|
|
<|> AT.digit
|
|
<|> AT.letter
|
|
|
|
-- ABNF: DQUOTE = %x22 ; " (Double Quote)
|
|
-- https://tools.ietf.org/html/rfc5234#appendix-B.1
|
|
dquoteParser :: AT.Parser Char
|
|
dquoteParser = AT.char '"'
|
|
|
|
-- ABNF: VCHAR = %x21-7E ; visible (printing) characters
|
|
-- https://tools.ietf.org/html/rfc5234#appendix-B.1
|
|
vcharParser :: AT.Parser Char
|
|
vcharParser = AT.anyChar
|
|
|
|
-- ABNF: quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
|
|
-- https://tools.ietf.org/html/rfc7230#section-3.2.6
|
|
quotedStringParser :: AT.Parser Text
|
|
quotedStringParser =
|
|
dquoteParser *> fmap T.pack (AT.many' (qdTextParser <|> quotedPairParser)) <* dquoteParser
|
|
|
|
-- ABNF: quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
|
-- https://tools.ietf.org/html/rfc7230#section-3.2.6
|
|
quotedPairParser :: AT.Parser Char
|
|
quotedPairParser =
|
|
AT.string "\\" *> (AT.space <|> vcharParser)
|
|
|
|
-- ABNF: qdtext = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text
|
|
-- https://tools.ietf.org/html/rfc7230#section-3.2.6
|
|
qdTextParser :: AT.Parser Char
|
|
qdTextParser = AT.space
|
|
<|> AT.char '!' -- %x21
|
|
-- skip %x22 as it is '"'
|
|
<|> AT.char '#' -- %x23
|
|
<|> AT.char '$' -- %x24
|
|
<|> AT.char '%' -- %x25
|
|
<|> AT.char '&' -- %x26
|
|
<|> AT.char '\'' -- %x27 single quote
|
|
<|> AT.char '(' -- %x28
|
|
<|> AT.char ')' -- %x29
|
|
<|> AT.char '*' -- %x2A
|
|
<|> AT.char '+' -- %x2B
|
|
<|> AT.char ',' -- %x2C
|
|
<|> AT.char '-' -- %x2D
|
|
<|> AT.char '.' -- %x2E
|
|
<|> AT.char '/' -- %x2F
|
|
<|> AT.digit -- %x30-39
|
|
<|> AT.char ':' -- %x3A
|
|
<|> AT.char ';' -- %x3B
|
|
<|> AT.char '<' -- %x3C
|
|
<|> AT.char '=' -- %x3D
|
|
<|> AT.char '>' -- %x3E
|
|
<|> AT.char '?' -- %x3F
|
|
<|> AT.char '@' -- %x40
|
|
<|> AT.letter -- %x41-5A / %x61-7A
|
|
<|> AT.char '[' -- %x5B
|
|
-- skip %x5C as it is '\'
|
|
<|> AT.char ']' -- %x5D
|
|
<|> AT.char '^' -- %x5E
|
|
<|> AT.char '_' -- %x5F
|
|
<|> AT.char '`' -- %x60
|
|
<|> AT.char '{' -- %x7B
|
|
<|> AT.char '|' -- %x7C
|
|
<|> AT.char '}' -- %x7D
|
|
<|> AT.char '~' -- %x7E
|