@@ -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 #-}
22{-# OPTIONS_GHC -fno-warn-orphans #-}
33{- HLINT ignore "Use lambda-case" -}
44module Network.RPC.Curryer.Server where
@@ -27,6 +27,7 @@ import qualified Data.Binary as B
2727import qualified Data.UUID as UUIDBase
2828import qualified Data.UUID.V4 as UUIDBase
2929import Control.Monad
30+import Data.Functor
3031
3132import qualified Network.RPC.Curryer.StreamlyAdditions as SA
3233--import Control.Monad
@@ -34,14 +35,14 @@ import Data.Hashable
3435import System.Timeout
3536import qualified Network.ByteOrder as BO
3637
38+
3739-- for toArrayS conversion
38−{-import qualified Data.ByteString.Internal as BSI
39−import qualified Streamly.Internal.Data.Array.Storable.Foreign.Types as SA
40−import Foreign.ForeignPtr (plusForeignPtr)
40+import qualified Data.ByteString.Internal as BSI
41+import qualified Streamly.Internal.Data.Array.Storable.Foreign.Types as Arr
4142import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
42−import GHC.Ptr (plusPtr)
43−-}
44−-- #define CURRYER_SHOW_BYTES 1
43+import GHC.Ptr (minusPtr)
44+
45+-- define CURRYER_SHOW_BYTES 1
4546#if CURRYER_SHOW_BYTES == 1
4647import Debug.Trace
4748#endif
@@ -97,7 +98,7 @@ data MessageType = RequestMessage TimeoutMicroseconds
9798-- | A list of `RequestHandler`s.
9899type RequestHandlers serverState = [RequestHandler serverState]
99100
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.
101102data RequestHandler serverState where
102103 -- | create a request handler with a response
103104 RequestHandler :: forall a b serverState. (Serialise a, Serialise b) => (ConnectionState serverState -> a -> IO b) -> RequestHandler serverState
@@ -110,13 +111,14 @@ data ConnectionState a = ConnectionState {
110111 connectionSocket :: Locking Socket
111112 }
112113
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−sendMessage :: Serialise a => ConnectionState s -> a -> IO ()
115−sendMessage sState msg = do
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
116117 requestID <- UUID <$> UUIDBase.nextRandom
117118 let env =
118− Envelope (fingerprint msg) (RequestMessage 0) requestID (serialise msg)
119− sendEnvelope env (connectionSocket sState)
119+ Envelope (fingerprint msg) (RequestMessage timeout') requestID (serialise msg)
120+ timeout' = 0
121+ sendEnvelope env lockSock
120122
121123--avoid orphan instance
122124newtype UUID = UUID { _unUUID :: UUIDBase.UUID }
@@ -143,6 +145,11 @@ data ConnectionError = CodecError String -- show of WineryException from excepti
143145 deriving (Generic, Show, Eq)
144146 deriving Serialise via WineryVariant ConnectionError
145147
148+data TimeoutException = TimeoutException
149+ deriving Show
150+
151+instance Exception TimeoutException
152+
146153type HostAddr = (Word8, Word8, Word8, Word8)
147154
148155allHostAddrs,localHostAddr :: HostAddr
@@ -152,15 +159,18 @@ localHostAddr = (127,0,0,1)
152159-- Each message is length-prefixed by a 32-bit unsigned length.
153160envelopeP :: Parser IO Word8 Envelope
154161envelopeP = do
155− let s = FL.toList
156− msgTypeP = (P.satisfy (== 0) *>
162+ let msgTypeP = (P.satisfy (== 0) *>
157163 (RequestMessage . fromIntegral <$> word32P)) `P.alt`
158− (P.satisfy (== 1) *> pure ResponseMessage) `P.alt`
159− (P.satisfy (== 2) *> pure TimeoutResponseMessage) `P.alt`
160− (P.satisfy (== 3) *> pure ExceptionResponseMessage)
164+ (P.satisfy (== 1) $> ResponseMessage) `P.alt`
165+ (P.satisfy (== 2) $> TimeoutResponseMessage) `P.alt`
166+ (P.satisfy (== 3) $> ExceptionResponseMessage)
161167 lenPrefixedByteStringP = do
162168 c <- fromIntegral <$> word32P
163− BS.pack <$> P.takeEQ c s
169+ --streamly can't handle takeEQ 0, so add special handling
170+ if c == 0 then
171+ pure BS.empty
172+ else
173+ fromArray <$> P.takeEQ c (Arr.writeN c)
164174 Envelope <$> fingerprintP <*> msgTypeP <*> uuidP <*> lenPrefixedByteStringP
165175
166176encodeEnvelope :: Envelope -> BS.ByteString
@@ -175,15 +185,13 @@ encodeEnvelope (Envelope (Fingerprint fp1 fp2) msgType msgId bs) =
175185 ExceptionResponseMessage -> BS.singleton 3
176186 msgIdBs =
177187 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]
179189 msgLen = fromIntegral (BS.length bs)
180190 lenPrefixedBs = BO.bytestring32 msgLen <> bs
181191
182192fingerprintP :: Parser IO Word8 Fingerprint
183−fingerprintP = do
184− f1 <- word64P
185− f2 <- word64P
186− pure (Fingerprint f1 f2)
193+fingerprintP =
194+ Fingerprint <$> word64P <*> word64P
187195
188196word64P :: Parser IO Word8 Word64
189197word64P = do
@@ -204,8 +212,10 @@ uuidP = do
204212 u1 <- word32P
205213 u2 <- word32P
206214 u3 <- word32P
207− u4 <- word32P
208− pure (UUID (UUIDBase.fromWords u1 u2 u3 u4))
215+ --u4 <- word32P
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
209219
210220type NewConnectionHandler msg = IO (Maybe msg)
211221
@@ -229,7 +239,7 @@ serve userMsgHandlers serverState hostaddr port mSockLock = do
229239 pure True
230240
231241openEnvelope :: forall s. (Serialise s, Typeable s) => Envelope -> Maybe s
232−openEnvelope (Envelope eprint _ _ bytes) = do
242+openEnvelope (Envelope eprint _ _ bytes) =
233243 if eprint == fingerprint (undefined :: s) then
234244 case deserialise bytes of
235245 Left err -> error (show err)
@@ -261,10 +271,13 @@ serverEnvelopeHandler sockLock msgHandlers serverState envelope@(Envelope _ (Req
261271 let runTimeout :: IO b -> IO (Maybe b)
262272 runTimeout m =
263273 if timeoutms == 0 then
264− Just <$> m
274+ (Just <$> m) `catch` timeoutExcHandler
265275 else
266− timeout (fromIntegral timeoutms) m
267−
276+ (timeout (fromIntegral timeoutms) m) `catch` timeoutExcHandler
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
280+
268281 sState = ConnectionState {
269282 connectionServerState = serverState,
270283 connectionSocket = sockLock
@@ -280,8 +293,8 @@ serverEnvelopeHandler sockLock msgHandlers serverState envelope@(Envelope _ (Req
280293 case mResponse of
281294 Just response ->
282295 Envelope (fingerprint response) ResponseMessage msgId (serialise response)
283− Nothing ->
284− Envelope (fingerprint TimeoutError) (TimeoutResponseMessage) msgId (BS.empty)
296+ Nothing ->
297+ Envelope (fingerprint TimeoutError) TimeoutResponseMessage msgId BS.empty
285298 sendEnvelope envelopeResponse sockLock
286299 pure (Just ())
287300 firstMatcher (AsyncRequestHandler msghandler) Nothing =
@@ -314,8 +327,16 @@ sendEnvelope envelope sockLock = do
314327 --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
315328 withLock sockLock $ \socket' ->
316329 Socket.sendAll socket' fullbytes
330+ --traceShowM ("sendEnvelope", envelope)
317331 traceBytes "sendEnvelope" fullbytes
318332
319333fingerprint :: Typeable a => a -> Fingerprint
320334fingerprint = typeRepFingerprint . typeOf
321335
336+fromArray :: Arr.Array Word8 -> BSI.ByteString
337+fromArray Arr.Array {..}
338+ | aLen == 0 = mempty
339+ | otherwise = BSI.PS aStart 0 aLen
340+ where
341+ aStartPtr = unsafeForeignPtrToPtr aStart
342+ aLen = aEnd `minusPtr` aStartPtr