@@ -9,6 +9,7 @@ module Network.Transport.TCP ( -- * Main API
99 , -- TCP specific functionality
1010 EndPointId
1111 , decodeEndPointAddress
12+ , ControlHeader(..)
1213 ) where
1314
1415import Network.Transport
@@ -22,6 +23,7 @@ import Network.Transport.Internal ( encodeInt32
2223 , prependLength
2324 , failWith
2425 , failWithIO
26+ , failWithMaybeIO
2527 )
2628import qualified Network.Socket as N ( HostName
2729 , ServiceName
@@ -104,12 +106,12 @@ createTransport host port = do
104106 state <- newMVar TransportState { _localEndPoints = IntMap.empty
105107 , _remoteEndPoints = Map.empty
106108 }
107− let transport = TCPTransport { _transportState = state
108− , _transportHost = host
109− , _transportPort = port
110− }
109+ let transport = TCPTransport { _transportState = state
110+ , _transportHost = host
111+ , _transportPort = port
112+ }
111113 runErrorT $ do
112− forkServer host port (transportServer transport)
114+ failWithIO id $ forkServer host port (transportServer transport)
113115 return Transport { newEndPoint = tcpNewEndPoint transport }
114116
115117-- | The transport server handles incoming connections for all endpoints
@@ -129,13 +131,16 @@ handleConnectionRequest transport sock = do
129131 ourEndPoint <- MaybeT $ (^. localEndPointAt ourEndPointId) <$> readMVar (transport ^. transportState)
130132 return (ourEndPoint, theirAddress)
131133 case request of
132− Just (ourEndPoint, theirAddress) ->
134+ Just (ourEndPoint, theirAddress) -> do
135+ -- Send acknowledgement
136+ sendMany sock [encodeInt32 ValidEndPoint]
133137 -- We ignore the return value of 'forkRemoteEndPoint' here because _they_ are
134138 -- connecting to _us_. 'forkRemoteEndPoint' will have stored the remote endpoint
135139 -- in the transport state, however, in case _we_ later connect to _them_.
136140 void $ forkRemoteEndPoint transport ourEndPoint theirAddress sock
137141 Nothing ->
138− return ()
142+ -- Invalid request
143+ N.sClose sock
139144
140145-- | Set up a new remote endpoint (either because we connected to a
141146-- new remote endpoint, or because a remote endpoint connected to us)
@@ -159,8 +164,9 @@ forkRemoteEndPoint transport ourEndPoint theirAddress sock = do
159164 return theirEndPoint
160165
161166-- Control headers
162−data ControlHeaders =
163− RequestConnectionId -- ^ Request a new connection ID from the remote endpoint
167+data ControlHeader =
168+ ValidEndPoint -- ^ Acknowledgement sent when setting up a new TCP connection
169+ | RequestConnectionId -- ^ Request a new connection ID from the remote endpoint
164170 | ControlResponse -- ^ Respond to a control request _from_ the remote endpoint
165171 deriving Enum
166172
@@ -188,7 +194,7 @@ handleIncomingMessages ourEndPoint theirEndPoint = execWriterT . runMaybeT . for
188194 createNewConnection :: ControlRequestId -> MaybeT (WriterT [ConnectionId] IO) ()
189195 createNewConnection reqId = do
190196 newId <- liftIO $ getNextConnectionId ourEndPoint
191− withRemoteLock theirEndPoint $ do
197+ withRemoteLock theirEndPoint $
192198 sendMany sock (encodeInt32 ControlResponse : encodeInt32 reqId : prependLength [encodeInt32 newId])
193199 liftIO $ writeChan ourChannel (ConnectionOpened newId ReliableOrdered (theirEndPoint ^. remoteAddress))
194200 -- We add the new connection ID to the list of open connections only once the
@@ -258,18 +264,18 @@ tcpConnect :: TCPTransport -- ^ Transport
258264 -> Reliability -- ^ Reliability (ignored)
259265 -> IO (Either (FailedWith ConnectErrorCode) Connection)
260266tcpConnect transport ourEndPoint theirAddress _ = runErrorT $ do
261− theirEndPoint <- remoteEndPoint transport ourEndPoint theirAddress
262− connId <- requestNewConnection ourEndPoint theirEndPoint
263− let sock = theirEndPoint ^. remoteSocket
264− return Connection { send = runErrorT .
265− failWithIO (FailedWith SendFailed . show) .
266− withRemoteLock theirEndPoint .
267− sendMany sock .
268− (\payload -> encodeInt32 connId : prependLength payload)
269− , close = -- TODO: we should not close the TCP connection until all
270− -- lightweight connections have been closed
271− N.sClose sock
272− }
267+ theirEndPoint <- remoteEndPoint transport ourEndPoint theirAddress
268+ connId <- requestNewConnection ourEndPoint theirEndPoint
269+ let sock = theirEndPoint ^. remoteSocket
270+ return Connection { send = runErrorT .
271+ failWithIO (FailedWith SendFailed . show) .
272+ withRemoteLock theirEndPoint .
273+ sendMany sock .
274+ (\payload -> encodeInt32 connId : prependLength payload)
275+ , close = -- TODO: we should not close the TCP connection until all
276+ -- lightweight connections have been closed
277+ N.sClose sock
278+ }
273279
274280-- | Request a new connection
275281requestNewConnection :: (MonadIO m, MonadError (FailedWith ConnectErrorCode) m)
@@ -310,7 +316,7 @@ remoteEndPoint transport ourEndPoint theirAddress = do
310316 -- Create a new connection
311317 -- liftIO $ putStrLn $ "Creating socket from " ++ show ourAddress ++ " to " ++ show theirAddress
312318 (host, port, endPointId) <- failWith (invalidAddress "Could not parse") $
313− (decodeEndPointAddress theirAddress)
319+ decodeEndPointAddress theirAddress
314320 addr:_ <- failWithIO (invalidAddress . show) $
315321 N.getAddrInfo Nothing (Just host) (Just port)
316322 sock <- failWithIO (insufficientResources . show) $
@@ -319,7 +325,11 @@ remoteEndPoint transport ourEndPoint theirAddress = do
319325 failWithIO (failed . show) $ N.connect sock (N.addrAddress addr)
320326 -- Send the endpoint ID we're interested in and our own address
321327 failWithIO (failed . show) $ sendMany sock (encodeInt32 endPointId : prependLength [ourAddress])
322− liftIO $ forkRemoteEndPoint transport ourEndPoint theirAddress sock
328+ -- For the remote endpoint to acknowledge
329+ ack <- failWithMaybeIO (invalidAddress . show) $ recvInt32 sock
330+ case ack of
331+ ValidEndPoint -> liftIO $ forkRemoteEndPoint transport ourEndPoint theirAddress sock
332+ _ -> throwError (invalidAddress "Unexpected response")
323333 where
324334 invalidAddress = FailedWith ConnectInvalidAddress
325335 insufficientResources = FailedWith ConnectInsufficientResources
@@ -399,3 +409,5 @@ remoteEndPointAt addr = remoteEndPoints >>> mapLens addr
399409
400410pendingCtrlRequestsAt :: ControlRequestId -> Lens EndPointState (Maybe (MVar [ByteString]))
401411pendingCtrlRequestsAt ix = pendingCtrlRequests >>> intMapLens (fromIntegral ix)
412+
413+