warp-3.4.13.1: Network/Wai/Handler/Warp/HTTP2/Request.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
module Network.Wai.Handler.Warp.HTTP2.Request (
toRequest,
getHTTP2Data,
setHTTP2Data,
modifyHTTP2Data,
) where
import Control.Arrow (first)
import qualified Data.ByteString.Char8 as C8
import Data.IORef
import qualified Data.Vault.Lazy as Vault
import Network.HPACK
import Network.HPACK.Token
import qualified Network.HTTP.Types as H
import Network.Socket (SockAddr)
import Network.Wai
import System.IO.Unsafe (unsafePerformIO)
import qualified System.TimeManager as T
import Network.Wai.Handler.Warp.HTTP2.Types
import Network.Wai.Handler.Warp.Imports
import Network.Wai.Handler.Warp.Request (getFileInfoKey, pauseTimeoutKey)
#ifdef MIN_VERSION_crypton_x509
import Network.Wai.Handler.Warp.Request (getClientCertificateKey)
#endif
import qualified Network.Wai.Handler.Warp.Settings as S (
Settings,
settingsNoParsePath,
)
import Network.Wai.Handler.Warp.Types
type ToReq =
(TokenHeaderList, ValueTable)
-> Maybe Int
-> IO ByteString
-> T.Handle
-> Transport
-> IO Request
----------------------------------------------------------------
http30 :: H.HttpVersion
http30 = H.HttpVersion 3 0
toRequest :: InternalInfo -> S.Settings -> SockAddr -> ToReq
toRequest ii settings addr ht bodylen body th transport = do
ref <- newIORef Nothing
toRequest' ii settings addr ref ht bodylen body th transport
toRequest'
:: InternalInfo
-> S.Settings
-> SockAddr
-> IORef (Maybe HTTP2Data)
-> ToReq
toRequest' ii settings addr ref (reqths, reqvt) bodylen body th transport =
-- setting 'requestBody' with 'setRequestBodyChunks' to avoid warnings
return $! setRequestBodyChunks body req
where
!req =
defaultRequest
{ requestMethod = colonMethod
, httpVersion = if isTransportQUIC transport then http30 else H.http20
, rawPathInfo = rawPath
, pathInfo = H.decodePathSegments path
, rawQueryString = query
, queryString = H.parseQuery query
, requestHeaders = headers
, isSecure = isTransportSecure transport
, remoteHost = addr
, vault = vaultValue
, requestBodyLength = maybe ChunkedBody (KnownLength . fromIntegral) bodylen
, requestHeaderHost = mHost <|> mAuth
, requestHeaderRange = mRange
, requestHeaderReferer = mReferer
, requestHeaderUserAgent = mUserAgent
}
headers = map (first tokenKey) ths
where
ths = case mHost of
Just _ -> reqths
Nothing -> case mAuth of
Just auth -> (tokenHost, auth) : reqths
_ -> reqths
!mPath = getHeaderValue tokenPath reqvt -- SHOULD
!colonMethod = fromJust $ getHeaderValue tokenMethod reqvt -- MUST
!mAuth = getHeaderValue tokenAuthority reqvt -- SHOULD
!mHost = getHeaderValue tokenHost reqvt
!mRange = getHeaderValue tokenRange reqvt
!mReferer = getHeaderValue tokenReferer reqvt
!mUserAgent = getHeaderValue tokenUserAgent reqvt
-- CONNECT request will have ":path" omitted, use ":authority" as unparsed
-- path instead so that it will have consistent behavior compare to HTTP 1.0
(unparsedPath, query) = C8.break (== '?') $ fromJust (mPath <|> mAuth)
!path = H.extractPath unparsedPath
!rawPath = if S.settingsNoParsePath settings then unparsedPath else path
-- fixme: pauseTimeout. th is not available here.
!vaultValue =
Vault.insert getFileInfoKey (getFileInfo ii)
. Vault.insert getHTTP2DataKey (readIORef ref)
. Vault.insert setHTTP2DataKey (writeIORef ref)
. Vault.insert modifyHTTP2DataKey (modifyIORef' ref)
. Vault.insert pauseTimeoutKey (T.pause th)
#ifdef MIN_VERSION_crypton_x509
. Vault.insert getClientCertificateKey (getTransportClientCertificate transport)
#endif
$ Vault.empty
getHTTP2DataKey :: Vault.Key (IO (Maybe HTTP2Data))
getHTTP2DataKey = unsafePerformIO Vault.newKey
{-# NOINLINE getHTTP2DataKey #-}
-- | Getting 'HTTP2Data' through vault of the request.
-- Warp uses this to receive 'HTTP2Data' from 'Middleware'.
--
-- Since: 3.2.7
getHTTP2Data :: Request -> IO (Maybe HTTP2Data)
getHTTP2Data req = case Vault.lookup getHTTP2DataKey (vault req) of
Nothing -> return Nothing
Just getter -> getter
setHTTP2DataKey :: Vault.Key (Maybe HTTP2Data -> IO ())
setHTTP2DataKey = unsafePerformIO Vault.newKey
{-# NOINLINE setHTTP2DataKey #-}
-- | Setting 'HTTP2Data' through vault of the request.
-- 'Application' or 'Middleware' should use this.
--
-- Since: 3.2.7
setHTTP2Data :: Request -> Maybe HTTP2Data -> IO ()
setHTTP2Data req mh2d = case Vault.lookup setHTTP2DataKey (vault req) of
Nothing -> return ()
Just setter -> setter mh2d
modifyHTTP2DataKey :: Vault.Key ((Maybe HTTP2Data -> Maybe HTTP2Data) -> IO ())
modifyHTTP2DataKey = unsafePerformIO Vault.newKey
{-# NOINLINE modifyHTTP2DataKey #-}
-- | Modifying 'HTTP2Data' through vault of the request.
-- 'Application' or 'Middleware' should use this.
--
-- Since: 3.2.8
modifyHTTP2Data :: Request -> (Maybe HTTP2Data -> Maybe HTTP2Data) -> IO ()
modifyHTTP2Data req func = case Vault.lookup modifyHTTP2DataKey (vault req) of
Nothing -> return ()
Just modify -> modify func