{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
module Capnp.Rpc.Server
( Server(..)
, ServerOps(..)
, CallInfo(..)
, runServer
, MethodHandler
, pureHandler
, rawHandler
, rawAsyncHandler
, methodThrow
, methodUnimplemented
, untypedHandler
, toUntypedHandler
, fromUntypedHandler
, invoke
) where
import Control.Concurrent.STM
import Control.Monad.STM.Class
import Data.Word
import Control.Exception.Safe (MonadCatch, try)
import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.Primitive (PrimMonad, PrimState)
import Data.Typeable (Typeable)
import Capnp.Classes
( Cerialize
, Decerialize(Cerial, decerialize)
, FromPtr(fromPtr)
, ToStruct(toStruct)
)
import Capnp.Convert (valueToMsg)
import Capnp.Message (Mutability(..))
import Capnp.Rpc.Errors (eMethodUnimplemented, wrapException)
import Capnp.Rpc.Promise (Fulfiller, breakPromise, fulfill, newCallback)
import Capnp.TraversalLimit (defaultLimit, evalLimitT)
import Capnp.Untyped (Ptr)
import Data.Mutable (freeze)
import qualified Capnp.Gen.Capnp.Rpc.Pure as RpcGen
import qualified Capnp.Message as Message
import qualified Capnp.Untyped as Untyped
import qualified Internal.TCloseQ as TCloseQ
newtype MethodHandler m p r = MethodHandler
{ MethodHandler m p r
-> Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ()
handleMethod
:: Maybe (Ptr 'Const)
-> Fulfiller (Maybe (Ptr 'Const))
-> m ()
}
invoke
:: MonadSTM m
=> MethodHandler m (Maybe (Ptr 'Const)) (Maybe (Ptr 'Const))
-> Maybe (Ptr 'Const)
-> Fulfiller (Maybe (Ptr 'Const))
-> m ()
invoke :: MethodHandler m (Maybe (Ptr 'Const)) (Maybe (Ptr 'Const))
-> Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ()
invoke = MethodHandler m (Maybe (Ptr 'Const)) (Maybe (Ptr 'Const))
-> Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ()
forall (m :: * -> *) p r.
MethodHandler m p r
-> Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ()
handleMethod
pureHandler ::
( MonadCatch m
, MonadSTM m
, PrimMonad m
, s ~ PrimState m
, Decerialize p
, FromPtr 'Const (Cerial 'Const p)
, Cerialize s r
, ToStruct ('Mut s) (Cerial ('Mut s) r)
) =>
(cap -> p -> m r)
-> cap
-> MethodHandler m p r
pureHandler :: (cap -> p -> m r) -> cap -> MethodHandler m p r
pureHandler cap -> p -> m r
f cap
cap = MethodHandler :: forall (m :: * -> *) p r.
(Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ())
-> MethodHandler m p r
MethodHandler
{ handleMethod :: Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ()
handleMethod = \Maybe (Ptr 'Const)
ptr Fulfiller (Maybe (Ptr 'Const))
reply -> do
p
param <- WordCount -> LimitT m p -> m p
forall (m :: * -> *) a.
MonadThrow m =>
WordCount -> LimitT m a -> m a
evalLimitT WordCount
defaultLimit (LimitT m p -> m p) -> LimitT m p -> m p
forall a b. (a -> b) -> a -> b
$
Message 'Const -> Maybe (Ptr 'Const) -> LimitT m (Cerial 'Const p)
forall (mut :: Mutability) a (m :: * -> *).
(FromPtr mut a, ReadCtx m mut) =>
Message mut -> Maybe (Ptr mut) -> m a
fromPtr Message 'Const
Message.empty Maybe (Ptr 'Const)
ptr LimitT m (Cerial 'Const p)
-> (Cerial 'Const p -> LimitT m p) -> LimitT m p
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Cerial 'Const p -> LimitT m p
forall a (m :: * -> *).
(Decerialize a, ReadCtx m 'Const) =>
Cerial 'Const a -> m a
decerialize
Either SomeException r
result <- m r -> m (Either SomeException r)
forall (m :: * -> *) e a.
(MonadCatch m, Exception e) =>
m a -> m (Either e a)
try (m r -> m (Either SomeException r))
-> m r -> m (Either SomeException r)
forall a b. (a -> b) -> a -> b
$ cap -> p -> m r
f cap
cap p
param
case Either SomeException r
result of
Right r
val -> do
Struct 'Const
struct <- WordCount -> LimitT m (Struct 'Const) -> m (Struct 'Const)
forall (m :: * -> *) a.
MonadThrow m =>
WordCount -> LimitT m a -> m a
evalLimitT WordCount
defaultLimit (LimitT m (Struct 'Const) -> m (Struct 'Const))
-> LimitT m (Struct 'Const) -> m (Struct 'Const)
forall a b. (a -> b) -> a -> b
$
r -> LimitT m (Message ('Mut s))
forall (m :: * -> *) s a.
(MonadLimit m, WriteCtx m s, Cerialize s a,
ToStruct ('Mut s) (Cerial ('Mut s) a)) =>
a -> m (Message ('Mut s))
valueToMsg r
val LimitT m (Message ('Mut s))
-> (Message ('Mut s) -> LimitT m (Message 'Const))
-> LimitT m (Message 'Const)
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Message ('Mut s) -> LimitT m (Message 'Const)
forall a (m :: * -> *) s.
(Thaw a, PrimMonad m, PrimState m ~ s) =>
Mutable s a -> m a
freeze LimitT m (Message 'Const)
-> (Message 'Const -> LimitT m (Struct 'Const))
-> LimitT m (Struct 'Const)
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Message 'Const -> LimitT m (Struct 'Const)
forall (m :: * -> *) (mut :: Mutability).
ReadCtx m mut =>
Message mut -> m (Struct mut)
Untyped.rootPtr
STM () -> m ()
forall (m :: * -> *) a. MonadSTM m => STM a -> m a
liftSTM (STM () -> m ()) -> STM () -> m ()
forall a b. (a -> b) -> a -> b
$ Fulfiller (Maybe (Ptr 'Const)) -> Maybe (Ptr 'Const) -> STM ()
forall (m :: * -> *) a. MonadSTM m => Fulfiller a -> a -> m ()
fulfill Fulfiller (Maybe (Ptr 'Const))
reply (Ptr 'Const -> Maybe (Ptr 'Const)
forall a. a -> Maybe a
Just (Struct 'Const -> Ptr 'Const
forall (mut :: Mutability). Struct mut -> Ptr mut
Untyped.PtrStruct Struct 'Const
struct))
Left SomeException
e ->
STM () -> m ()
forall (m :: * -> *) a. MonadSTM m => STM a -> m a
liftSTM (STM () -> m ()) -> STM () -> m ()
forall a b. (a -> b) -> a -> b
$ Fulfiller (Maybe (Ptr 'Const)) -> Exception -> STM ()
forall (m :: * -> *) a.
MonadSTM m =>
Fulfiller a -> Exception -> m ()
breakPromise Fulfiller (Maybe (Ptr 'Const))
reply (Bool -> SomeException -> Exception
wrapException Bool
False SomeException
e)
}
rawHandler ::
( MonadCatch m
, MonadSTM m
, PrimMonad m
, s ~ PrimState m
, Decerialize p
, FromPtr 'Const (Cerial 'Const p)
, Decerialize r
, ToStruct 'Const (Cerial 'Const r)
) =>
(cap -> Cerial 'Const p -> m (Cerial 'Const r))
-> cap
-> MethodHandler m p r
rawHandler :: (cap -> Cerial 'Const p -> m (Cerial 'Const r))
-> cap -> MethodHandler m p r
rawHandler cap -> Cerial 'Const p -> m (Cerial 'Const r)
f cap
cap = MethodHandler :: forall (m :: * -> *) p r.
(Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ())
-> MethodHandler m p r
MethodHandler
{ handleMethod :: Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ()
handleMethod = \Maybe (Ptr 'Const)
ptr Fulfiller (Maybe (Ptr 'Const))
reply -> do
Cerial 'Const p
cerial <- WordCount -> LimitT m (Cerial 'Const p) -> m (Cerial 'Const p)
forall (m :: * -> *) a.
MonadThrow m =>
WordCount -> LimitT m a -> m a
evalLimitT WordCount
defaultLimit (LimitT m (Cerial 'Const p) -> m (Cerial 'Const p))
-> LimitT m (Cerial 'Const p) -> m (Cerial 'Const p)
forall a b. (a -> b) -> a -> b
$ Message 'Const -> Maybe (Ptr 'Const) -> LimitT m (Cerial 'Const p)
forall (mut :: Mutability) a (m :: * -> *).
(FromPtr mut a, ReadCtx m mut) =>
Message mut -> Maybe (Ptr mut) -> m a
fromPtr Message 'Const
Message.empty Maybe (Ptr 'Const)
ptr
Either SomeException (Cerial 'Const r)
result <- m (Cerial 'Const r) -> m (Either SomeException (Cerial 'Const r))
forall (m :: * -> *) e a.
(MonadCatch m, Exception e) =>
m a -> m (Either e a)
try (m (Cerial 'Const r) -> m (Either SomeException (Cerial 'Const r)))
-> m (Cerial 'Const r)
-> m (Either SomeException (Cerial 'Const r))
forall a b. (a -> b) -> a -> b
$ cap -> Cerial 'Const p -> m (Cerial 'Const r)
f cap
cap Cerial 'Const p
cerial
case Either SomeException (Cerial 'Const r)
result of
Right Cerial 'Const r
val -> STM () -> m ()
forall (m :: * -> *) a. MonadSTM m => STM a -> m a
liftSTM (STM () -> m ()) -> STM () -> m ()
forall a b. (a -> b) -> a -> b
$ Fulfiller (Maybe (Ptr 'Const)) -> Maybe (Ptr 'Const) -> STM ()
forall (m :: * -> *) a. MonadSTM m => Fulfiller a -> a -> m ()
fulfill Fulfiller (Maybe (Ptr 'Const))
reply (Ptr 'Const -> Maybe (Ptr 'Const)
forall a. a -> Maybe a
Just (Struct 'Const -> Ptr 'Const
forall (mut :: Mutability). Struct mut -> Ptr mut
Untyped.PtrStruct (Cerial 'Const r -> Struct 'Const
forall (mut :: Mutability) a. ToStruct mut a => a -> Struct mut
toStruct Cerial 'Const r
val)))
Left SomeException
e -> STM () -> m ()
forall (m :: * -> *) a. MonadSTM m => STM a -> m a
liftSTM (STM () -> m ()) -> STM () -> m ()
forall a b. (a -> b) -> a -> b
$ Fulfiller (Maybe (Ptr 'Const)) -> Exception -> STM ()
forall (m :: * -> *) a.
MonadSTM m =>
Fulfiller a -> Exception -> m ()
breakPromise Fulfiller (Maybe (Ptr 'Const))
reply (Bool -> SomeException -> Exception
wrapException Bool
False SomeException
e)
}
rawAsyncHandler ::
( MonadCatch m
, MonadSTM m
, PrimMonad m
, s ~ PrimState m
, Decerialize p
, FromPtr 'Const (Cerial 'Const p)
, Decerialize r
, ToStruct 'Const (Cerial 'Const r)
) =>
(cap -> Cerial 'Const p -> Fulfiller (Cerial 'Const r) -> m ())
-> cap
-> MethodHandler m p r
rawAsyncHandler :: (cap -> Cerial 'Const p -> Fulfiller (Cerial 'Const r) -> m ())
-> cap -> MethodHandler m p r
rawAsyncHandler cap -> Cerial 'Const p -> Fulfiller (Cerial 'Const r) -> m ()
f cap
cap = MethodHandler :: forall (m :: * -> *) p r.
(Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ())
-> MethodHandler m p r
MethodHandler
{ handleMethod :: Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ()
handleMethod = \Maybe (Ptr 'Const)
ptr Fulfiller (Maybe (Ptr 'Const))
reply -> do
Fulfiller (Cerial 'Const r)
fulfiller <- (Either Exception (Cerial 'Const r) -> STM ())
-> m (Fulfiller (Cerial 'Const r))
forall (m :: * -> *) a.
MonadSTM m =>
(Either Exception a -> STM ()) -> m (Fulfiller a)
newCallback ((Either Exception (Cerial 'Const r) -> STM ())
-> m (Fulfiller (Cerial 'Const r)))
-> (Either Exception (Cerial 'Const r) -> STM ())
-> m (Fulfiller (Cerial 'Const r))
forall a b. (a -> b) -> a -> b
$ \case
Left Exception
e -> Fulfiller (Maybe (Ptr 'Const)) -> Exception -> STM ()
forall (m :: * -> *) a.
MonadSTM m =>
Fulfiller a -> Exception -> m ()
breakPromise Fulfiller (Maybe (Ptr 'Const))
reply Exception
e
Right Cerial 'Const r
v -> Fulfiller (Maybe (Ptr 'Const)) -> Maybe (Ptr 'Const) -> STM ()
forall (m :: * -> *) a. MonadSTM m => Fulfiller a -> a -> m ()
fulfill Fulfiller (Maybe (Ptr 'Const))
reply (Maybe (Ptr 'Const) -> STM ()) -> Maybe (Ptr 'Const) -> STM ()
forall a b. (a -> b) -> a -> b
$ Ptr 'Const -> Maybe (Ptr 'Const)
forall a. a -> Maybe a
Just (Struct 'Const -> Ptr 'Const
forall (mut :: Mutability). Struct mut -> Ptr mut
Untyped.PtrStruct (Cerial 'Const r -> Struct 'Const
forall (mut :: Mutability) a. ToStruct mut a => a -> Struct mut
toStruct Cerial 'Const r
v))
Cerial 'Const p
cerial <- WordCount -> LimitT m (Cerial 'Const p) -> m (Cerial 'Const p)
forall (m :: * -> *) a.
MonadThrow m =>
WordCount -> LimitT m a -> m a
evalLimitT WordCount
defaultLimit (LimitT m (Cerial 'Const p) -> m (Cerial 'Const p))
-> LimitT m (Cerial 'Const p) -> m (Cerial 'Const p)
forall a b. (a -> b) -> a -> b
$ Message 'Const -> Maybe (Ptr 'Const) -> LimitT m (Cerial 'Const p)
forall (mut :: Mutability) a (m :: * -> *).
(FromPtr mut a, ReadCtx m mut) =>
Message mut -> Maybe (Ptr mut) -> m a
fromPtr Message 'Const
Message.empty Maybe (Ptr 'Const)
ptr
cap -> Cerial 'Const p -> Fulfiller (Cerial 'Const r) -> m ()
f cap
cap Cerial 'Const p
cerial Fulfiller (Cerial 'Const r)
fulfiller
}
toUntypedHandler
:: MethodHandler m p r
-> MethodHandler m (Maybe (Ptr 'Const)) (Maybe (Ptr 'Const))
toUntypedHandler :: MethodHandler m p r
-> MethodHandler m (Maybe (Ptr 'Const)) (Maybe (Ptr 'Const))
toUntypedHandler MethodHandler{Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ()
handleMethod :: Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ()
handleMethod :: forall (m :: * -> *) p r.
MethodHandler m p r
-> Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ()
..} = MethodHandler :: forall (m :: * -> *) p r.
(Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ())
-> MethodHandler m p r
MethodHandler{Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ()
handleMethod :: Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ()
handleMethod :: Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ()
..}
fromUntypedHandler
:: MethodHandler m (Maybe (Ptr 'Const)) (Maybe (Ptr 'Const))
-> MethodHandler m p r
fromUntypedHandler :: MethodHandler m (Maybe (Ptr 'Const)) (Maybe (Ptr 'Const))
-> MethodHandler m p r
fromUntypedHandler MethodHandler{Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ()
handleMethod :: Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ()
handleMethod :: forall (m :: * -> *) p r.
MethodHandler m p r
-> Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ()
..} = MethodHandler :: forall (m :: * -> *) p r.
(Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ())
-> MethodHandler m p r
MethodHandler{Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ()
handleMethod :: Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ()
handleMethod :: Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ()
..}
untypedHandler
:: (Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ())
-> MethodHandler m (Maybe (Ptr 'Const)) (Maybe (Ptr 'Const))
untypedHandler :: (Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ())
-> MethodHandler m (Maybe (Ptr 'Const)) (Maybe (Ptr 'Const))
untypedHandler = (Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ())
-> MethodHandler m (Maybe (Ptr 'Const)) (Maybe (Ptr 'Const))
forall (m :: * -> *) p r.
(Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ())
-> MethodHandler m p r
MethodHandler
methodThrow :: MonadIO m => RpcGen.Exception -> MethodHandler m p r
methodThrow :: Exception -> MethodHandler m p r
methodThrow Exception
exn = MethodHandler :: forall (m :: * -> *) p r.
(Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ())
-> MethodHandler m p r
MethodHandler
{ handleMethod :: Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ()
handleMethod = \Maybe (Ptr 'Const)
_ Fulfiller (Maybe (Ptr 'Const))
fulfiller -> IO () -> m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ Fulfiller (Maybe (Ptr 'Const)) -> Exception -> IO ()
forall (m :: * -> *) a.
MonadSTM m =>
Fulfiller a -> Exception -> m ()
breakPromise Fulfiller (Maybe (Ptr 'Const))
fulfiller Exception
exn
}
methodUnimplemented :: MonadIO m => MethodHandler m p r
methodUnimplemented :: MethodHandler m p r
methodUnimplemented = Exception -> MethodHandler m p r
forall (m :: * -> *) p r.
MonadIO m =>
Exception -> MethodHandler m p r
methodThrow Exception
eMethodUnimplemented
class Monad m => Server m a | a -> m where
shutdown :: a -> m ()
shutdown a
_ = () -> m ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
unwrap :: Typeable b => a -> Maybe b
unwrap a
_ = Maybe b
forall a. Maybe a
Nothing
data ServerOps m = ServerOps
{ ServerOps m
-> Word64
-> Word16
-> MethodHandler m (Maybe (Ptr 'Const)) (Maybe (Ptr 'Const))
handleCall
:: Word64
-> Word16
-> MethodHandler m (Maybe (Ptr 'Const)) (Maybe (Ptr 'Const))
, ServerOps m -> m ()
handleStop :: m ()
, ServerOps m -> forall a. Typeable a => Maybe a
handleCast :: forall a. Typeable a => Maybe a
}
data CallInfo = CallInfo
{ CallInfo -> Word64
interfaceId :: !Word64
, CallInfo -> Word16
methodId :: !Word16
, CallInfo -> Maybe (Ptr 'Const)
arguments :: Maybe (Ptr 'Const)
, CallInfo -> Fulfiller (Maybe (Ptr 'Const))
response :: Fulfiller (Maybe (Ptr 'Const))
}
runServer :: TCloseQ.Q CallInfo -> ServerOps IO -> IO ()
runServer :: Q CallInfo -> ServerOps IO -> IO ()
runServer Q CallInfo
q ServerOps IO
ops = IO ()
go
where
go :: IO ()
go = STM (Maybe CallInfo) -> IO (Maybe CallInfo)
forall a. STM a -> IO a
atomically (Q CallInfo -> STM (Maybe CallInfo)
forall a. Q a -> STM (Maybe a)
TCloseQ.read Q CallInfo
q) IO (Maybe CallInfo) -> (Maybe CallInfo -> IO ()) -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Maybe CallInfo
Nothing ->
() -> IO ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
Just CallInfo{Word64
interfaceId :: Word64
interfaceId :: CallInfo -> Word64
interfaceId, Word16
methodId :: Word16
methodId :: CallInfo -> Word16
methodId, Maybe (Ptr 'Const)
arguments :: Maybe (Ptr 'Const)
arguments :: CallInfo -> Maybe (Ptr 'Const)
arguments, Fulfiller (Maybe (Ptr 'Const))
response :: Fulfiller (Maybe (Ptr 'Const))
response :: CallInfo -> Fulfiller (Maybe (Ptr 'Const))
response} -> do
MethodHandler IO (Maybe (Ptr 'Const)) (Maybe (Ptr 'Const))
-> Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> IO ()
forall (m :: * -> *) p r.
MethodHandler m p r
-> Maybe (Ptr 'Const) -> Fulfiller (Maybe (Ptr 'Const)) -> m ()
handleMethod
(ServerOps IO
-> Word64
-> Word16
-> MethodHandler IO (Maybe (Ptr 'Const)) (Maybe (Ptr 'Const))
forall (m :: * -> *).
ServerOps m
-> Word64
-> Word16
-> MethodHandler m (Maybe (Ptr 'Const)) (Maybe (Ptr 'Const))
handleCall ServerOps IO
ops Word64
interfaceId Word16
methodId)
Maybe (Ptr 'Const)
arguments
Fulfiller (Maybe (Ptr 'Const))
response
IO ()
go