-- | This module provides a TCP server that multiplexes incoming and outgoing messages -- from many connected peers onto a single pair of input and output channels. The socket address -- of the peer is used to identify the source and destination of messages. -- -- This interface introduces a new error condition: that a message on the outgoing channel has a -- socket address for which no socket exists. This may occur because of incorrect usage of this library, -- or because a peer disconnected after the client placed a message on the outgoing channel, -- but before that message was sent. Currently, the server does not notify its caller of the occurrence of this error. module Nettle.Servers.MultiplexedTCPServer ( muxedTCPServer, TCPMessage(..), ServerPortNumber ) where import Prelude hiding (interact, catch) import Network.Socket (SockAddr) import Nettle.OpenFlow.Messages import Control.Concurrent import Control.Exception import Control.Monad import qualified Data.Map as Map import Nettle.Servers.Server -- | The type of externally visible events that may occur for the multiplexed TCP server. data TCPMessage a = ConnectionEstablished SockAddr -- ^ A connection to a peer with the given address is established. | ConnectionTerminated SockAddr -- ^ A connection with the given address is terminated, due to the given exception. | PeerMessage SockAddr a -- ^ A message of type @a@ has been received from the peer with the given address. deriving (Show,Eq) instance Functor TCPMessage where fmap f (ConnectionEstablished a) = ConnectionEstablished a fmap f (ConnectionTerminated a) = ConnectionTerminated a fmap f (PeerMessage a x) = PeerMessage a (f x) -- | Runs a server that returns two commands, one to receive the next message from any connected client, -- and one that sends a message to a client. muxedTCPServer :: ServerPortNumber -> IO (IO (TCPMessage (TransactionID,SCMessage)), SockAddr -> (TransactionID,CSMessage) -> IO ()) muxedTCPServer pstring = do server <- startOpenFlowServer Nothing pstring addressToClientMap <- newMVar Map.empty incomingChan <- newChan let enqIncoming clientHandle = do mm <- receiveFromSwitch clientHandle case mm of Nothing -> do modifyMVar_ addressToClientMap (return . Map.delete (switchSockAddr clientHandle)) writeChan incomingChan (ConnectionTerminated (switchSockAddr clientHandle)) return () Just m -> do writeChan incomingChan (PeerMessage (switchSockAddr clientHandle) m) enqIncoming clientHandle let getIncoming = readChan incomingChan let postOutgoing sockAddress msg = withMVar addressToClientMap $ \dict -> case Map.lookup sockAddress dict of Just switchHandle -> sendToSwitch switchHandle msg Nothing -> error ("handle disappeared before message " ++ show msg ++ " was sent.") let acceptLoop = forever (do (client,_) <- acceptSwitch server modifyMVar_ addressToClientMap (return . Map.insert (switchSockAddr client) client) writeChan incomingChan (ConnectionEstablished (switchSockAddr client)) forkIO (enqIncoming client) ) forkIO acceptLoop return (getIncoming, postOutgoing)