@@ -1,4 +1,4 @@
1−{-# LANGUAGE DerivingVia, DeriveGeneric, RankNTypes, ScopedTypeVariables, MultiParamTypeClasses, OverloadedStrings, GeneralizedNewtypeDeriving, TypeApplications, CPP, ExistentialQuantification, StandaloneDeriving, GADTs #-}
1+{-# LANGUAGE DerivingVia, DeriveGeneric, RankNTypes, ScopedTypeVariables, MultiParamTypeClasses, OverloadedStrings, GeneralizedNewtypeDeriving, CPP, ExistentialQuantification, StandaloneDeriving, GADTs, RecordWildCards #-}
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
@@ -27,6 +27,7 @@ import qualified Data.Binary as B
27import qualified Data.UUID as UUIDBase
27import qualified Data.UUID as UUIDBase
28import qualified Data.UUID.V4 as UUIDBase
28import qualified Data.UUID.V4 as UUIDBase
29import Control.Monad
29import Control.Monad
31import qualified Network.RPC.Curryer.StreamlyAdditions as SA
32import qualified Network.RPC.Curryer.StreamlyAdditions as SA
32--import Control.Monad
33--import Control.Monad
@@ -34,14 +35,14 @@ import Data.Hashable
34import System.Timeout
35import System.Timeout
35import qualified Network.ByteOrder as BO
36import qualified Network.ByteOrder as BO
37-- for toArrayS conversion
39-- for toArrayS conversion
38−{-import qualified Data.ByteString.Internal as BSI
40+import qualified Data.ByteString.Internal as BSI
39−import qualified Streamly.Internal.Data.Array.Storable.Foreign.Types as SA
41+import qualified Streamly.Internal.Data.Array.Storable.Foreign.Types as Arr
40−import Foreign.ForeignPtr (plusForeignPtr)
41import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
42import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
42−import GHC.Ptr (plusPtr)
43+import GHC.Ptr (minusPtr)
44−-- #define CURRYER_SHOW_BYTES 1
45+-- define CURRYER_SHOW_BYTES 1
45#if CURRYER_SHOW_BYTES == 1
46#if CURRYER_SHOW_BYTES == 1
46import Debug.Trace
47import Debug.Trace
@@ -97,7 +98,7 @@ data MessageType = RequestMessage TimeoutMicroseconds
97-- | A list of `RequestHandler`s.
98-- | A list of `RequestHandler`s.
98type RequestHandlers serverState = [RequestHandler serverState]
99type RequestHandlers serverState = [RequestHandler serverState]
100−-- |
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.
101data RequestHandler serverState where
102data RequestHandler serverState where
102 -- | create a request handler with a response
103 -- | create a request handler with a response
103 RequestHandler :: forall a b serverState. (Serialise a, Serialise b) => (ConnectionState serverState -> a -> IO b) -> RequestHandler serverState
104 RequestHandler :: forall a b serverState. (Serialise a, Serialise b) => (ConnectionState serverState -> a -> IO b) -> RequestHandler serverState
@@ -110,13 +111,14 @@ data ConnectionState a = ConnectionState {
110 connectionSocket :: Locking Socket
111 connectionSocket :: Locking Socket
113−-- | 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.
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.
114−sendMessage :: Serialise a => ConnectionState s -> a -> IO ()
115+sendMessage :: Serialise a => Locking Socket -> a -> IO ()
115−sendMessage sState msg = do
116+sendMessage lockSock msg = do
116 requestID <- UUID <$> UUIDBase.nextRandom
117 requestID <- UUID <$> UUIDBase.nextRandom
117 let env =
118 let env =
118− Envelope (fingerprint msg) (RequestMessage 0) requestID (serialise msg)
119+ Envelope (fingerprint msg) (RequestMessage timeout') requestID (serialise msg)
119− sendEnvelope env (connectionSocket sState)
121+ sendEnvelope env lockSock
121--avoid orphan instance
123--avoid orphan instance
122newtype UUID = UUID { _unUUID :: UUIDBase.UUID }
124newtype UUID = UUID { _unUUID :: UUIDBase.UUID }
@@ -143,6 +145,11 @@ data ConnectionError = CodecError String -- show of WineryException from excepti
143 deriving (Generic, Show, Eq)
145 deriving (Generic, Show, Eq)
144 deriving Serialise via WineryVariant ConnectionError
146 deriving Serialise via WineryVariant ConnectionError
148+data TimeoutException = TimeoutException
151+instance Exception TimeoutException
146type HostAddr = (Word8, Word8, Word8, Word8)
153type HostAddr = (Word8, Word8, Word8, Word8)
148allHostAddrs,localHostAddr :: HostAddr
155allHostAddrs,localHostAddr :: HostAddr
@@ -152,15 +159,18 @@ localHostAddr = (127,0,0,1)
152-- Each message is length-prefixed by a 32-bit unsigned length.
159-- Each message is length-prefixed by a 32-bit unsigned length.
153envelopeP :: Parser IO Word8 Envelope
160envelopeP :: Parser IO Word8 Envelope
154envelopeP = do
161envelopeP = do
156− msgTypeP = (P.satisfy (== 0) *>
162+ let msgTypeP = (P.satisfy (== 0) *>
157 (RequestMessage . fromIntegral <$> word32P)) `P.alt`
163 (RequestMessage . fromIntegral <$> word32P)) `P.alt`
158− (P.satisfy (== 1) *> pure ResponseMessage) `P.alt`
164+ (P.satisfy (== 1) $> ResponseMessage) `P.alt`
159− (P.satisfy (== 2) *> pure TimeoutResponseMessage) `P.alt`
165+ (P.satisfy (== 2) $> TimeoutResponseMessage) `P.alt`
160− (P.satisfy (== 3) *> pure ExceptionResponseMessage)
166+ (P.satisfy (== 3) $> ExceptionResponseMessage)
161 lenPrefixedByteStringP = do
167 lenPrefixedByteStringP = do
162 c <- fromIntegral <$> word32P
168 c <- fromIntegral <$> word32P
163− BS.pack <$> P.takeEQ c s
169+ --streamly can't handle takeEQ 0, so add special handling
173+ fromArray <$> P.takeEQ c (Arr.writeN c)
164 Envelope <$> fingerprintP <*> msgTypeP <*> uuidP <*> lenPrefixedByteStringP
174 Envelope <$> fingerprintP <*> msgTypeP <*> uuidP <*> lenPrefixedByteStringP
166encodeEnvelope :: Envelope -> BS.ByteString
176encodeEnvelope :: Envelope -> BS.ByteString
@@ -175,15 +185,13 @@ encodeEnvelope (Envelope (Fingerprint fp1 fp2) msgType msgId bs) =
175 ExceptionResponseMessage -> BS.singleton 3
185 ExceptionResponseMessage -> BS.singleton 3
176 msgIdBs =
186 msgIdBs =
177 case UUIDBase.toWords (_unUUID msgId) of
187 case UUIDBase.toWords (_unUUID msgId) of
178− (u1, u2, u3, u4) -> foldr (<>) BS.empty (map BO.bytestring32 [u1, u2, u3, u4])
188+ (u1, u2, u3, u4) -> foldr ((<>) . BO.bytestring32) BS.empty [u1, u2, u3, u4]
179 msgLen = fromIntegral (BS.length bs)
189 msgLen = fromIntegral (BS.length bs)
180 lenPrefixedBs = BO.bytestring32 msgLen <> bs
190 lenPrefixedBs = BO.bytestring32 msgLen <> bs
182fingerprintP :: Parser IO Word8 Fingerprint
192fingerprintP :: Parser IO Word8 Fingerprint
183−fingerprintP = do
193+fingerprintP =
186− pure (Fingerprint f1 f2)
194+ Fingerprint <$> word64P <*> word64P
188word64P :: Parser IO Word8 Word64
196word64P :: Parser IO Word8 Word64
189word64P = do
197word64P = do
@@ -204,8 +212,10 @@ uuidP = do
204 u1 <- word32P
212 u1 <- word32P
205 u2 <- word32P
213 u2 <- word32P
206 u3 <- word32P
214 u3 <- word32P
207− u4 <- word32P
215+ --u4 <- word32P
208− pure (UUID (UUIDBase.fromWords u1 u2 u3 u4))
216+ --pure (UUID (UUIDBase.fromWords u1 u2 u3 u4))-}
217+ --(UUID . UUIDBase.fromWords) <$> word32P <*> word32P <*> word32P <*> word32P
218+ UUID . UUIDBase.fromWords u1 u2 u3 <$> word32P
210type NewConnectionHandler msg = IO (Maybe msg)
220type NewConnectionHandler msg = IO (Maybe msg)
@@ -229,7 +239,7 @@ serve userMsgHandlers serverState hostaddr port mSockLock = do
229 pure True
239 pure True
231openEnvelope :: forall s. (Serialise s, Typeable s) => Envelope -> Maybe s
241openEnvelope :: forall s. (Serialise s, Typeable s) => Envelope -> Maybe s
232−openEnvelope (Envelope eprint _ _ bytes) = do
242+openEnvelope (Envelope eprint _ _ bytes) =
233 if eprint == fingerprint (undefined :: s) then
243 if eprint == fingerprint (undefined :: s) then
234 case deserialise bytes of
244 case deserialise bytes of
235 Left err -> error (show err)
245 Left err -> error (show err)
@@ -261,10 +271,13 @@ serverEnvelopeHandler sockLock msgHandlers serverState envelope@(Envelope _ (Req
261 let runTimeout :: IO b -> IO (Maybe b)
271 let runTimeout :: IO b -> IO (Maybe b)
262 runTimeout m =
272 runTimeout m =
263 if timeoutms == 0 then
273 if timeoutms == 0 then
264− Just <$> m
274+ (Just <$> m) `catch` timeoutExcHandler
266− timeout (fromIntegral timeoutms) m
276+ (timeout (fromIntegral timeoutms) m) `catch` timeoutExcHandler
267−
277+ --allow server-side function to throw TimeoutError which is caught here and becomes TimeoutError value
278+ timeoutExcHandler :: TimeoutException -> IO (Maybe b)
279+ timeoutExcHandler _ = pure Nothing
268 sState = ConnectionState {
281 sState = ConnectionState {
269 connectionServerState = serverState,
282 connectionServerState = serverState,
270 connectionSocket = sockLock
283 connectionSocket = sockLock
@@ -280,8 +293,8 @@ serverEnvelopeHandler sockLock msgHandlers serverState envelope@(Envelope _ (Req
280 case mResponse of
293 case mResponse of
281 Just response ->
294 Just response ->
282 Envelope (fingerprint response) ResponseMessage msgId (serialise response)
295 Envelope (fingerprint response) ResponseMessage msgId (serialise response)
283− Nothing ->
296+ Nothing ->
284− Envelope (fingerprint TimeoutError) (TimeoutResponseMessage) msgId (BS.empty)
297+ Envelope (fingerprint TimeoutError) TimeoutResponseMessage msgId BS.empty
285 sendEnvelope envelopeResponse sockLock
298 sendEnvelope envelopeResponse sockLock
286 pure (Just ())
299 pure (Just ())
287 firstMatcher (AsyncRequestHandler msghandler) Nothing =
300 firstMatcher (AsyncRequestHandler msghandler) Nothing =
@@ -314,8 +327,16 @@ sendEnvelope envelope sockLock = do
314 --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
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
315 withLock sockLock $ \socket' ->
328 withLock sockLock $ \socket' ->
316 Socket.sendAll socket' fullbytes
329 Socket.sendAll socket' fullbytes
330+ --traceShowM ("sendEnvelope", envelope)
317 traceBytes "sendEnvelope" fullbytes
331 traceBytes "sendEnvelope" fullbytes
319fingerprint :: Typeable a => a -> Fingerprint
333fingerprint :: Typeable a => a -> Fingerprint
320fingerprint = typeRepFingerprint . typeOf
334fingerprint = 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