module Ecluse.Runtime.Server.Middleware (
goingAwayMiddleware,
timeoutSeconds,
probeApplication,
jsonResponse,
) where
import Network.HTTP.Types (Status, hConnection, hContentType, status200, status404, status503)
import Network.Wai (Application, Middleware, Response, mapResponseHeaders, modifyResponse, pathInfo, responseLBS)
import Ecluse.Runtime.Server.Drain (DrainSignal, isDraining)
goingAwayMiddleware :: DrainSignal -> Middleware
goingAwayMiddleware :: DrainSignal -> Middleware
goingAwayMiddleware DrainSignal
drain Application
app Request
request Response -> IO ResponseReceived
respond = do
draining <- DrainSignal -> IO Bool
isDraining DrainSignal
drain
if draining
then modifyResponse closeConnection app request respond
else app request respond
where
closeConnection :: Response -> Response
closeConnection :: Response -> Response
closeConnection = (ResponseHeaders -> ResponseHeaders) -> Response -> Response
mapResponseHeaders ((HeaderName
hConnection, ByteString
"close") Header -> ResponseHeaders -> ResponseHeaders
forall a. a -> [a] -> [a]
:)
timeoutSeconds :: Int
timeoutSeconds :: Int
timeoutSeconds = Int
60
probeApplication :: DrainSignal -> IO Bool -> IO Bool -> Application
probeApplication :: DrainSignal -> IO Bool -> IO Bool -> Application
probeApplication DrainSignal
drain IO Bool
checkReady IO Bool
checkLiveness Request
request Response -> IO ResponseReceived
respond =
case Request -> [Text]
pathInfo Request
request of
[Text
"livez"] -> do
alive <- IO Bool
checkLiveness
if alive
then respond (jsonResponse status200 "{\"status\":\"live\"}")
else respond (jsonResponse status503 "{\"status\":\"liveness check failed\"}")
[Text
"readyz"] -> DrainSignal -> IO Bool -> IO Response
readiness DrainSignal
drain IO Bool
checkReady IO Response
-> (Response -> IO ResponseReceived) -> IO ResponseReceived
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Response -> IO ResponseReceived
respond
[Text]
_ -> Response -> IO ResponseReceived
respond Response
notFound
readiness :: DrainSignal -> IO Bool -> IO Response
readiness :: DrainSignal -> IO Bool -> IO Response
readiness DrainSignal
drain IO Bool
checkReady =
DrainSignal -> IO Bool
isDraining DrainSignal
drain IO Bool -> (Bool -> IO Response) -> IO Response
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Bool
True -> Response -> IO Response
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Status -> ByteString -> Response
jsonResponse Status
status503 ByteString
"{\"status\":\"draining\"}")
Bool
False ->
IO Bool
checkReady IO Bool -> (Bool -> Response) -> IO Response
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \case
Bool
False -> Status -> ByteString -> Response
jsonResponse Status
status503 ByteString
"{\"status\":\"awaiting startup readiness\"}"
Bool
True -> Status -> ByteString -> Response
jsonResponse Status
status200 ByteString
"{\"status\":\"ready\"}"
notFound :: Response
notFound :: Response
notFound =
Status -> ResponseHeaders -> ByteString -> Response
responseLBS Status
status404 [(HeaderName
hContentType, ByteString
"text/plain; charset=utf-8")] ByteString
"Not Found\n"
jsonResponse :: Status -> LByteString -> Response
jsonResponse :: Status -> ByteString -> Response
jsonResponse Status
status =
Status -> ResponseHeaders -> ByteString -> Response
responseLBS Status
status [(HeaderName
hContentType, ByteString
"application/json")]