-- | Common utilities. module Network.HTTP.Media.Utils ( breakChar, trimBS, mediaChars, isMediaChar, tokenChars, isTokenChar, isValidToken, ) where import Data.ByteString (ByteString) import qualified Data.ByteString.Char8 as BS import Data.Char (isControl) -- | Equivalent to 'Data.ByteString.break' (on equality against the given -- character), but leaves out the byte that the string is broken on. breakChar :: Char -> ByteString -> Maybe (ByteString, ByteString) breakChar c = safeTail . BS.break (== c) where safeTail (a, b) | BS.null b = Nothing | otherwise = Just (a, BS.tail b) -- | Trims tab and space characters from both ends of a ByteString. trimBS :: ByteString -> ByteString trimBS = fst . BS.spanEnd isLWS . BS.dropWhile isLWS where isLWS c = c == ' ' || c == '\t' -- | List of the valid characters for a media-type `reg-name` as per RFC 4288. mediaChars :: [Char] mediaChars = ['A' .. 'Z'] ++ ['a' .. 'z'] ++ ['0' .. '9'] ++ "!#$&.+-^_" -- | Evaluates whether the given character is valid in a media type `reg-name` -- as per RFC 4288. isMediaChar :: Char -> Bool isMediaChar = (`elem` mediaChars) -- | Evaluates whether the given character is valid in an HTTP header token as -- per RFC 2616. isTokenChar :: Char -> Bool isTokenChar = (||) <$> not . isControl <*> (`notElem` separators) where separators = [ '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ' ] -- | HTTP header token characters as per RFC 2616. tokenChars :: [Char] tokenChars = filter isTokenChar ['\0' .. '\127'] -- | Evaluates whether the given ASCII string is valid as an HTTP header token -- as per RFC 2616. isValidToken :: ByteString -> Bool isValidToken = (&&) <$> not . BS.null <*> BS.all isTokenChar