@@ -1,4 +1,4 @@
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, GADTs #-}
22{-# OPTIONS_GHC -fno-warn-orphans #-}
33{- HLINT ignore "Use lambda-case" -}
44module Network.RPC.Curryer.Server where
@@ -27,7 +27,6 @@ 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
3130
3231import qualified Network.RPC.Curryer.StreamlyAdditions as SA
3332--import Control.Monad
@@ -35,14 +34,14 @@ import Data.Hashable
3534import System.Timeout
3635import qualified Network.ByteOrder as BO
3736
38−
3937-- for toArrayS conversion
40−import qualified Data.ByteString.Internal as BSI
41−import qualified Streamly.Internal.Data.Array.Storable.Foreign.Types as Arr
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)
4241import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
43−import GHC.Ptr (minusPtr)
44−
45−-- define CURRYER_SHOW_BYTES 1
42+import GHC.Ptr (plusPtr)
43+-}
44+-- #define CURRYER_SHOW_BYTES 1
4645#if CURRYER_SHOW_BYTES == 1
4746import Debug.Trace
4847#endif
@@ -87,7 +86,6 @@ type TimeoutMicroseconds = Int
8786deriving instance Generic Fingerprint
8887deriving via WineryVariant Fingerprint instance Serialise Fingerprint
8988
90−-- | Internal type used to mark envelope types.
9189data MessageType = RequestMessage TimeoutMicroseconds
9290 | ResponseMessage
9391 | TimeoutResponseMessage
@@ -95,30 +93,27 @@ data MessageType = RequestMessage TimeoutMicroseconds
9593 deriving (Generic, Show)
9694 deriving Serialise via WineryVariant MessageType
9795
98−-- | A list of `RequestHandler`s.
99−type 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.
96+type MessageHandlers serverState = [RequestHandler serverState]
97+
10298data RequestHandler serverState where
10399 -- | create a request handler with a response
104100 RequestHandler :: forall a b serverState. (Serialise a, Serialise b) => (ConnectionState serverState -> a -> IO b) -> RequestHandler serverState
105101 -- | create an asynchronous request handler where the client does not expect nor await a response
106102 AsyncRequestHandler :: forall a serverState. Serialise a => (ConnectionState serverState -> a -> IO ()) -> RequestHandler serverState
107103
108−-- | Server state sent in via `serve` and passed to `RequestHandler`s.
104+-- | Passed to
109105data ConnectionState a = ConnectionState {
110106 connectionServerState :: a,
111107 connectionSocket :: Locking Socket
112108 }
113109
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
110+-- | 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.
111+sendMessage :: Serialise a => ConnectionState s -> a -> IO ()
112+sendMessage sState msg = do
117113 requestID <- UUID <$> UUIDBase.nextRandom
118114 let env =
119− Envelope (fingerprint msg) (RequestMessage timeout') requestID (serialise msg)
120− timeout' = 0
121− sendEnvelope env lockSock
115+ Envelope (fingerprint msg) (RequestMessage 0) requestID (serialise msg)
116+ sendEnvelope env (connectionSocket sState)
122117
123118--avoid orphan instance
124119newtype UUID = UUID { _unUUID :: UUIDBase.UUID }
@@ -138,39 +133,31 @@ instance Serialise UUID where
138133 x -> error $ "invalid schema element " <> show x
139134 decodeCurrent = B.decode . BSL.fromStrict <$> (decodeVarInt >>= getBytes)
140135
141−-- | Errors from remote calls.
136+
142137data ConnectionError = CodecError String -- show of WineryException from exception initiator which cannot otherwise be transmitted over a line due to dependencies on TypeReps
143138 | TimeoutError
144139 | ExceptionError String
145140 deriving (Generic, Show, Eq)
146141 deriving Serialise via WineryVariant ConnectionError
147142
148−data TimeoutException = TimeoutException
149− deriving Show
150−
151−instance Exception TimeoutException
152−
153143type HostAddr = (Word8, Word8, Word8, Word8)
154144
155145allHostAddrs,localHostAddr :: HostAddr
156146allHostAddrs = (0,0,0,0)
157147localHostAddr = (127,0,0,1)
158148
159−-- Each message is length-prefixed by a 32-bit unsigned length.
149+-- Each message is lxggength-prefixed by a 32-bit unsigned length.
160150envelopeP :: Parser IO Word8 Envelope
161151envelopeP = do
162− let msgTypeP = (P.satisfy (== 0) *>
152+ let s = FL.toList
153+ msgTypeP = (P.satisfy (== 0) *>
163154 (RequestMessage . fromIntegral <$> word32P)) `P.alt`
164− (P.satisfy (== 1) $> ResponseMessage) `P.alt`
165− (P.satisfy (== 2) $> TimeoutResponseMessage) `P.alt`
166− (P.satisfy (== 3) $> ExceptionResponseMessage)
155+ (P.satisfy (== 1) *> pure ResponseMessage) `P.alt`
156+ (P.satisfy (== 2) *> pure TimeoutResponseMessage) `P.alt`
157+ (P.satisfy (== 3) *> pure ExceptionResponseMessage)
167158 lenPrefixedByteStringP = do
168159 c <- fromIntegral <$> word32P
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)
160+ BS.pack <$> P.takeEQ c s
174161 Envelope <$> fingerprintP <*> msgTypeP <*> uuidP <*> lenPrefixedByteStringP
175162
176163encodeEnvelope :: Envelope -> BS.ByteString
@@ -185,13 +172,15 @@ encodeEnvelope (Envelope (Fingerprint fp1 fp2) msgType msgId bs) =
185172 ExceptionResponseMessage -> BS.singleton 3
186173 msgIdBs =
187174 case UUIDBase.toWords (_unUUID msgId) of
188− (u1, u2, u3, u4) -> foldr ((<>) . BO.bytestring32) BS.empty [u1, u2, u3, u4]
175+ (u1, u2, u3, u4) -> foldr (<>) BS.empty (map BO.bytestring32 [u1, u2, u3, u4])
189176 msgLen = fromIntegral (BS.length bs)
190177 lenPrefixedBs = BO.bytestring32 msgLen <> bs
191178
192179fingerprintP :: Parser IO Word8 Fingerprint
193−fingerprintP =
194− Fingerprint <$> word64P <*> word64P
180+fingerprintP = do
181+ f1 <- word64P
182+ f2 <- word64P
183+ pure (Fingerprint f1 f2)
195184
196185word64P :: Parser IO Word8 Word64
197186word64P = do
@@ -212,18 +201,15 @@ uuidP = do
212201 u1 <- word32P
213202 u2 <- word32P
214203 u3 <- word32P
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
204+ u4 <- word32P
205+ pure (UUID (UUIDBase.fromWords u1 u2 u3 u4))
219206
220207type NewConnectionHandler msg = IO (Maybe msg)
221208
222209type NewMessageHandler req resp = req -> IO resp
223−
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.
210+
225211serve ::
226− RequestHandlers s->
212+ MessageHandlers s->
227213 s ->
228214 HostAddr ->
229215 PortNumber ->
@@ -239,7 +225,7 @@ serve userMsgHandlers serverState hostaddr port mSockLock = do
239225 pure True
240226
241227openEnvelope :: forall s. (Serialise s, Typeable s) => Envelope -> Maybe s
242−openEnvelope (Envelope eprint _ _ bytes) =
228+openEnvelope (Envelope eprint _ _ bytes) = do
243229 if eprint == fingerprint (undefined :: s) then
244230 case deserialise bytes of
245231 Left err -> error (show err)
@@ -256,28 +242,23 @@ matchEnvelope envelope dispatchf =
256242 Nothing -> Nothing
257243 Just decoded -> Just (dispatchf, decoded)
258244
259−-- | Called by `serve` to process incoming envelope requests. Never returns, so use `async` to spin it off on another thread.
260245serverEnvelopeHandler ::
261246 Locking Socket
262− -> RequestHandlers s
247+ -> MessageHandlers s
263248 -> s
264249 -> Envelope
265250 -> IO ()
266251serverEnvelopeHandler _ _ _ (Envelope _ TimeoutResponseMessage _ _) = pure ()
267252serverEnvelopeHandler _ _ _ (Envelope _ ExceptionResponseMessage _ _) = pure ()
268−serverEnvelopeHandler _ _ _ (Envelope _ ResponseMessage _ _) = pure ()
269253serverEnvelopeHandler sockLock msgHandlers serverState envelope@(Envelope _ (RequestMessage timeoutms) msgId _) = do
270254 --find first matching handler
271255 let runTimeout :: IO b -> IO (Maybe b)
272256 runTimeout m =
273257 if timeoutms == 0 then
274− (Just <$> m) `catch` timeoutExcHandler
258+ Just <$> m
275259 else
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−
260+ timeout (fromIntegral timeoutms) m
261+
281262 sState = ConnectionState {
282263 connectionServerState = serverState,
283264 connectionSocket = sockLock
@@ -293,8 +274,8 @@ serverEnvelopeHandler sockLock msgHandlers serverState envelope@(Envelope _ (Req
293274 case mResponse of
294275 Just response ->
295276 Envelope (fingerprint response) ResponseMessage msgId (serialise response)
296− Nothing ->
297− Envelope (fingerprint TimeoutError) TimeoutResponseMessage msgId BS.empty
277+ Nothing ->
278+ Envelope (fingerprint TimeoutError) (TimeoutResponseMessage) msgId (BS.empty)
298279 sendEnvelope envelopeResponse sockLock
299280 pure (Just ())
300281 firstMatcher (AsyncRequestHandler msghandler) Nothing =
@@ -310,10 +291,12 @@ serverEnvelopeHandler sockLock msgHandlers serverState envelope@(Envelope _ (Req
310291 let env = Envelope (fingerprint (show exc)) ExceptionResponseMessage msgId (serialise (show exc)) in
311292 sendEnvelope env sockLock
312293 Right () -> pure ()
313−
294+serverEnvelopeHandler _ _ _ (Envelope _ ResponseMessage _ _) = error "server received response message"
314295
315296type EnvelopeHandler = Envelope -> IO ()
316297
298+type AsyncMessageHandler a = a -> IO ()
299+
317300drainSocketMessages :: Socket -> EnvelopeHandler -> IO ()
318301drainSocketMessages sock envelopeHandler = do
319302 let sockStream = S.unfold readWithBufferOf (1024 * 4, sock)
@@ -327,16 +310,8 @@ sendEnvelope envelope sockLock = do
327310 --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
328311 withLock sockLock $ \socket' ->
329312 Socket.sendAll socket' fullbytes
330− --traceShowM ("sendEnvelope", envelope)
331313 traceBytes "sendEnvelope" fullbytes
332314
333315fingerprint :: Typeable a => a -> Fingerprint
334316fingerprint = typeRepFingerprint . typeOf
335317
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