di-wai-0.2: lib/Di/Wai.hs
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -Wno-orphans #-}
-- | This module is designed to be imported as follows:
--
-- @
-- import qualified "Di.Wai"
-- @
module Di.Wai (middleware, middleware') where
import Data.Foldable
import Data.IORef
import Data.Word
import Df1.Wai qualified
import Di.Df1 qualified as Di
import Network.Wai qualified as Wai
import System.Clock qualified as Clock
import System.IO.Unsafe
refReqId :: IORef Word64
refReqId = unsafePerformIO $ newIORef 0
{-# NOINLINE refReqId #-}
-- | Like 'middleware', but it exposes the 'Di.Df1' that includes 'Df1.Path'
-- data about 'Wai.Request' to the underlying 'Wai.Application'.
middleware'
:: Di.Df1
-> (Di.Df1 -> Wai.Application)
-> Wai.Application
middleware' di0 = \fapp req respond -> do
t0 <- Clock.getTime Clock.Monotonic
reqId <- atomicModifyIORef' refReqId $ \ol -> (ol + 1, ol)
let di1 =
foldl'
(\di (k, v) -> Di.attr k v di)
(Di.push "http" di0)
(("request", Di.value reqId) : Df1.Wai.request req)
Di.info_ di1 "Request coming in"
fapp di1 req $ \res -> do
t1 <- Clock.getTime Clock.Monotonic
let td = Clock.toNanoSecs t1 - Clock.toNanoSecs t0
di2 =
foldl'
(\di (k, v) -> Di.attr k v di)
di1
(("nanoseconds", Di.value td) : Df1.Wai.response res)
Di.info_ di2 "Response going out"
respond res
-- | Obtain a 'Wai.Middleware' that will log incomming 'Wai.Request's
-- and outgoing 'Wai.Response's through the given 'Di.Df1'.
--
-- If your 'Wai.Application' needs access to the 'Di.Df1' uniquely tied
-- to the current 'Wai.Request', then use 'middleware'' instead.
middleware :: Di.Df1 -> Wai.Middleware
middleware di = middleware' di . const