warpZ-3.4.10: Network/Wai/Handler/Warp/HTTP2/Response.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Network.Wai.Handler.Warp.HTTP2.Response (
fromResponse,
) where
import qualified Control.Exception as E
import qualified Data.ByteString.Builder as BB
import qualified Data.List as L (find)
import qualified Network.HTTP.Types as H
import qualified Network.HTTP2.Server as H2
import Network.Wai hiding (responseBuilder, responseFile, responseStream)
import Network.Wai.Internal (Response (..))
import Network.Wai.Handler.Warp.File
import Network.Wai.Handler.Warp.HTTP2.Request (getHTTP2Data)
import Network.Wai.Handler.Warp.HTTP2.Types
import Network.Wai.Handler.Warp.Header
import qualified Network.Wai.Handler.Warp.Response as R
import qualified Network.Wai.Handler.Warp.Settings as S
import Network.Wai.Handler.Warp.Types
----------------------------------------------------------------
fromResponse
:: S.Settings
-> InternalInfo
-> Request
-> Response
-> IO (H2.Response, H.Status, Bool)
fromResponse settings ii req rsp = do
date <- getDate ii
rspst@(h2rsp, st, hasBody) <- case rsp of
ResponseFile st rsphdr path mpart -> do
let rsphdr' = add date rsphdr
responseFile st rsphdr' method path mpart ii reqhdr
ResponseBuilder st rsphdr builder -> do
let rsphdr' = add date rsphdr
return $ responseBuilder st rsphdr' method builder
ResponseStream st rsphdr strmbdy -> do
let rsphdr' = add date rsphdr
return $ responseStream st rsphdr' method strmbdy
_ -> error "ResponseRaw is not supported in HTTP/2"
mh2data <- getHTTP2Data req
case mh2data of
Nothing -> return rspst
Just h2data -> do
let !trailers = http2dataTrailers h2data
!h2rsp' = H2.setResponseTrailersMaker h2rsp trailers
return (h2rsp', st, hasBody)
where
!method = requestMethod req
!reqhdr = requestHeaders req
!server = S.settingsServerName settings
add date rsphdr =
let hasServerHdr = L.find ((== H.hServer) . fst) rsphdr
addSVR =
maybe ((H.hServer, server) :) (const id) hasServerHdr
in R.addAltSvc settings $
(H.hDate, date) : addSVR rsphdr
----------------------------------------------------------------
responseFile
:: H.Status
-> H.ResponseHeaders
-> H.Method
-> FilePath
-> Maybe FilePart
-> InternalInfo
-> H.RequestHeaders
-> IO (H2.Response, H.Status, Bool)
responseFile st rsphdr _ _ _ _ _
| noBody st = return $ responseNoBody st rsphdr
responseFile st rsphdr method path (Just fp) _ _ =
return $ responseFile2XX st rsphdr method fileSpec
where
!off' = fromIntegral $ filePartOffset fp
!bytes' = fromIntegral $ filePartByteCount fp
!fileSpec = H2.FileSpec path off' bytes'
responseFile _ rsphdr method path Nothing ii reqhdr = do
efinfo <- E.try $ getFileInfo ii path
case efinfo of
Left (_ex :: E.IOException) -> return $ response404 rsphdr
Right finfo -> do
let reqidx = indexRequestHeader reqhdr
rspidx = indexResponseHeader rsphdr
case conditionalRequest finfo rsphdr method rspidx reqidx of
WithoutBody s -> return $ responseNoBody s rsphdr
WithBody s rsphdr' off bytes -> do
let !off' = fromIntegral off
!bytes' = fromIntegral bytes
!fileSpec = H2.FileSpec path off' bytes'
return $ responseFile2XX s rsphdr' method fileSpec
----------------------------------------------------------------
responseFile2XX
:: H.Status
-> H.ResponseHeaders
-> H.Method
-> H2.FileSpec
-> (H2.Response, H.Status, Bool)
responseFile2XX st rsphdr method fileSpec
| method == H.methodHead = responseNoBody st rsphdr
| otherwise = (H2.responseFile st rsphdr fileSpec, st, True)
----------------------------------------------------------------
responseBuilder
:: H.Status
-> H.ResponseHeaders
-> H.Method
-> BB.Builder
-> (H2.Response, H.Status, Bool)
responseBuilder st rsphdr method builder
| method == H.methodHead || noBody st = responseNoBody st rsphdr
| otherwise = (H2.responseBuilder st rsphdr builder, st, True)
----------------------------------------------------------------
responseStream
:: H.Status
-> H.ResponseHeaders
-> H.Method
-> StreamingBody
-> (H2.Response, H.Status, Bool)
responseStream st rsphdr method strmbdy
| method == H.methodHead || noBody st = responseNoBody st rsphdr
| otherwise = (H2.responseStreaming st rsphdr strmbdy, st, True)
----------------------------------------------------------------
responseNoBody :: H.Status -> H.ResponseHeaders -> (H2.Response, H.Status, Bool)
responseNoBody st rsphdr = (H2.responseNoBody st rsphdr, st, False)
----------------------------------------------------------------
response404 :: H.ResponseHeaders -> (H2.Response, H.Status, Bool)
response404 rsphdr = (h2rsp, st, True)
where
h2rsp = H2.responseBuilder st rsphdr' body
st = H.notFound404
!rsphdr' = R.replaceHeader H.hContentType "text/plain; charset=utf-8" rsphdr
!body = BB.byteString "File not found"
----------------------------------------------------------------
noBody :: H.Status -> Bool
noBody = not . R.hasBody