{-# LANGUAGE OverloadedStrings #-} module Network.HPACK.Token ( -- * Data type Token (..), tokenCIKey, tokenFoldedKey, toToken, -- * Ix minTokenIx, maxStaticTokenIx, maxTokenIx, cookieTokenIx, -- * Utilities isMaxTokenIx, isCookieTokenIx, isStaticTokenIx, isStaticToken, -- * Defined tokens tokenAuthority, tokenMethod, tokenPath, tokenScheme, tokenStatus, tokenAcceptCharset, tokenAcceptEncoding, tokenAcceptLanguage, tokenAcceptRanges, tokenAccept, tokenAccessControlAllowOrigin, tokenAge, tokenAllow, tokenAuthorization, tokenCacheControl, tokenContentDisposition, tokenContentEncoding, tokenContentLanguage, tokenContentLength, tokenContentLocation, tokenContentRange, tokenContentType, tokenCookie, tokenDate, tokenEtag, tokenExpect, tokenExpires, tokenFrom, tokenHost, tokenIfMatch, tokenIfModifiedSince, tokenIfNoneMatch, tokenIfRange, tokenIfUnmodifiedSince, tokenLastModified, tokenLink, tokenLocation, tokenMaxForwards, tokenProxyAuthenticate, tokenProxyAuthorization, tokenRange, tokenReferer, tokenRefresh, tokenRetryAfter, tokenServer, tokenSetCookie, tokenStrictTransportSecurity, tokenTransferEncoding, tokenUserAgent, tokenVary, tokenVia, tokenWwwAuthenticate, tokenConnection, tokenTE, tokenMax, tokenAccessControlAllowCredentials, tokenAccessControlAllowHeaders, tokenAccessControlAllowMethods, tokenAccessControlExposeHeaders, tokenAccessControlRequestHeaders, tokenAccessControlRequestMethod, tokenAltSvc, tokenContentSecurityPolicy, tokenEarlyData, tokenExpectCt, tokenForwarded, tokenOrigin, tokenPurpose, tokenTimingAllowOrigin, tokenUpgradeInsecureRequests, tokenXContentTypeOptions, tokenXForwardedFor, tokenXFrameOptions, tokenXXssProtection, ) where import qualified Data.ByteString as B import Data.ByteString.Internal (ByteString (..), memcmp) import Data.CaseInsensitive (CI (..), mk, original) import Foreign.ForeignPtr (withForeignPtr) import Foreign.Ptr (plusPtr) import System.IO.Unsafe (unsafeDupablePerformIO) -- $setup -- >>> :set -XOverloadedStrings -- | Internal representation for header keys. data Token = Token { tokenIx :: Int -- ^ Index for value table , shouldBeIndexed :: Bool -- ^ should be indexed in HPACK , isPseudo :: Bool -- ^ is this a pseudo header key? , tokenKey :: CI ByteString -- ^ Case insensitive header key } deriving (Eq, Show) -- | Extracting a case insensitive header key from a token. {-# INLINE tokenCIKey #-} tokenCIKey :: Token -> ByteString tokenCIKey (Token _ _ _ ci) = original ci -- | Extracting a folded header key from a token. {-# INLINE tokenFoldedKey #-} tokenFoldedKey :: Token -> ByteString tokenFoldedKey (Token _ _ _ ci) = foldedCase ci {- FOURMOLU_DISABLE -} tokenAuthority :: Token tokenMethod :: Token tokenPath :: Token tokenScheme :: Token tokenStatus :: Token tokenAcceptCharset :: Token tokenAcceptEncoding :: Token tokenAcceptLanguage :: Token tokenAcceptRanges :: Token tokenAccept :: Token tokenAccessControlAllowOrigin :: Token tokenAge :: Token tokenAllow :: Token tokenAuthorization :: Token tokenCacheControl :: Token tokenContentDisposition :: Token tokenContentEncoding :: Token tokenContentLanguage :: Token tokenContentLength :: Token tokenContentLocation :: Token tokenContentRange :: Token tokenContentType :: Token tokenCookie :: Token tokenDate :: Token tokenEtag :: Token tokenExpect :: Token tokenExpires :: Token tokenFrom :: Token tokenHost :: Token tokenIfMatch :: Token tokenIfModifiedSince :: Token tokenIfNoneMatch :: Token tokenIfRange :: Token tokenIfUnmodifiedSince :: Token tokenLastModified :: Token tokenLink :: Token tokenLocation :: Token tokenMaxForwards :: Token tokenProxyAuthenticate :: Token tokenProxyAuthorization :: Token tokenRange :: Token tokenReferer :: Token tokenRefresh :: Token tokenRetryAfter :: Token tokenServer :: Token tokenSetCookie :: Token tokenStrictTransportSecurity :: Token tokenTransferEncoding :: Token tokenUserAgent :: Token tokenVary :: Token tokenVia :: Token tokenWwwAuthenticate :: Token tokenConnection :: Token -- Warp tokenTE :: Token -- Warp tokenAccessControlAllowCredentials :: Token -- QPACK tokenAccessControlAllowHeaders :: Token -- QPACK tokenAccessControlAllowMethods :: Token -- QPACK tokenAccessControlExposeHeaders :: Token -- QPACK tokenAccessControlRequestHeaders :: Token -- QPACK tokenAccessControlRequestMethod :: Token -- QPACK tokenAltSvc :: Token -- QPACK tokenContentSecurityPolicy :: Token -- QPACK tokenEarlyData :: Token -- QPACK tokenExpectCt :: Token -- QPACK tokenForwarded :: Token -- QPACK tokenOrigin :: Token -- QPACK tokenPurpose :: Token -- QPACK tokenTimingAllowOrigin :: Token -- QPACK tokenUpgradeInsecureRequests :: Token -- QPACK tokenXContentTypeOptions :: Token -- QPACK tokenXForwardedFor :: Token -- QPACK tokenXFrameOptions :: Token -- QPACK tokenXXssProtection :: Token -- QPACK tokenMax :: Token -- Other tokens tokenAuthority = Token 0 True True ":authority" tokenMethod = Token 1 True True ":method" tokenPath = Token 2 False True ":path" tokenScheme = Token 3 True True ":scheme" tokenStatus = Token 4 True True ":status" tokenAcceptCharset = Token 5 True False "Accept-Charset" tokenAcceptEncoding = Token 6 True False "Accept-Encoding" tokenAcceptLanguage = Token 7 True False "Accept-Language" tokenAcceptRanges = Token 8 True False "Accept-Ranges" tokenAccept = Token 9 True False "Accept" tokenAccessControlAllowOrigin = Token 10 True False "Access-Control-Allow-Origin" tokenAge = Token 11 True False "Age" tokenAllow = Token 12 True False "Allow" tokenAuthorization = Token 13 True False "Authorization" tokenCacheControl = Token 14 True False "Cache-Control" tokenContentDisposition = Token 15 True False "Content-Disposition" tokenContentEncoding = Token 16 True False "Content-Encoding" tokenContentLanguage = Token 17 True False "Content-Language" tokenContentLength = Token 18 False False "Content-Length" tokenContentLocation = Token 19 False False "Content-Location" tokenContentRange = Token 20 True False "Content-Range" tokenContentType = Token 21 True False "Content-Type" tokenCookie = Token 22 True False "Cookie" tokenDate = Token 23 True False "Date" tokenEtag = Token 24 False False "Etag" tokenExpect = Token 25 True False "Expect" tokenExpires = Token 26 True False "Expires" tokenFrom = Token 27 True False "From" tokenHost = Token 28 True False "Host" tokenIfMatch = Token 29 True False "If-Match" tokenIfModifiedSince = Token 30 True False "If-Modified-Since" tokenIfNoneMatch = Token 31 True False "If-None-Match" tokenIfRange = Token 32 True False "If-Range" tokenIfUnmodifiedSince = Token 33 True False "If-Unmodified-Since" tokenLastModified = Token 34 True False "Last-Modified" tokenLink = Token 35 True False "Link" tokenLocation = Token 36 True False "Location" tokenMaxForwards = Token 37 True False "Max-Forwards" tokenProxyAuthenticate = Token 38 True False "Proxy-Authenticate" tokenProxyAuthorization = Token 39 True False "Proxy-Authorization" tokenRange = Token 40 True False "Range" tokenReferer = Token 41 True False "Referer" tokenRefresh = Token 42 True False "Refresh" tokenRetryAfter = Token 43 True False "Retry-After" tokenServer = Token 44 True False "Server" tokenSetCookie = Token 45 False False "Set-Cookie" tokenStrictTransportSecurity = Token 46 True False "Strict-Transport-Security" tokenTransferEncoding = Token 47 True False "Transfer-Encoding" tokenUserAgent = Token 48 True False "User-Agent" tokenVary = Token 49 True False "Vary" tokenVia = Token 50 True False "Via" tokenWwwAuthenticate = Token 51 True False "Www-Authenticate" -- | A place holder to hold header keys not defined in the static table. -- | For Warp tokenConnection = Token 52 False False "Connection" tokenTE = Token 53 False False "TE" -- | For QPACK tokenAccessControlAllowCredentials = Token 54 True False "Access-Control-Allow-Credentials" tokenAccessControlAllowHeaders = Token 55 True False "Access-Control-Allow-Headers" tokenAccessControlAllowMethods = Token 56 True False "Access-Control-Allow-Methods" tokenAccessControlExposeHeaders = Token 57 True False "Access-Control-Expose-Headers" tokenAccessControlRequestHeaders = Token 58 True False "Access-Control-Request-Headers" tokenAccessControlRequestMethod = Token 59 True False "Access-Control-Request-Method" tokenAltSvc = Token 60 True False "Alt-Svc" tokenContentSecurityPolicy = Token 61 True False "Content-Security-Policy" tokenEarlyData = Token 62 True False "Early-Data" tokenExpectCt = Token 63 True False "Expect-Ct" tokenForwarded = Token 64 True False "Forwarded" tokenOrigin = Token 65 True False "Origin" tokenPurpose = Token 66 True False "Purpose" tokenTimingAllowOrigin = Token 67 True False "Timing-Allow-Origin" tokenUpgradeInsecureRequests = Token 68 True False "Upgrade-Insecure-Requests" tokenXContentTypeOptions = Token 69 True False "X-Content-Type-Options" tokenXForwardedFor = Token 70 True False "X-Forwarded-For" tokenXFrameOptions = Token 71 True False "X-Frame-Options" tokenXXssProtection = Token 72 True False "X-Xss-Protection" tokenMax = Token 73 True False "for other tokens" {- FOURMOLU_ENABLE -} -- | Minimum token index. minTokenIx :: Int minTokenIx = 0 -- | Maximun token index defined in the static table. maxStaticTokenIx :: Int maxStaticTokenIx = 51 -- | Maximum token index. maxTokenIx :: Int maxTokenIx = 73 -- | Token index for 'tokenCookie'. cookieTokenIx :: Int cookieTokenIx = 22 -- | Is this token ix for Cookie? {-# INLINE isCookieTokenIx #-} isCookieTokenIx :: Int -> Bool isCookieTokenIx n = n == cookieTokenIx -- | Is this token ix to be held in the place holder? {-# INLINE isMaxTokenIx #-} isMaxTokenIx :: Int -> Bool isMaxTokenIx n = n == maxTokenIx -- | Is this token ix for a header not defined in the static table? {-# INLINE isStaticTokenIx #-} isStaticTokenIx :: Int -> Bool isStaticTokenIx n = n <= maxStaticTokenIx -- | Is this token for a header not defined in the static table? {-# INLINE isStaticToken #-} isStaticToken :: Token -> Bool isStaticToken n = tokenIx n <= maxStaticTokenIx -- | Making a token from a header key. -- -- >>> toToken ":authority" == tokenAuthority -- True -- >>> toToken "foo" -- Token {tokenIx = 73, shouldBeIndexed = True, isPseudo = False, tokenKey = "foo"} -- >>> toToken ":bar" -- Token {tokenIx = 73, shouldBeIndexed = True, isPseudo = True, tokenKey = ":bar"} toToken :: ByteString -> Token toToken "" = Token maxTokenIx True False "" toToken bs = case len of 2 -> if bs === "te" then tokenTE else mkTokenMax bs 3 -> case lst of 97 | bs === "via" -> tokenVia 101 | bs === "age" -> tokenAge _ -> mkTokenMax bs 4 -> case lst of 101 | bs === "date" -> tokenDate 103 | bs === "etag" -> tokenEtag 107 | bs === "link" -> tokenLink 109 | bs === "from" -> tokenFrom 116 | bs === "host" -> tokenHost 121 | bs === "vary" -> tokenVary _ -> mkTokenMax bs 5 -> case lst of 101 | bs === "range" -> tokenRange 104 | bs === ":path" -> tokenPath 119 | bs === "allow" -> tokenAllow _ -> mkTokenMax bs 6 -> case lst of 101 | bs === "cookie" -> tokenCookie 110 | bs === "origin" -> tokenOrigin 114 | bs === "server" -> tokenServer 116 | bs === "expect" -> tokenExpect | bs === "accept" -> tokenAccept _ -> mkTokenMax bs 7 -> case lst of 99 | bs === "alt-svc" -> tokenAltSvc 100 | bs === ":method" -> tokenMethod 101 | bs === ":scheme" -> tokenScheme | bs === "purpose" -> tokenPurpose 104 | bs === "refresh" -> tokenRefresh 114 | bs === "referer" -> tokenReferer 115 | bs === "expires" -> tokenExpires | bs === ":status" -> tokenStatus _ -> mkTokenMax bs 8 -> case lst of 101 | bs === "if-range" -> tokenIfRange 104 | bs === "if-match" -> tokenIfMatch 110 | bs === "location" -> tokenLocation _ -> mkTokenMax bs 9 -> case lst of 100 | bs === "forwarded" -> tokenForwarded 116 | bs === "expect-ct" -> tokenExpectCt _ -> mkTokenMax bs 10 -> case lst of 97 | bs === "early-data" -> tokenEarlyData 101 | bs === "set-cookie" -> tokenSetCookie 110 | bs === "connection" -> tokenConnection 116 | bs === "user-agent" -> tokenUserAgent 121 | bs === ":authority" -> tokenAuthority _ -> mkTokenMax bs 11 -> case lst of 114 | bs === "retry-after" -> tokenRetryAfter _ -> mkTokenMax bs 12 -> case lst of 101 | bs === "content-type" -> tokenContentType 115 | bs === "max-forwards" -> tokenMaxForwards _ -> mkTokenMax bs 13 -> case lst of 100 | bs === "last-modified" -> tokenLastModified 101 | bs === "content-range" -> tokenContentRange 104 | bs === "if-none-match" -> tokenIfNoneMatch 108 | bs === "cache-control" -> tokenCacheControl 110 | bs === "authorization" -> tokenAuthorization 115 | bs === "accept-ranges" -> tokenAcceptRanges _ -> mkTokenMax bs 14 -> case lst of 104 | bs === "content-length" -> tokenContentLength 116 | bs === "accept-charset" -> tokenAcceptCharset _ -> mkTokenMax bs 15 -> case lst of 101 | bs === "accept-language" -> tokenAcceptLanguage 103 | bs === "accept-encoding" -> tokenAcceptEncoding 114 | bs === "x-forwarded-for" -> tokenXForwardedFor 115 | bs === "x-frame-options" -> tokenXFrameOptions _ -> mkTokenMax bs 16 -> case lst of 101 | bs === "content-language" -> tokenContentLanguage | bs === "www-authenticate" -> tokenWwwAuthenticate 103 | bs === "content-encoding" -> tokenContentEncoding 110 | bs === "content-location" -> tokenContentLocation | bs === "x-xss-protection" -> tokenXXssProtection _ -> mkTokenMax bs 17 -> case lst of 101 | bs === "if-modified-since" -> tokenIfModifiedSince 103 | bs === "transfer-encoding" -> tokenTransferEncoding _ -> mkTokenMax bs 18 -> case lst of 101 | bs === "proxy-authenticate" -> tokenProxyAuthenticate _ -> mkTokenMax bs 19 -> case lst of 101 | bs === "if-unmodified-since" -> tokenIfUnmodifiedSince 110 | bs === "proxy-authorization" -> tokenProxyAuthorization | bs === "content-disposition" -> tokenContentDisposition | bs === "timing-allow-origin" -> tokenTimingAllowOrigin _ -> mkTokenMax bs 22 -> case lst of 115 | bs === "x-content-type-options" -> tokenXContentTypeOptions _ -> mkTokenMax bs 23 -> case lst of 121 | bs === "content-security-policy" -> tokenContentSecurityPolicy _ -> mkTokenMax bs 25 -> case lst of 115 | bs === "upgrade-insecure-requests" -> tokenUpgradeInsecureRequests 121 | bs === "strict-transport-security" -> tokenStrictTransportSecurity _ -> mkTokenMax bs 27 -> case lst of 110 | bs === "access-control-allow-origin" -> tokenAccessControlAllowOrigin _ -> mkTokenMax bs 28 -> case lst of 115 | bs === "access-control-allow-headers" -> tokenAccessControlAllowHeaders | bs === "access-control-allow-methods" -> tokenAccessControlAllowMethods _ -> mkTokenMax bs 29 -> case lst of 100 | bs === "access-control-request-method" -> tokenAccessControlRequestMethod 115 | bs === "access-control-expose-headers" -> tokenAccessControlExposeHeaders _ -> mkTokenMax bs 30 -> case lst of 115 | bs === "access-control-request-headers" -> tokenAccessControlRequestHeaders _ -> mkTokenMax bs 32 -> case lst of 115 | bs === "access-control-allow-credentials" -> tokenAccessControlAllowCredentials _ -> mkTokenMax bs _ -> mkTokenMax bs where len = B.length bs lst = B.last bs PS fp1 off1 siz === PS fp2 off2 _ = unsafeDupablePerformIO $ withForeignPtr fp1 $ \p1 -> withForeignPtr fp2 $ \p2 -> do i <- memcmp (p1 `plusPtr` off1) (p2 `plusPtr` off2) siz return $ i == 0 mkTokenMax :: ByteString -> Token mkTokenMax bs = Token maxTokenIx True p (mk bs) where p | B.length bs == 0 = False | B.head bs == 58 = True | otherwise = False