nettle-openflow-0.2.0: src/Nettle/Servers/MultiplexedTCPServer.hs
-- | 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)