@@ -29,6 +29,7 @@ import Network.Transport.Internal ( encodeInt32
29 , failWith
29 , failWith
30 , failWithIO
30 , failWithIO
33import qualified Network.Socket as N ( HostName
34import qualified Network.Socket as N ( HostName
34 , ServiceName
35 , ServiceName
@@ -61,7 +62,7 @@ import Data.IORef (IORef, newIORef, writeIORef, readIORef, modifyIORef)
61import Control.Category ((>>>))
62import Control.Category ((>>>))
62import Control.Applicative ((<*>), (*>), (<$>))
63import Control.Applicative ((<*>), (*>), (<$>))
63import Control.Monad (forM_, void, when)
64import Control.Monad (forM_, void, when)
64−import Control.Monad.Error (ErrorT(ErrorT), runErrorT)
65+import Control.Monad.Error (ErrorT(ErrorT), runErrorT, throwError)
65import Control.Monad.IO.Class (liftIO)
66import Control.Monad.IO.Class (liftIO)
66import Control.Exception (IOException)
67import Control.Exception (IOException)
67import Data.ByteString (ByteString)
68import Data.ByteString (ByteString)
@@ -250,14 +251,14 @@ data ControlHeader =
250 | CloseConnection -- ^ Tell the remote endpoint we will no longer be using a connection
251 | CloseConnection -- ^ Tell the remote endpoint we will no longer be using a connection
251 | ControlResponse -- ^ Respond to a control request _from_ the remote endpoint
252 | ControlResponse -- ^ Respond to a control request _from_ the remote endpoint
252 | CloseSocket -- ^ Request to close the connection (see module description)
253 | CloseSocket -- ^ Request to close the connection (see module description)
253− deriving Enum
254+ deriving (Enum, Bounded)
255-- Response sent by /B/ to /A/ when /A/ tries to connect
256-- Response sent by /B/ to /A/ when /A/ tries to connect
256data ConnectionRequestResponse =
257data ConnectionRequestResponse =
257 ConnectionRequestAccepted -- ^ /B/ accepts the connection
258 ConnectionRequestAccepted -- ^ /B/ accepts the connection
258 | ConnectionRequestEndPointInvalid -- ^ /A/ requested an invalid endpoint
259 | ConnectionRequestEndPointInvalid -- ^ /A/ requested an invalid endpoint
259 | ConnectionRequestCrossed -- ^ /A/s request crossed with a request from /B/ (see protocols)
260 | ConnectionRequestCrossed -- ^ /A/s request crossed with a request from /B/ (see protocols)
260− deriving Enum
261+ deriving (Enum, Bounded)
262--------------------------------------------------------------------------------
263--------------------------------------------------------------------------------
263-- Top-level functionality --
264-- Top-level functionality --
@@ -527,13 +528,14 @@ connectToRemoteEndPoint (ourEndPoint, theirEndPoint) = do
527 N.getAddrInfo Nothing (Just host) (Just port)
528 N.getAddrInfo Nothing (Just host) (Just port)
528 sock <- failWithIO (insufficientResources . show) $
529 sock <- failWithIO (insufficientResources . show) $
529 N.socket (N.addrFamily addr) N.Stream N.defaultProtocol
530 N.socket (N.addrFamily addr) N.Stream N.defaultProtocol
530− failWithIO (failed . show) $ do
531+ response <- failWithIO (failed . show) $ do
531 N.setSocketOption sock N.ReuseAddr 1
532 N.setSocketOption sock N.ReuseAddr 1
532 N.connect sock (N.addrAddress addr)
533 N.connect sock (N.addrAddress addr)
533 sendMany sock (encodeInt32 theirEndPointId : prependLength [ourAddress])
534 sendMany sock (encodeInt32 theirEndPointId : prependLength [ourAddress])
534− -- TODO: what happens when the server sends an int outside the range of ConnectionRequestResponse?
535+ recvInt32 sock
535− response <- recvInt32 sock
536+ case tryToEnum response of
537+ Nothing -> throwError (failed "Unexpected response")
536− return (response, sock)
538+ Just r -> return (r, sock)
538 invalidAddress, insufficientResources, failed :: String -> FailedWith ConnectErrorCode
540 invalidAddress, insufficientResources, failed :: String -> FailedWith ConnectErrorCode
539 invalidAddress = FailedWith ConnectInvalidAddress
541 invalidAddress = FailedWith ConnectInvalidAddress
@@ -667,19 +669,22 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
667 readMessage sock connId
669 readMessage sock connId
668 go sock openConnections
670 go sock openConnections
670− case toEnum (fromIntegral connId) of
672+ case tryToEnum (fromIntegral connId) of
671− RequestConnectionId -> do
673+ Just RequestConnectionId -> do
672 recvInt32 sock >>= createNewConnection openConnections
674 recvInt32 sock >>= createNewConnection openConnections
673 go sock openConnections
675 go sock openConnections
674− ControlResponse -> do
676+ Just ControlResponse -> do
675 recvInt32 sock >>= readControlResponse sock
677 recvInt32 sock >>= readControlResponse sock
676 go sock openConnections
678 go sock openConnections
677− CloseConnection -> do
679+ Just CloseConnection -> do
678 recvInt32 sock >>= closeConnection openConnections
680 recvInt32 sock >>= closeConnection openConnections
679 go sock openConnections
681 go sock openConnections
680− CloseSocket -> do
682+ Just CloseSocket -> do
681 closeSocket sock
683 closeSocket sock
682 go sock openConnections
684 go sock openConnections
686+ -- Invalid control request, exit
687+ putStrLn "Warning: invalid control request"
684 -- Create a new connection
689 -- Create a new connection
685 createNewConnection :: IORef IntSet -> ControlRequestId -> IO ()
690 createNewConnection :: IORef IntSet -> ControlRequestId -> IO ()