@@ -35,9 +35,11 @@ import Control.Concurrent.Chan (Chan, newChan, readChan, writeChan)
35import Control.Concurrent.MVar (MVar, newMVar, modifyMVar, readMVar)
35import Control.Concurrent.MVar (MVar, newMVar, modifyMVar, readMVar)
36import Control.Category ((>>>))
36import Control.Category ((>>>))
37import Control.Applicative ((<*>), (*>), (<$>), pure)
37import Control.Applicative ((<*>), (*>), (<$>), pure)
38−import Control.Monad (forever)
38+import Control.Monad (forever, forM_)
39import Control.Monad.Error (ErrorT, liftIO, runErrorT)
39import Control.Monad.Error (ErrorT, liftIO, runErrorT)
40import Control.Monad.Trans.Maybe (MaybeT(MaybeT), runMaybeT)
40import Control.Monad.Trans.Maybe (MaybeT(MaybeT), runMaybeT)
41+import Control.Monad.Trans.Writer (WriterT, execWriterT)
42+import Control.Monad.Writer.Class (tell)
41import Data.ByteString (ByteString)
43import Data.ByteString (ByteString)
42import qualified Data.ByteString as BS (concat)
44import qualified Data.ByteString as BS (concat)
43import qualified Data.ByteString.Char8 as BSC (pack, unpack)
45import qualified Data.ByteString.Char8 as BSC (pack, unpack)
@@ -72,7 +74,7 @@ endPointAt ix = endPoints >>> intMapLens ix
72-- * Deal with all exceptions that may occur
74-- * Deal with all exceptions that may occur
73createTransport :: N.HostName -> N.ServiceName -> IO Transport
75createTransport :: N.HostName -> N.ServiceName -> IO Transport
74createTransport host port = do
76createTransport host port = do
75− state <- newMVar $ TransportState { _endPoints = IntMap.empty }
77+ state <- newMVar TransportState { _endPoints = IntMap.empty }
76 forkServer host port (transportServer state)
78 forkServer host port (transportServer state)
77 return Transport { newEndPoint = tcpNewEndPoint state host port }
79 return Transport { newEndPoint = tcpNewEndPoint state host port }
@@ -89,38 +91,49 @@ transportServer state sock = forever $ do
89-- Returns when the client closes the socket and on invalid input.
91-- Returns when the client closes the socket and on invalid input.
90handleClient :: MVar TransportState -> N.Socket -> IO ()
92handleClient :: MVar TransportState -> N.Socket -> IO ()
91handleClient state sock = do
93handleClient state sock = do
92− runMaybeT $ do
94+ mendpoint <- runMaybeT $ do
93 -- Our endpoint the other side is interested in
95 -- Our endpoint the other side is interested in
94 endPointBS <- recvExact sock 2
96 endPointBS <- recvExact sock 2
95− endPointIx <- decodeInt16 (BS.concat $ endPointBS)
97+ endPointIx <- decodeInt16 (BS.concat endPointBS)
96 -- Full endpoint address of the other side
98 -- Full endpoint address of the other side
97 theirAddress <- BS.concat <$> recvWithLength sock
99 theirAddress <- BS.concat <$> recvWithLength sock
98− -- Start handling incoming messages
100+ -- Find our endpoint
99 (endPointCh, endPointSt) <- MaybeT $ (^. endPointAt (fromIntegral endPointIx)) <$> readMVar state
101 (endPointCh, endPointSt) <- MaybeT $ (^. endPointAt (fromIntegral endPointIx)) <$> readMVar state
102+ return (endPointCh, endPointSt, theirAddress)
104+ Just (endPointCh, endPointSt, theirAddress) -> do
100− endpointServer sock (EndPointAddress theirAddress) endPointCh endPointSt
105+ openConnections <- endpointServer sock (EndPointAddress theirAddress) endPointCh endPointSt
106+ forM_ openConnections $ \cix -> writeChan endPointCh (ConnectionClosed cix)
101− return ()
109+ return ()
103−-- | Handle requests for one endpoint. Returns only if an error occurs
111+-- | Handle requests for one endpoint. Returns only if an error occurs,
112+-- in which case it returns the connections that are still open (normally,
113+-- this should be an empty list).
104−endpointServer :: N.Socket -> EndPointAddress -> Chan Event -> MVar EndPointState -> MaybeT IO ()
114+endpointServer :: N.Socket -> EndPointAddress -> Chan Event -> MVar EndPointState -> IO [ConnectionIx]
105−endpointServer sock theirAddress chan state = forever $ do
115+endpointServer sock theirAddress chan state = execWriterT . runMaybeT . forever $ do
106 connBS <- recvExact sock 2
116 connBS <- recvExact sock 2
107 connIx <- fromIntegral <$> decodeInt16 (BS.concat connBS)
117 connIx <- fromIntegral <$> decodeInt16 (BS.concat connBS)
108 if connIx == 0
118 if connIx == 0
109 then createNewConnection
119 then createNewConnection
110 else readMessage connIx
120 else readMessage connIx
122+ -- A brief note on the types:
123+ -- These functions may fail (connection error): hence MaybeT
124+ -- We also need to keep track of which connections are still open: hence WriterT
112 -- Create a new connection
126 -- Create a new connection
113− createNewConnection :: MaybeT IO ()
127+ createNewConnection :: MaybeT (WriterT [ConnectionIx] IO) ()
114 createNewConnection = do
128 createNewConnection = do
115− (newIx, newBs) <- liftIO $ do
116− newIx <- modifyMVar state $ \st -> return (nextConnectionIx ^+= 1 $ st, st ^. nextConnectionIx)
129+ newIx <- liftIO $ modifyMVar state $ \st -> return (nextConnectionIx ^+= 1 $ st, st ^. nextConnectionIx)
117− newBs <- encodeInt16 (fromIntegral newIx)
130+ newBs <- encodeInt16 (fromIntegral newIx)
118− return (newIx, newBs)
119 sendMany sock [newBs]
131 sendMany sock [newBs]
120 liftIO $ writeChan chan (ConnectionOpened newIx ReliableOrdered theirAddress)
132 liftIO $ writeChan chan (ConnectionOpened newIx ReliableOrdered theirAddress)
122 -- Read a message and output it on the endPoint's channel
135 -- Read a message and output it on the endPoint's channel
123− readMessage :: ConnectionIx -> MaybeT IO ()
136+ readMessage :: ConnectionIx -> MaybeT (WriterT [ConnectionIx] IO) ()
124 readMessage connIx = do
137 readMessage connIx = do
125 payload <- recvWithLength sock
138 payload <- recvWithLength sock
126 liftIO $ writeChan chan (Received connIx payload)
139 liftIO $ writeChan chan (Received connIx payload)
@@ -129,7 +142,7 @@ endpointServer sock theirAddress chan state = forever $ do
129tcpNewEndPoint :: MVar TransportState -> N.HostName -> N.ServiceName -> IO (Either (FailedWith NewEndPointErrorCode) EndPoint)
142tcpNewEndPoint :: MVar TransportState -> N.HostName -> N.ServiceName -> IO (Either (FailedWith NewEndPointErrorCode) EndPoint)
130tcpNewEndPoint transportState host port = do
143tcpNewEndPoint transportState host port = do
131 chan <- newChan
144 chan <- newChan
132− endPointState <- newMVar $ EndPointState { _nextConnectionIx = 1 } -- Connection id 0 is reserved for requesting new connections
145+ endPointState <- newMVar EndPointState { _nextConnectionIx = 1 } -- Connection id 0 is reserved for requesting new connections
133 endPointIx <- modifyMVar transportState $ \st -> do let ix = IntMap.size (st ^. endPoints)
146 endPointIx <- modifyMVar transportState $ \st -> do let ix = IntMap.size (st ^. endPoints)
134 return ((endPointAt ix ^= Just (chan, endPointState)) $ st, ix)
147 return ((endPointAt ix ^= Just (chan, endPointState)) $ st, ix)
135 let addr = EndPointAddress . BSC.pack $ host ++ ":" ++ port ++ ":" ++ show endPointIx
148 let addr = EndPointAddress . BSC.pack $ host ++ ":" ++ port ++ ":" ++ show endPointIx
@@ -149,15 +162,16 @@ tcpConnect :: EndPointAddress -> EndPointAddress -> Reliability -> IO (Either (F
149tcpConnect myAddress theirAddress _ = runErrorT $ do
162tcpConnect myAddress theirAddress _ = runErrorT $ do
150 sock <- socketForEndPoint myAddress theirAddress
163 sock <- socketForEndPoint myAddress theirAddress
151 connIx <- maybeTToErrorT undefined $ requestNewConnection sock
164 connIx <- maybeTToErrorT undefined $ requestNewConnection sock
165+ return Connection { send = runErrorT .
152− return $ Connection { send = \msg -> runErrorT $ maybeTToErrorT (FailedWith SendFailed "Send failed") $
166+ maybeTToErrorT (FailedWith SendFailed "Send failed") .
153− sendWithLength sock (Just connIx) msg
167+ sendWithLength sock (Just connIx)
154− , close = N.sClose sock
168+ , close = N.sClose sock
157-- | Request a new connection
171-- | Request a new connection
158requestNewConnection :: N.Socket -> MaybeT IO ByteString
172requestNewConnection :: N.Socket -> MaybeT IO ByteString
159requestNewConnection sock = do
173requestNewConnection sock = do
160− (liftIO $ pure <$> encodeInt16 0) >>= sendMany sock
174+ liftIO (pure <$> encodeInt16 0) >>= sendMany sock
161 BS.concat <$> recvExact sock 2
175 BS.concat <$> recvExact sock 2
163-- | Find a socket to the specified endpoint
177-- | Find a socket to the specified endpoint