module Network.GRPC.Server.Handler (
RpcHandler(..)
, hoistRpcHandler
, mkRpcHandler
, mkRpcHandlerNoDefMetadata
, SomeRpcHandler(..)
, someRpcHandler
, hoistSomeRpcHandler
, runHandler
) where
import Prelude hiding (lookup)
import Control.Concurrent.Async
import Control.Monad
import Control.Monad.Catch
import Control.Monad.IO.Class
import Data.Kind
import Data.Proxy
import GHC.Stack
import System.ThreadManager (KilledByThreadManager(..))
import Network.GRPC.Common
import Network.GRPC.Server.Call
import Network.GRPC.Server.Context
import Network.GRPC.Util.GHC
import Network.GRPC.Util.HTTP2.Stream (ClientDisconnected(..))
import Network.GRPC.Util.Session qualified as Session
data RpcHandler (m :: Type -> Type) (rpc :: k) = RpcHandler {
forall k (m :: * -> *) (rpc :: k).
RpcHandler m rpc -> Call rpc -> m ()
runRpcHandler :: Call rpc -> m ()
}
hoistRpcHandler ::
(forall a. m a -> n a)
-> RpcHandler m rpc
-> RpcHandler n rpc
hoistRpcHandler :: forall {k} (m :: * -> *) (n :: * -> *) (rpc :: k).
(forall a. m a -> n a) -> RpcHandler m rpc -> RpcHandler n rpc
hoistRpcHandler forall a. m a -> n a
f (RpcHandler Call rpc -> m ()
h) = (Call rpc -> n ()) -> RpcHandler n rpc
forall k (m :: * -> *) (rpc :: k).
(Call rpc -> m ()) -> RpcHandler m rpc
RpcHandler (m () -> n ()
forall a. m a -> n a
f (m () -> n ()) -> (Call rpc -> m ()) -> Call rpc -> n ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Call rpc -> m ()
h)
mkRpcHandler ::
( Default (ResponseInitialMetadata rpc)
, MonadIO m
)
=> (Call rpc -> m ()) -> RpcHandler m rpc
mkRpcHandler :: forall {k} (rpc :: k) (m :: * -> *).
(Default (ResponseInitialMetadata rpc), MonadIO m) =>
(Call rpc -> m ()) -> RpcHandler m rpc
mkRpcHandler Call rpc -> m ()
k = (Call rpc -> m ()) -> RpcHandler m rpc
forall k (m :: * -> *) (rpc :: k).
(Call rpc -> m ()) -> RpcHandler m rpc
RpcHandler ((Call rpc -> m ()) -> RpcHandler m rpc)
-> (Call rpc -> m ()) -> RpcHandler m rpc
forall a b. (a -> b) -> a -> b
$ \Call rpc
call -> do
IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ Call rpc -> ResponseInitialMetadata rpc -> IO ()
forall {k} (rpc :: k).
HasCallStack =>
Call rpc -> ResponseInitialMetadata rpc -> IO ()
setResponseInitialMetadata Call rpc
call ResponseInitialMetadata rpc
forall a. Default a => a
def
Call rpc -> m ()
k Call rpc
call
mkRpcHandlerNoDefMetadata :: (Call rpc -> m ()) -> RpcHandler m rpc
mkRpcHandlerNoDefMetadata :: forall {k} (rpc :: k) (m :: * -> *).
(Call rpc -> m ()) -> RpcHandler m rpc
mkRpcHandlerNoDefMetadata = (Call rpc -> m ()) -> RpcHandler m rpc
forall k (m :: * -> *) (rpc :: k).
(Call rpc -> m ()) -> RpcHandler m rpc
RpcHandler
data SomeRpcHandler m = forall rpc.
SupportsServerRpc rpc
=> SomeRpcHandler (Proxy rpc) (RpcHandler m rpc)
someRpcHandler :: forall rpc m.
SupportsServerRpc rpc
=> RpcHandler m rpc -> SomeRpcHandler m
someRpcHandler :: forall {k} (rpc :: k) (m :: * -> *).
SupportsServerRpc rpc =>
RpcHandler m rpc -> SomeRpcHandler m
someRpcHandler = Proxy rpc -> RpcHandler m rpc -> SomeRpcHandler m
forall (m :: * -> *) {k} (rpc :: k).
SupportsServerRpc rpc =>
Proxy rpc -> RpcHandler m rpc -> SomeRpcHandler m
SomeRpcHandler Proxy rpc
forall {k} (t :: k). Proxy t
Proxy
hoistSomeRpcHandler ::
(forall a. m a -> n a)
-> SomeRpcHandler m
-> SomeRpcHandler n
hoistSomeRpcHandler :: forall (m :: * -> *) (n :: * -> *).
(forall a. m a -> n a) -> SomeRpcHandler m -> SomeRpcHandler n
hoistSomeRpcHandler forall a. m a -> n a
f (SomeRpcHandler Proxy rpc
p RpcHandler m rpc
h) =
Proxy rpc -> RpcHandler n rpc -> SomeRpcHandler n
forall (m :: * -> *) {k} (rpc :: k).
SupportsServerRpc rpc =>
Proxy rpc -> RpcHandler m rpc -> SomeRpcHandler m
SomeRpcHandler Proxy rpc
p ((forall a. m a -> n a) -> RpcHandler m rpc -> RpcHandler n rpc
forall {k} (m :: * -> *) (n :: * -> *) (rpc :: k).
(forall a. m a -> n a) -> RpcHandler m rpc -> RpcHandler n rpc
hoistRpcHandler m a -> n a
forall a. m a -> n a
f RpcHandler m rpc
h)
runHandler :: forall rpc.
HasCallStack
=> (forall x. IO x -> IO x)
-> Call rpc
-> RpcHandler IO rpc
-> IO ()
runHandler :: forall {k} (rpc :: k).
HasCallStack =>
(forall x. IO x -> IO x) -> Call rpc -> RpcHandler IO rpc -> IO ()
runHandler forall x. IO x -> IO x
unmask Call rpc
call RpcHandler IO rpc
handler = do
handlerThread <- ThreadLabel -> IO () -> IO (Async ())
forall a. ThreadLabel -> IO a -> IO (Async a)
asyncLabelled ThreadLabel
"grapesy:handler" IO ()
handler'
waitForHandler unmask call handlerThread
where
handler' :: IO ()
handler' :: IO ()
handler' = do
result <- IO () -> IO (Either SomeException ())
forall (m :: * -> *) e a.
(HasCallStack, MonadCatch m, Exception e) =>
m a -> m (Either e a)
try (IO () -> IO (Either SomeException ()))
-> IO () -> IO (Either SomeException ())
forall a b. (a -> b) -> a -> b
$ RpcHandler IO rpc -> Call rpc -> IO ()
forall k (m :: * -> *) (rpc :: k).
RpcHandler m rpc -> Call rpc -> m ()
runRpcHandler RpcHandler IO rpc
handler Call rpc
call
handlerTeardown result
handlerTeardown :: Either SomeException () -> IO ()
handlerTeardown :: Either SomeException () -> IO ()
handlerTeardown (Right ()) = do
forwarded <- Call rpc -> SomeException -> IO Bool
forall {k} (rpc :: k). Call rpc -> SomeException -> IO Bool
forwardException Call rpc
call (SomeException -> IO Bool) -> SomeException -> IO Bool
forall a b. (a -> b) -> a -> b
$ HandlerTerminated -> SomeException
forall e. Exception e => e -> SomeException
toException HandlerTerminated
HandlerTerminated
ignoreUncleanClose call $ ExitCaseSuccess ()
when forwarded $
throwM HandlerTerminated
handlerTeardown (Left SomeException
err) = do
_forwarded <- Call rpc -> SomeException -> IO Bool
forall {k} (rpc :: k). Call rpc -> SomeException -> IO Bool
forwardException Call rpc
call SomeException
err
ignoreUncleanClose call $ ExitCaseException err
throwM err
ignoreUncleanClose :: Call rpc -> ExitCase a -> IO ()
ignoreUncleanClose :: forall {k} (rpc :: k) a. Call rpc -> ExitCase a -> IO ()
ignoreUncleanClose Call{Channel (ServerSession rpc)
callChannel :: Channel (ServerSession rpc)
callChannel :: forall {k} (rpc :: k). Call rpc -> Channel (ServerSession rpc)
callChannel} ExitCase a
reason =
IO (Maybe SomeException) -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO (Maybe SomeException) -> IO ())
-> IO (Maybe SomeException) -> IO ()
forall a b. (a -> b) -> a -> b
$ Channel (ServerSession rpc)
-> ExitCase a -> IO (Maybe SomeException)
forall sess a.
HasCallStack =>
Channel sess -> ExitCase a -> IO (Maybe SomeException)
Session.close Channel (ServerSession rpc)
callChannel ExitCase a
reason
waitForHandler ::
HasCallStack
=> (forall x. IO x -> IO x)
-> Call rpc -> Async () -> IO ()
waitForHandler :: forall {k} (rpc :: k).
HasCallStack =>
(forall x. IO x -> IO x) -> Call rpc -> Async () -> IO ()
waitForHandler forall x. IO x -> IO x
unmask Call rpc
call Async ()
handlerThread = IO ()
loop
where
loop :: IO ()
loop :: IO ()
loop = IO () -> IO ()
forall x. IO x -> IO x
unmask (Async () -> IO ()
forall a. Async a -> IO a
wait Async ()
handlerThread) IO () -> (SomeException -> IO ()) -> IO ()
forall e a.
(HasCallStack, Exception e) =>
IO a -> (e -> IO a) -> IO a
forall (m :: * -> *) e a.
(MonadCatch m, HasCallStack, Exception e) =>
m a -> (e -> m a) -> m a
`catch` SomeException -> IO ()
handleException
handleException :: SomeException -> IO ()
handleException :: SomeException -> IO ()
handleException SomeException
err
| Just (KilledByThreadManager Maybe SomeException
mErr) <- SomeException -> Maybe KilledByThreadManager
forall e. Exception e => SomeException -> Maybe e
fromException SomeException
err = do
let exitReason :: ExitCase ()
exitReason :: ExitCase ()
exitReason =
case Maybe SomeException
mErr of
Maybe SomeException
Nothing -> () -> ExitCase ()
forall a. a -> ExitCase a
ExitCaseSuccess ()
Just SomeException
exitWithException ->
SomeException -> ExitCase ()
forall a. SomeException -> ExitCase a
ExitCaseException (SomeException -> ExitCase ())
-> (ClientDisconnected -> SomeException)
-> ClientDisconnected
-> ExitCase ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ClientDisconnected -> SomeException
forall e. Exception e => e -> SomeException
toException (ClientDisconnected -> ExitCase ())
-> ClientDisconnected -> ExitCase ()
forall a b. (a -> b) -> a -> b
$
SomeException -> CallStack -> ClientDisconnected
ClientDisconnected SomeException
exitWithException CallStack
HasCallStack => CallStack
callStack
Call rpc -> ExitCase () -> IO ()
forall {k} (rpc :: k) a. Call rpc -> ExitCase a -> IO ()
ignoreUncleanClose Call rpc
call ExitCase ()
exitReason
IO ()
loop
| Bool
otherwise = do
Async () -> SomeException -> IO ()
forall e a. Exception e => Async a -> e -> IO ()
cancelWith Async ()
handlerThread SomeException
err
SomeException -> IO ()
forall e a. (HasCallStack, Exception e) => e -> IO a
forall (m :: * -> *) e a.
(MonadThrow m, HasCallStack, Exception e) =>
e -> m a
throwM SomeException
err
forwardException :: Call rpc -> SomeException -> IO Bool
forwardException :: forall {k} (rpc :: k). Call rpc -> SomeException -> IO Bool
forwardException call :: Call rpc
call@Call{ServerContext
callContext :: ServerContext
callContext :: forall {k} (rpc :: k). Call rpc -> ServerContext
callContext} SomeException
err = do
trailers <- ServerParams -> SomeException -> IO ProperTrailers
serverExceptionToClientError (ServerContext -> ServerParams
serverParams ServerContext
callContext) SomeException
err
(True <$ sendProperTrailers call trailers) `catch` handler
where
handler :: SomeException -> IO Bool
handler :: SomeException -> IO Bool
handler SomeException
_e = Bool -> IO Bool
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False