@@ -1,7 +1,8 @@
1−{-# LANGUAGE DerivingVia, DeriveGeneric, RankNTypes, ScopedTypeVariables, MultiParamTypeClasses, OverloadedStrings, GeneralizedNewtypeDeriving, CPP, ExistentialQuantification, StandaloneDeriving, GADTs, RecordWildCards #-}
1+{-# LANGUAGE DerivingVia, DeriveGeneric, RankNTypes, ScopedTypeVariables, MultiParamTypeClasses, OverloadedStrings, GeneralizedNewtypeDeriving, TypeApplications, CPP, ExistentialQuantification, StandaloneDeriving #-}
2{-# OPTIONS_GHC -fno-warn-orphans #-}
2{-# OPTIONS_GHC -fno-warn-orphans #-}
3{- HLINT ignore "Use lambda-case" -}
3{- HLINT ignore "Use lambda-case" -}
4module Network.RPC.Curryer.Server where
4module Network.RPC.Curryer.Server where
5import qualified Streamly.Prelude as S
6import qualified Streamly.Prelude as S
6import Streamly.Network.Socket
7import Streamly.Network.Socket
7import Streamly.Internal.Network.Socket (handleWithM)
8import Streamly.Internal.Network.Socket (handleWithM)
@@ -25,24 +26,21 @@ import Streamly.Data.Fold as FL
25import qualified Streamly.Internal.Data.Stream.IsStream as S
26import qualified Streamly.Internal.Data.Stream.IsStream as S
26import qualified Data.Binary as B
27import qualified Data.Binary as B
27import qualified Data.UUID as UUIDBase
28import qualified Data.UUID as UUIDBase
28−import qualified Data.UUID.V4 as UUIDBase
32import qualified Network.RPC.Curryer.StreamlyAdditions as SA
30import qualified Network.RPC.Curryer.StreamlyAdditions as SA
33--import Control.Monad
31--import Control.Monad
34import Data.Hashable
32import Data.Hashable
35−import System.Timeout
33+--import System.Timeout
36import qualified Network.ByteOrder as BO
34import qualified Network.ByteOrder as BO
39-- for toArrayS conversion
36-- for toArrayS conversion
40−import qualified Data.ByteString.Internal as BSI
37+{-import qualified Data.ByteString.Internal as BSI
41−import qualified Streamly.Internal.Data.Array.Storable.Foreign.Types as Arr
38+import qualified Streamly.Internal.Data.Array.Storable.Foreign.Types as SA
39+import Foreign.ForeignPtr (plusForeignPtr)
42import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
40import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
43−import GHC.Ptr (minusPtr)
41+import GHC.Ptr (plusPtr)
45−-- define CURRYER_SHOW_BYTES 1
43+-- #define CURRYER_SHOW_BYTES 1
46#if CURRYER_SHOW_BYTES == 1
44#if CURRYER_SHOW_BYTES == 1
47import Debug.Trace
45import Debug.Trace
@@ -68,58 +66,45 @@ withLock (Locking mvar v) m =
68lockless :: Locking a -> a
66lockless :: Locking a -> a
69lockless (Locking _ a) = a
67lockless (Locking _ a) = a
71−type Timeout = Word32
69+type Timeout = Int
73type BinaryMessage = BS.ByteString
71type BinaryMessage = BS.ByteString
75--includes the fingerprint of the incoming data type (held in the BinaryMessage) to determine how to dispatch the message.
73--includes the fingerprint of the incoming data type (held in the BinaryMessage) to determine how to dispatch the message.
76--add another envelope type for unencoded binary messages for any easy optimization for in-process communication
74--add another envelope type for unencoded binary messages for any easy optimization for in-process communication
77−data Envelope = Envelope {
75+data Envelope = Envelope !Fingerprint !MessageType !UUID !BinaryMessage
78− envFingerprint :: !Fingerprint,
79− envMessageType :: !MessageType,
81− envPayload :: !BinaryMessage
83 deriving (Generic, Show)
76 deriving (Generic, Show)
84−
77+ deriving Serialise via WineryVariant Envelope
85−type TimeoutMicroseconds = Int
87deriving instance Generic Fingerprint
79deriving instance Generic Fingerprint
88deriving via WineryVariant Fingerprint instance Serialise Fingerprint
80deriving via WineryVariant Fingerprint instance Serialise Fingerprint
90−-- | Internal type used to mark envelope types.
91−data MessageType = RequestMessage TimeoutMicroseconds
82+data MessageType = RequestMessage
92 | ResponseMessage
83 | ResponseMessage
93− | TimeoutResponseMessage
94− | ExceptionResponseMessage
95 deriving (Generic, Show)
84 deriving (Generic, Show)
96 deriving Serialise via WineryVariant MessageType
85 deriving Serialise via WineryVariant MessageType
98−-- | A list of `RequestHandler`s.
87+data MessageHandlers =
99−type RequestHandlers serverState = [RequestHandler serverState]
88+ forall a b. (Show a, Serialise a, Serialise b) => MessageHandlers [RequestHandler a b]
101−-- | Data types for server-side request handlers, in synchronous (client waits for return value) and asynchronous (client does not wait for return value) forms.
102−data RequestHandler serverState where
103− -- | create a request handler with a response
104− RequestHandler :: forall a b serverState. (Serialise a, Serialise b) => (ConnectionState serverState -> a -> IO b) -> RequestHandler serverState
105− -- | create an asynchronous request handler where the client does not expect nor await a response
106− AsyncRequestHandler :: forall a serverState. Serialise a => (ConnectionState serverState -> a -> IO ()) -> RequestHandler serverState
108−-- | Server state sent in via `serve` and passed to `RequestHandler`s.
109−data ConnectionState a = ConnectionState {
110− connectionServerState :: a,
111− connectionSocket :: Locking Socket
114−-- | Used by server-side request handlers to send additional messages to the client. This is useful for sending asynchronous responses to the client outside of the normal request-response flow. The locking socket can be found in the ConnectionState when a request handler is called.
115−sendMessage :: Serialise a => Locking Socket -> a -> IO ()
116−sendMessage lockSock msg = do
117− requestID <- UUID <$> UUIDBase.nextRandom
119− Envelope (fingerprint msg) (RequestMessage timeout') requestID (serialise msg)
121− sendEnvelope env lockSock
90+type RequestHandler a b = a -> IO b
92+{-data Message a = Response UUID a
93+ | AsyncRequest UUID a
94+ | ResponseExpectedRequest UUID (Maybe Timeout) a
95+ | TimedOutResponse UUID
96+ | ExceptionResponse UUID String
97+ deriving (Generic, Show)
98+ deriving Serialise via WineryVariant (Message a)
100+-- | The response type
102+data HandlerResponse a = NoResponse
104+ | HandlerException String
106+ deriving Serialise via WineryVariant (HandlerResponse a)
123--avoid orphan instance
108--avoid orphan instance
124newtype UUID = UUID { _unUUID :: UUIDBase.UUID }
109newtype UUID = UUID { _unUUID :: UUIDBase.UUID }
125 deriving (Show, Eq, B.Binary, Hashable)
110 deriving (Show, Eq, B.Binary, Hashable)
@@ -138,18 +123,13 @@ instance Serialise UUID where
138 x -> error $ "invalid schema element " <> show x
123 x -> error $ "invalid schema element " <> show x
139 decodeCurrent = B.decode . BSL.fromStrict <$> (decodeVarInt >>= getBytes)
124 decodeCurrent = B.decode . BSL.fromStrict <$> (decodeVarInt >>= getBytes)
141−-- | Errors from remote calls.
126+
142data ConnectionError = CodecError String -- show of WineryException from exception initiator which cannot otherwise be transmitted over a line due to dependencies on TypeReps
127data ConnectionError = CodecError String -- show of WineryException from exception initiator which cannot otherwise be transmitted over a line due to dependencies on TypeReps
143 | TimeoutError
128 | TimeoutError
144 | ExceptionError String
129 | ExceptionError String
145 deriving (Generic, Show, Eq)
130 deriving (Generic, Show, Eq)
146 deriving Serialise via WineryVariant ConnectionError
131 deriving Serialise via WineryVariant ConnectionError
148−data TimeoutException = TimeoutException
151−instance Exception TimeoutException
153type HostAddr = (Word8, Word8, Word8, Word8)
133type HostAddr = (Word8, Word8, Word8, Word8)
155allHostAddrs,localHostAddr :: HostAddr
135allHostAddrs,localHostAddr :: HostAddr
@@ -159,18 +139,12 @@ localHostAddr = (127,0,0,1)
159-- Each message is length-prefixed by a 32-bit unsigned length.
139-- Each message is length-prefixed by a 32-bit unsigned length.
160envelopeP :: Parser IO Word8 Envelope
140envelopeP :: Parser IO Word8 Envelope
161envelopeP = do
141envelopeP = do
162− let msgTypeP = (P.satisfy (== 0) *>
143+ msgTypeP = (P.satisfy (== 0) *> pure RequestMessage) `P.alt`
163− (RequestMessage . fromIntegral <$> word32P)) `P.alt`
164− (P.satisfy (== 1) $> ResponseMessage) `P.alt`
144+ (P.satisfy (== 1) *> pure ResponseMessage)
165− (P.satisfy (== 2) $> TimeoutResponseMessage) `P.alt`
166− (P.satisfy (== 3) $> ExceptionResponseMessage)
167 lenPrefixedByteStringP = do
145 lenPrefixedByteStringP = do
168 c <- fromIntegral <$> word32P
146 c <- fromIntegral <$> word32P
169− --streamly can't handle takeEQ 0, so add special handling
147+ BS.pack <$> P.takeEQ c s
173− fromArray <$> P.takeEQ c (Arr.writeN c)
174 Envelope <$> fingerprintP <*> msgTypeP <*> uuidP <*> lenPrefixedByteStringP
148 Envelope <$> fingerprintP <*> msgTypeP <*> uuidP <*> lenPrefixedByteStringP
176encodeEnvelope :: Envelope -> BS.ByteString
150encodeEnvelope :: Envelope -> BS.ByteString
@@ -179,19 +153,19 @@ encodeEnvelope (Envelope (Fingerprint fp1 fp2) msgType msgId bs) =
180 fingerprintBs = BO.bytestring64 fp1 <> BO.bytestring64 fp2
154 fingerprintBs = BO.bytestring64 fp1 <> BO.bytestring64 fp2
181 msgTypeBs = case msgType of
155 msgTypeBs = case msgType of
182− RequestMessage timeoutms -> BS.singleton 0 <> BO.bytestring32 (fromIntegral timeoutms)
156+ RequestMessage -> BS.singleton 0
183 ResponseMessage -> BS.singleton 1
157 ResponseMessage -> BS.singleton 1
184− TimeoutResponseMessage -> BS.singleton 2
185− ExceptionResponseMessage -> BS.singleton 3
186 msgIdBs =
158 msgIdBs =
187 case UUIDBase.toWords (_unUUID msgId) of
159 case UUIDBase.toWords (_unUUID msgId) of
188− (u1, u2, u3, u4) -> foldr ((<>) . BO.bytestring32) BS.empty [u1, u2, u3, u4]
160+ (u1, u2, u3, u4) -> foldr (<>) BS.empty (map BO.bytestring32 [u1, u2, u3, u4])
189 msgLen = fromIntegral (BS.length bs)
161 msgLen = fromIntegral (BS.length bs)
190 lenPrefixedBs = BO.bytestring32 msgLen <> bs
162 lenPrefixedBs = BO.bytestring32 msgLen <> bs
192fingerprintP :: Parser IO Word8 Fingerprint
164fingerprintP :: Parser IO Word8 Fingerprint
193−fingerprintP =
165+fingerprintP = do
194− Fingerprint <$> word64P <*> word64P
168+ pure (Fingerprint f1 f2)
196word64P :: Parser IO Word8 Word64
170word64P :: Parser IO Word8 Word64
197word64P = do
171word64P = do
@@ -212,112 +186,112 @@ uuidP = do
212 u1 <- word32P
186 u1 <- word32P
213 u2 <- word32P
187 u2 <- word32P
214 u3 <- word32P
188 u3 <- word32P
215− --u4 <- word32P
189+ u4 <- word32P
216− --pure (UUID (UUIDBase.fromWords u1 u2 u3 u4))-}
190+ pure (UUID (UUIDBase.fromWords u1 u2 u3 u4))
217− --(UUID . UUIDBase.fromWords) <$> word32P <*> word32P <*> word32P <*> word32P
218− UUID . UUIDBase.fromWords u1 u2 u3 <$> word32P
220type NewConnectionHandler msg = IO (Maybe msg)
192type NewConnectionHandler msg = IO (Maybe msg)
222type NewMessageHandler req resp = req -> IO resp
194type NewMessageHandler req resp = req -> IO resp
224−-- | Listen for new connections and handle requests which are passed the server state 's'. The MVar SockAddr can be be optionally used to know when the server is ready for processing requests.
226− RequestHandlers s->
197+ MessageHandlers ->
228 HostAddr ->
198 HostAddr ->
229 PortNumber ->
199 PortNumber ->
230 Maybe (MVar SockAddr) ->
200 Maybe (MVar SockAddr) ->
232−serve userMsgHandlers serverState hostaddr port mSockLock = do
202+serve userMsgHandler hostaddr port mSockLock = do
234 handleSock sock = do
204 handleSock sock = do
235 lockingSocket <- newLock sock
205 lockingSocket <- newLock sock
236− drainSocketMessages sock (serverEnvelopeHandler lockingSocket userMsgHandlers serverState)
206+ drainSocketMessages sock (serverEnvelopeHandler lockingSocket userMsgHandler)
238− S.serially (S.unfold (SA.acceptOnAddrWith [(ReuseAddr,1)] mSockLock) (hostaddr, port)) & S.parallely . S.mapM (handleWithM handleSock) & S.drain
208+ serially (S.unfold (SA.acceptOnAddrWith [(ReuseAddr,1)] mSockLock) (hostaddr, port)) & parallely . S.mapM (handleWithM handleSock) & S.drain
239 pure True
209 pure True
211+{-serverEnvelopeHandler :: Locking Socket -> MessageHandlers s -> Envelope -> IO ()
212+serverEnvelopeHandler sockLock (Envelope fprint RequestMessage msgId messagebs) = do
214+serverEnvelopeHandler sockLock (Envelope fprint ResponseMessage msgId messagebs) = do
241openEnvelope :: forall s. (Serialise s, Typeable s) => Envelope -> Maybe s
218openEnvelope :: forall s. (Serialise s, Typeable s) => Envelope -> Maybe s
242−openEnvelope (Envelope eprint _ _ bytes) =
219+openEnvelope (Envelope eprint _ _ bytes) = do
220+ {-let decoder = case getDecoder (schema (Proxy :: Proxy s)) of
221+ Left err -> error (show err)
243 if eprint == fingerprint (undefined :: s) then
223 if eprint == fingerprint (undefined :: s) then
244 case deserialise bytes of
224 case deserialise bytes of
245 Left err -> error (show err)
225 Left err -> error (show err)
246 Right v -> Just v
226 Right v -> Just v
227+ --Just (evalDecoder decoder bytes)
250−matchEnvelope :: forall a b s. (Serialise a, Serialise b, Typeable b) =>
252− (ConnectionState s -> a -> IO b) ->
253− Maybe (ConnectionState s -> a -> IO b, a)
254−matchEnvelope envelope dispatchf =
255− case openEnvelope envelope :: Maybe a of
257− Just decoded -> Just (dispatchf, decoded)
259−-- | Called by `serve` to process incoming envelope requests. Never returns, so use `async` to spin it off on another thread.
260serverEnvelopeHandler ::
231serverEnvelopeHandler ::
261 Locking Socket
232 Locking Socket
262− -> RequestHandlers s
233+ -> MessageHandlers
264 -> Envelope
234 -> Envelope
266−serverEnvelopeHandler _ _ _ (Envelope _ TimeoutResponseMessage _ _) = pure ()
267−serverEnvelopeHandler _ _ _ (Envelope _ ExceptionResponseMessage _ _) = pure ()
268−serverEnvelopeHandler _ _ _ (Envelope _ ResponseMessage _ _) = pure ()
269−serverEnvelopeHandler sockLock msgHandlers serverState envelope@(Envelope _ (RequestMessage timeoutms) msgId _) = do
236+serverEnvelopeHandler sockLock (MessageHandlers msgHandlers) envelope@(Envelope _ RequestMessage msgId _) = do
270 --find first matching handler
237 --find first matching handler
271− let runTimeout :: IO b -> IO (Maybe b)
238+ let firstMatch = foldr firstMatcher Nothing msgHandlers
272− runTimeout m =
239+ firstMatcher :: forall a b. (Serialise a, Serialise b) => RequestHandler a b -> Maybe (RequestHandler a b, a) -> Maybe (RequestHandler a b, a)
273− if timeoutms == 0 then
240+ firstMatcher msghandler Nothing =
274− (Just <$> m) `catch` timeoutExcHandler
241+ case openEnvelope envelope :: Maybe a of
275− else
242+ Just decoded -> Just (msghandler, decoded)
276− (timeout (fromIntegral timeoutms) m) `catch` timeoutExcHandler
243+ Nothing -> Nothing
277− --allow server-side function to throw TimeoutError which is caught here and becomes TimeoutError value
244+ firstMatcher _ acc = acc
278− timeoutExcHandler :: TimeoutException -> IO (Maybe b)
245+ case firstMatch of
279− timeoutExcHandler _ = pure Nothing
246+ Nothing -> error "failed to handle msg"
247+ Just (msghandler, decoded) -> do
248+ --TODO add exception handling
249+ response <- msghandler decoded
250+ sendEnvelope (Envelope (fingerprint response) ResponseMessage msgId (serialise response)) sockLock
251+serverEnvelopeHandler _ _ (Envelope _ ResponseMessage _ _) = error "server received response message"
281− sState = ConnectionState {
254+
282− connectionServerState = serverState,
255+ --putStrLn $ "GOT MSG " ++ show msg
283− connectionSocket = sockLock
256+ let --runTimeout :: IO (HandlerResponse resp) -> IO (Maybe (HandlerResponse resp))
284− }
257+ runTimeout mTimeout m = case mTimeout of
286− firstMatcher (RequestHandler msghandler) Nothing =
287− case matchEnvelope envelope msghandler of
288− Nothing -> pure Nothing
258+ Nothing -> Just <$> m
289− Just (dispatchf, decoded) -> do
259+ Just timeoutMicroseconds -> timeout timeoutMicroseconds m
290− --TODO add exception handling
260+ case msg of
291− mResponse <- runTimeout (dispatchf sState decoded)
261+ Response{} -> putStrLn "client sent response"
292− let envelopeResponse =
262+ ResponseExpectedRequest requestID mTimeout val -> do
293− case mResponse of
263+ let normalResponder = do
294− Just response ->
264+ resp <- runTimeout mTimeout (requestMessageHandler val)
295− Envelope (fingerprint response) ResponseMessage msgId (serialise response)
265+ case resp of
296− Nothing ->
266+ Just (HandlerResponse responseVal) -> sendMessage (Response requestID responseVal) sockLock
297− Envelope (fingerprint TimeoutError) TimeoutResponseMessage msgId BS.empty
267+ Just NoResponse -> error "attempt to return non-response to expected response message"
298− sendEnvelope envelopeResponse sockLock
268+ Just (HandlerException exc) -> sendMessage (ExceptionResponse @(Message resp) requestID exc) sockLock
299− pure (Just ())
269+ Nothing ->
300− firstMatcher (AsyncRequestHandler msghandler) Nothing =
270+ sendMessage (TimedOutResponse @(Message resp) requestID) sockLock
301− case matchEnvelope envelope msghandler of
271+ excHandler :: SomeException -> IO ()
302− Nothing -> pure Nothing
272+ excHandler e = do
303− Just (dispatchf, decoded) -> do
273+ --send exception to client
304− _ <- dispatchf sState decoded
274+ sendMessage (ExceptionResponse @(Message resp) requestID (show e)) sockLock
305− pure (Just ())
275+ throwIO e
306− firstMatcher _ acc = pure acc
276+ catch normalResponder excHandler
307− eExc <- try $ foldM_ (flip firstMatcher) Nothing msgHandlers :: IO (Either SomeException ())
277+ AsyncRequest _ val ->
308− case eExc of
278+ void $ requestMessageHandler val
309− Left exc ->
279+ --no response necessary
310− let env = Envelope (fingerprint (show exc)) ExceptionResponseMessage msgId (serialise (show exc)) in
280+ ExceptionResponse{} -> putStrLn "client sent exception response"
311− sendEnvelope env sockLock
281+ TimedOutResponse{} -> putStrLn "client sent timed out response"
312− Right () -> pure ()
282+-}
284+--add callback to allow for responses via socket
285+--type MessageHandler a = Message a -> IO ()
315type EnvelopeHandler = Envelope -> IO ()
287type EnvelopeHandler = Envelope -> IO ()
289+type AsyncMessageHandler a = a -> IO ()
317drainSocketMessages :: Socket -> EnvelopeHandler -> IO ()
291drainSocketMessages :: Socket -> EnvelopeHandler -> IO ()
318drainSocketMessages sock envelopeHandler = do
292drainSocketMessages sock envelopeHandler = do
319 let sockStream = S.unfold readWithBufferOf (1024 * 4, sock)
293 let sockStream = S.unfold readWithBufferOf (1024 * 4, sock)
320− S.drain $ S.serially $ S.parseMany envelopeP sockStream & S.mapM envelopeHandler
294+ S.drain $ serially $ S.parseMany envelopeP sockStream & S.mapM envelopeHandler
322--send length-tagged bytestring, perhaps should be in network byte order?
296--send length-tagged bytestring, perhaps should be in network byte order?
323sendEnvelope :: Envelope -> Locking Socket -> IO ()
297sendEnvelope :: Envelope -> Locking Socket -> IO ()
@@ -327,16 +301,7 @@ sendEnvelope envelope sockLock = do
327 --Socket.sendAll syscalls send() on a loop until all the bytes are sent, so we need socket locking here to account for serialized messages of size > PIPE_BUF
301 --Socket.sendAll syscalls send() on a loop until all the bytes are sent, so we need socket locking here to account for serialized messages of size > PIPE_BUF
328 withLock sockLock $ \socket' ->
302 withLock sockLock $ \socket' ->
329 Socket.sendAll socket' fullbytes
303 Socket.sendAll socket' fullbytes
330− --traceShowM ("sendEnvelope", envelope)
331 traceBytes "sendEnvelope" fullbytes
304 traceBytes "sendEnvelope" fullbytes
333fingerprint :: Typeable a => a -> Fingerprint
306fingerprint :: Typeable a => a -> Fingerprint
334fingerprint = typeRepFingerprint . typeOf
307fingerprint = typeRepFingerprint . typeOf
336−fromArray :: Arr.Array Word8 -> BSI.ByteString
337−fromArray Arr.Array {..}
338− | aLen == 0 = mempty
339− | otherwise = BSI.PS aStart 0 aLen
341− aStartPtr = unsafeForeignPtrToPtr aStart
342− aLen = aEnd `minusPtr` aStartPtr