@@ -7,7 +7,7 @@
7module Network.Transport.TCP ( -- * Main API
7module Network.Transport.TCP ( -- * Main API
8 createTransport
8 createTransport
9 , -- TCP specific functionality
9 , -- TCP specific functionality
10− EndPointIx
10+ EndPointId
11 , decodeEndPointAddress
11 , decodeEndPointAddress
@@ -15,12 +15,13 @@ import Network.Transport
15import Network.Transport.Internal.TCP ( forkServer
15import Network.Transport.Internal.TCP ( forkServer
16 , connectTo
16 , connectTo
17 , recvExact
17 , recvExact
19 , recvWithLength
18 , recvWithLength
20 , sendWithLength
19 , sendWithLength
22import Network.Transport.Internal ( encodeInt16
24import Network.Transport.Internal ( encodeInt16
24 , maybeToErrorT
25 , maybeToErrorT
25 , maybeTToErrorT
26 , maybeTToErrorT
@@ -34,7 +35,7 @@ import Control.Concurrent (forkIO)
34import Control.Concurrent.Chan (Chan, newChan, readChan, writeChan)
35import Control.Concurrent.Chan (Chan, newChan, readChan, writeChan)
35import Control.Concurrent.MVar (MVar, newMVar, modifyMVar, readMVar)
36import Control.Concurrent.MVar (MVar, newMVar, modifyMVar, readMVar)
36import Control.Category ((>>>))
37import Control.Category ((>>>))
37−import Control.Applicative ((<*>), (*>), (<$>), pure)
38+import Control.Applicative ((<*>), (*>), (<$>))
38import Control.Monad (forever, forM_)
39import Control.Monad (forever, forM_)
39import Control.Monad.Error (ErrorT, liftIO, runErrorT)
40import Control.Monad.Error (ErrorT, liftIO, runErrorT)
40import Control.Monad.Trans.Maybe (MaybeT(MaybeT), runMaybeT)
41import Control.Monad.Trans.Maybe (MaybeT(MaybeT), runMaybeT)
@@ -43,24 +44,24 @@ import Control.Monad.Writer.Class (tell)
43import Data.ByteString (ByteString)
44import Data.ByteString (ByteString)
44import qualified Data.ByteString as BS (concat)
45import qualified Data.ByteString as BS (concat)
45import qualified Data.ByteString.Char8 as BSC (pack, unpack)
46import qualified Data.ByteString.Char8 as BSC (pack, unpack)
47+import Data.Int (Int16)
46import Data.IntMap (IntMap)
48import Data.IntMap (IntMap)
47import qualified Data.IntMap as IntMap (empty, size)
49import qualified Data.IntMap as IntMap (empty, size)
48import Data.Lens.Lazy (Lens, lens, intMapLens, (^.), (^=), (^+=))
50import Data.Lens.Lazy (Lens, lens, intMapLens, (^.), (^=), (^+=))
49import Text.Regex.Applicative (RE, few, anySym, sym, many, match)
51import Text.Regex.Applicative (RE, few, anySym, sym, many, match)
51data TransportState = TransportState { _endPoints :: IntMap (Chan Event, MVar EndPointState) }
53data TransportState = TransportState { _endPoints :: IntMap (Chan Event, MVar EndPointState) }
52−data EndPointState = EndPointState { _nextConnectionIx :: ConnectionIx }
54+data EndPointState = EndPointState { _nextConnectionId :: ConnectionId }
53−type EndPointIx = Int
55+type EndPointId = Int16
54−type ConnectionIx = Int
56endPoints :: Lens TransportState (IntMap (Chan Event, MVar EndPointState))
57endPoints :: Lens TransportState (IntMap (Chan Event, MVar EndPointState))
57endPoints = lens _endPoints (\es st -> st { _endPoints = es })
58endPoints = lens _endPoints (\es st -> st { _endPoints = es })
59−nextConnectionIx :: Lens EndPointState ConnectionIx
60+nextConnectionId :: Lens EndPointState ConnectionId
60−nextConnectionIx = lens _nextConnectionIx (\cix st -> st { _nextConnectionIx = cix })
61+nextConnectionId = lens _nextConnectionId (\cix st -> st { _nextConnectionId = cix })
62−endPointAt :: EndPointIx -> Lens TransportState (Maybe (Chan Event, MVar EndPointState))
63+endPointAt :: EndPointId -> Lens TransportState (Maybe (Chan Event, MVar EndPointState))
63−endPointAt ix = endPoints >>> intMapLens ix
64+endPointAt ix = endPoints >>> intMapLens (fromIntegral ix)
65-- | Create a TCP transport
66-- | Create a TCP transport
@@ -92,18 +93,14 @@ transportServer state sock = forever $ do
92handleClient :: MVar TransportState -> N.Socket -> IO ()
93handleClient :: MVar TransportState -> N.Socket -> IO ()
93handleClient state sock = do
94handleClient state sock = do
94 mendpoint <- runMaybeT $ do
95 mendpoint <- runMaybeT $ do
95− -- Our endpoint the other side is interested in
96+ ourEndPointId <- recvInt16 sock
96− endPointBS <- recvExact sock 2
97− endPointIx <- decodeInt16 (BS.concat endPointBS)
98− -- Full endpoint address of the other side
99− theirAddress <- BS.concat <$> recvWithLength sock
97+ theirAddress <- BS.concat <$> recvWithLength sock
100− -- Find our endpoint
101− (endPointCh, endPointSt) <- MaybeT $ (^. endPointAt (fromIntegral endPointIx)) <$> readMVar state
98+ ourEndPoint <- MaybeT $ (^. endPointAt (fromIntegral ourEndPointId)) <$> readMVar state
102− return (endPointCh, endPointSt, theirAddress)
99+ return (ourEndPoint, EndPointAddress theirAddress)
103 case mendpoint of
100 case mendpoint of
104− Just (endPointCh, endPointSt, theirAddress) -> do
101+ Just ((endPointCh, endPointSt), theirAddress) -> do
105− openConnections <- endpointServer sock (EndPointAddress theirAddress) endPointCh endPointSt
102+ unclosedConnections <- endpointServer sock theirAddress endPointCh endPointSt
106− forM_ openConnections $ \cix -> writeChan endPointCh (ConnectionClosed cix)
103+ forM_ unclosedConnections $ \cix -> writeChan endPointCh (ConnectionClosed cix)
107 Nothing ->
104 Nothing ->
108 -- Invalid request
105 -- Invalid request
109 return ()
106 return ()
@@ -111,41 +108,39 @@ handleClient state sock = do
111-- | Handle requests for one endpoint. Returns only if an error occurs,
108-- | Handle requests for one endpoint. Returns only if an error occurs,
112-- in which case it returns the connections that are still open (normally,
109-- in which case it returns the connections that are still open (normally,
113-- this should be an empty list).
110-- this should be an empty list).
114−endpointServer :: N.Socket -> EndPointAddress -> Chan Event -> MVar EndPointState -> IO [ConnectionIx]
111+endpointServer :: N.Socket -> EndPointAddress -> Chan Event -> MVar EndPointState -> IO [ConnectionId]
115endpointServer sock theirAddress chan state = execWriterT . runMaybeT . forever $ do
112endpointServer sock theirAddress chan state = execWriterT . runMaybeT . forever $ do
116− connBS <- recvExact sock 2
113+ connId <- recvInt32 sock
117− connIx <- fromIntegral <$> decodeInt16 (BS.concat connBS)
114+ case connId of
119− then createNewConnection
115+ 0 -> createNewConnection
120− else readMessage connIx
116+ _ -> readMessage connId
122 -- A brief note on the types:
118 -- A brief note on the types:
123 -- These functions may fail (connection error): hence MaybeT
119 -- These functions may fail (connection error): hence MaybeT
124 -- We also need to keep track of which connections are still open: hence WriterT
120 -- We also need to keep track of which connections are still open: hence WriterT
126 -- Create a new connection
122 -- Create a new connection
127− createNewConnection :: MaybeT (WriterT [ConnectionIx] IO) ()
123+ createNewConnection :: MaybeT (WriterT [ConnectionId] IO) ()
128 createNewConnection = do
124 createNewConnection = do
129− newIx <- liftIO $ modifyMVar state $ \st -> return (nextConnectionIx ^+= 1 $ st, st ^. nextConnectionIx)
125+ newId <- liftIO $ modifyMVar state $ \st -> return (nextConnectionId ^+= 1 $ st, st ^. nextConnectionId)
130− newBs <- encodeInt16 (fromIntegral newIx)
126+ sendInt32 sock newId
131− sendMany sock [newBs]
132− liftIO $ writeChan chan (ConnectionOpened newIx ReliableOrdered theirAddress)
127+ liftIO $ writeChan chan (ConnectionOpened newId ReliableOrdered theirAddress)
128+ -- We add the new connection ID to the list of open connections only once the
129+ -- endpoint has been notified of the new connection (sendInt32 may fail)
133− tell [newIx]
130+ tell [newId]
135 -- Read a message and output it on the endPoint's channel
132 -- Read a message and output it on the endPoint's channel
136− readMessage :: ConnectionIx -> MaybeT (WriterT [ConnectionIx] IO) ()
133+ readMessage :: ConnectionId -> MaybeT (WriterT [ConnectionId] IO) ()
137− readMessage connIx = do
138− payload <- recvWithLength sock
139− liftIO $ writeChan chan (Received connIx payload)
134+ readMessage connId = recvWithLength sock >>= liftIO . writeChan chan . Received connId
141-- | Create a new endpoint
136-- | Create a new endpoint
142tcpNewEndPoint :: MVar TransportState -> N.HostName -> N.ServiceName -> IO (Either (FailedWith NewEndPointErrorCode) EndPoint)
137tcpNewEndPoint :: MVar TransportState -> N.HostName -> N.ServiceName -> IO (Either (FailedWith NewEndPointErrorCode) EndPoint)
143tcpNewEndPoint transportState host port = do
138tcpNewEndPoint transportState host port = do
144 chan <- newChan
139 chan <- newChan
145− endPointState <- newMVar EndPointState { _nextConnectionIx = 1 } -- Connection id 0 is reserved for requesting new connections
140+ endPointState <- newMVar EndPointState { _nextConnectionId = firstNonReservedConnectionId }
146− endPointIx <- modifyMVar transportState $ \st -> do let ix = IntMap.size (st ^. endPoints)
141+ endPointId <- modifyMVar transportState $ \st -> do let ix = fromIntegral $ IntMap.size (st ^. endPoints)
147 return ((endPointAt ix ^= Just (chan, endPointState)) $ st, ix)
142 return ((endPointAt ix ^= Just (chan, endPointState)) $ st, ix)
148− let addr = EndPointAddress . BSC.pack $ host ++ ":" ++ port ++ ":" ++ show endPointIx
143+ let addr = EndPointAddress . BSC.pack $ host ++ ":" ++ port ++ ":" ++ show endPointId
149 return . Right $ EndPoint { receive = readChan chan
144 return . Right $ EndPoint { receive = readChan chan
150 , address = addr
145 , address = addr
151 , connect = tcpConnect addr
146 , connect = tcpConnect addr
@@ -155,24 +150,26 @@ tcpNewEndPoint transportState host port = do
156 newMulticastGroupError = FailedWith NewMulticastGroupUnsupported "TCP does not support multicast"
151 newMulticastGroupError = FailedWith NewMulticastGroupUnsupported "TCP does not support multicast"
157 resolveMulticastGroupError = FailedWith ResolveMulticastGroupUnsupported "TCP does not support multicast"
152 resolveMulticastGroupError = FailedWith ResolveMulticastGroupUnsupported "TCP does not support multicast"
154+-- | We reserve a bunch of connection IDs for control messages
155+firstNonReservedConnectionId :: ConnectionId
156+firstNonReservedConnectionId = 1024
160-- | Connnect to an endpoint
158-- | Connnect to an endpoint
161tcpConnect :: EndPointAddress -> EndPointAddress -> Reliability -> IO (Either (FailedWith ConnectErrorCode) Connection)
159tcpConnect :: EndPointAddress -> EndPointAddress -> Reliability -> IO (Either (FailedWith ConnectErrorCode) Connection)
162tcpConnect myAddress theirAddress _ = runErrorT $ do
160tcpConnect myAddress theirAddress _ = runErrorT $ do
163 sock <- socketForEndPoint myAddress theirAddress
161 sock <- socketForEndPoint myAddress theirAddress
164− connIx <- maybeTToErrorT undefined $ requestNewConnection sock
162+ connId <- maybeTToErrorT undefined $ requestNewConnection sock
165 return Connection { send = runErrorT .
163 return Connection { send = runErrorT .
166 maybeTToErrorT (FailedWith SendFailed "Send failed") .
164 maybeTToErrorT (FailedWith SendFailed "Send failed") .
167− sendWithLength sock (Just connIx)
165+ sendWithLength sock (Just connId)
168 , close = N.sClose sock
166 , close = N.sClose sock
171-- | Request a new connection
169-- | Request a new connection
170+-- Returns the new connection ID in serialized form
172requestNewConnection :: N.Socket -> MaybeT IO ByteString
171requestNewConnection :: N.Socket -> MaybeT IO ByteString
173−requestNewConnection sock = do
172+requestNewConnection sock = sendInt32 sock 0 >> BS.concat <$> recvExact sock 4
174− liftIO (pure <$> encodeInt16 0) >>= sendMany sock
175− BS.concat <$> recvExact sock 2
177-- | Find a socket to the specified endpoint
174-- | Find a socket to the specified endpoint
@@ -183,9 +180,9 @@ socketForEndPoint :: EndPointAddress -- ^ Our address
183 -> EndPointAddress -- ^ Their address
180 -> EndPointAddress -- ^ Their address
184 -> ErrorT (FailedWith ConnectErrorCode) IO N.Socket
181 -> ErrorT (FailedWith ConnectErrorCode) IO N.Socket
185socketForEndPoint (EndPointAddress myAddress) theirAddress = do
182socketForEndPoint (EndPointAddress myAddress) theirAddress = do
186− (host, port, endPointIx) <- maybeToErrorT invalidAddress (decodeEndPointAddress theirAddress)
183+ (host, port, endPointId) <- maybeToErrorT invalidAddress (decodeEndPointAddress theirAddress)
187 sock <- maybeTToErrorT undefined (connectTo host port)
184 sock <- maybeTToErrorT undefined (connectTo host port)
188− endPointBS <- liftIO $ encodeInt16 (fromIntegral endPointIx)
185+ endPointBS <- encodeInt16 (fromIntegral endPointId)
189 maybeTToErrorT undefined $ sendWithLength sock (Just endPointBS) [myAddress]
186 maybeTToErrorT undefined $ sendWithLength sock (Just endPointBS) [myAddress]
190 return sock
187 return sock
@@ -193,8 +190,8 @@ socketForEndPoint (EndPointAddress myAddress) theirAddress = do
194-- | Decode end point address
191-- | Decode end point address
195-- TODO: This uses regular expression parsing, which is nice, but unnecessary
192-- TODO: This uses regular expression parsing, which is nice, but unnecessary
196−decodeEndPointAddress :: EndPointAddress -> Maybe (N.HostName, N.ServiceName, EndPointIx)
193+decodeEndPointAddress :: EndPointAddress -> Maybe (N.HostName, N.ServiceName, EndPointId)
197decodeEndPointAddress (EndPointAddress bs) = match endPointAddressRE (BSC.unpack bs)
194decodeEndPointAddress (EndPointAddress bs) = match endPointAddressRE (BSC.unpack bs)
199− endPointAddressRE :: RE Char (N.HostName, N.ServiceName, EndPointIx)
196+ endPointAddressRE :: RE Char (N.HostName, N.ServiceName, EndPointId)
200 endPointAddressRE = (,,) <$> few anySym <*> (sym ':' *> few anySym) <*> (sym ':' *> (read <$> many anySym))
197 endPointAddressRE = (,,) <$> few anySym <*> (sym ':' *> few anySym) <*> (sym ':' *> (read <$> many anySym))