@@ -1,12 +1,21 @@
1-- | TCP transport
1-- | TCP transport
2−module Network.Transport.TCP where
2+module Network.Transport.TCP ( -- * Main API
4+ , -- TCP specific functionality
6+ , decodeEndPointAddress
4import Network.Transport
9import Network.Transport
10+import Network.Transport.Internal.TCP ( forkServer
5import Network.Transport.Internal ( encodeInt16
15import Network.Transport.Internal ( encodeInt16
6 , decodeInt16
16 , decodeInt16
9 , maybeToErrorT
17 , maybeToErrorT
11import qualified Network.Socket as N ( HostName
20import qualified Network.Socket as N ( HostName
12 , ServiceName
21 , ServiceName
@@ -14,10 +23,7 @@ import qualified Network.Socket as N ( HostName
14 , SocketType(Stream)
23 , SocketType(Stream)
15 , SocketOption(ReuseAddr)
24 , SocketOption(ReuseAddr)
16 , getAddrInfo
25 , getAddrInfo
21 , addrFamily
27 , addrFamily
22 , addrAddress
28 , addrAddress
23 , defaultProtocol
29 , defaultProtocol
@@ -26,22 +32,21 @@ import qualified Network.Socket as N ( HostName
29−import qualified Network.Socket.ByteString as NBS (sendMany, recv)
35+import qualified Network.Socket.ByteString as NBS (sendMany)
30−import Control.Concurrent (forkIO, ThreadId)
36+import Control.Concurrent (forkIO)
31import Control.Concurrent.Chan (Chan, newChan, readChan, writeChan)
37import Control.Concurrent.Chan (Chan, newChan, readChan, writeChan)
32import Control.Concurrent.MVar (MVar, newMVar, modifyMVar, readMVar)
38import Control.Concurrent.MVar (MVar, newMVar, modifyMVar, readMVar)
33import Control.Category ((>>>))
39import Control.Category ((>>>))
34import Control.Applicative ((<*>), (*>), (<$>), pure)
40import Control.Applicative ((<*>), (*>), (<$>), pure)
35−import Control.Monad (forever, mzero)
41+import Control.Monad (forever)
36import Control.Monad.Error (ErrorT, liftIO, runErrorT)
42import Control.Monad.Error (ErrorT, liftIO, runErrorT)
37import Control.Monad.Trans.Maybe (MaybeT(MaybeT), runMaybeT)
43import Control.Monad.Trans.Maybe (MaybeT(MaybeT), runMaybeT)
38import Data.ByteString (ByteString)
44import Data.ByteString (ByteString)
39−import qualified Data.ByteString as BS (length, concat, null)
45+import qualified Data.ByteString as BS (concat)
40import qualified Data.ByteString.Char8 as BSC (pack, unpack)
46import qualified Data.ByteString.Char8 as BSC (pack, unpack)
41import Data.IntMap (IntMap)
47import Data.IntMap (IntMap)
42import qualified Data.IntMap as IntMap (empty, size)
48import qualified Data.IntMap as IntMap (empty, size)
43import Data.Lens.Lazy (Lens, lens, intMapLens, (^.), (^=), (^+=))
49import Data.Lens.Lazy (Lens, lens, intMapLens, (^.), (^=), (^+=))
44−import Data.Int (Int32)
45import Text.Regex.Applicative (RE, few, anySym, sym, many, match)
50import Text.Regex.Applicative (RE, few, anySym, sym, many, match)
47data TransportState = TransportState { _endPoints :: IntMap (Chan Event, MVar EndPointState) }
52data TransportState = TransportState { _endPoints :: IntMap (Chan Event, MVar EndPointState) }
@@ -55,7 +60,7 @@ endPoints = lens _endPoints (\es st -> st { _endPoints = es })
55nextConnectionIx :: Lens EndPointState ConnectionIx
60nextConnectionIx :: Lens EndPointState ConnectionIx
56nextConnectionIx = lens _nextConnectionIx (\cix st -> st { _nextConnectionIx = cix })
61nextConnectionIx = lens _nextConnectionIx (\cix st -> st { _nextConnectionIx = cix })
58−endPointAt :: Int -> Lens TransportState (Maybe (Chan Event, MVar EndPointState))
63+endPointAt :: EndPointIx -> Lens TransportState (Maybe (Chan Event, MVar EndPointState))
59endPointAt ix = endPoints >>> intMapLens ix
64endPointAt ix = endPoints >>> intMapLens ix
61-- | Create a TCP transport
66-- | Create a TCP transport
@@ -74,19 +79,6 @@ createTransport host port = do
74 forkServer host port (transportServer state)
79 forkServer host port (transportServer state)
75 return Transport { newEndPoint = tcpNewEndPoint state host port }
80 return Transport { newEndPoint = tcpNewEndPoint state host port }
77−-- | Start a server at the specified address
78−forkServer :: N.HostName -> N.ServiceName -> (N.Socket -> IO ()) -> IO ThreadId
79−forkServer host port server = do
80− -- Resolve the specified address. By specification, getAddrInfo will never
81− -- return an empty list (but will throw an exception instead) and will return
82− -- the "best" address first, whatever that means
83− addr:_ <- N.getAddrInfo (Just N.defaultHints) (Just host) (Just port)
84− sock <- N.socket (N.addrFamily addr) N.Stream N.defaultProtocol
85− N.setSocketOption sock N.ReuseAddr 1
86− N.bindSocket sock (N.addrAddress addr)
90-- | The transport server handles incoming connections for all endpoints
82-- | The transport server handles incoming connections for all endpoints
92-- TODO: lots of error checking
84-- TODO: lots of error checking
@@ -129,7 +121,7 @@ endpointServer sock theirAddress chan state = forever $ do
129 writeChan chan (ConnectionOpened newIx ReliableOrdered theirAddress)
121 writeChan chan (ConnectionOpened newIx ReliableOrdered theirAddress)
131 -- Read a message and output it on the endPoint's channel
123 -- Read a message and output it on the endPoint's channel
132− readMessage :: Int -> MaybeT IO ()
124+ readMessage :: ConnectionIx -> MaybeT IO ()
133 readMessage connIx = do
125 readMessage connIx = do
134 payload <- recvWithLength sock
126 payload <- recvWithLength sock
135 liftIO $ writeChan chan (Received connIx payload)
127 liftIO $ writeChan chan (Received connIx payload)
@@ -145,24 +137,29 @@ tcpNewEndPoint transportState host port = do
145 return . Right $ EndPoint { receive = readChan chan
137 return . Right $ EndPoint { receive = readChan chan
146 , address = addr
138 , address = addr
147 , connect = tcpConnect addr
139 , connect = tcpConnect addr
148− , newMulticastGroup = undefined
140+ , newMulticastGroup = return . Left $ newMulticastGroupError
149− , resolveMulticastGroup = undefined
141+ , resolveMulticastGroup = \_ -> return . Left $ resolveMulticastGroupError
144+ newMulticastGroupError = FailedWith NewMulticastGroupUnsupported "TCP does not support multicast"
145+ resolveMulticastGroupError = FailedWith ResolveMulticastGroupUnsupported "TCP does not support multicast"
152-- | Connnect to an endpoint
148-- | Connnect to an endpoint
153tcpConnect :: EndPointAddress -> EndPointAddress -> Reliability -> IO (Either (FailedWith ConnectErrorCode) Connection)
149tcpConnect :: EndPointAddress -> EndPointAddress -> Reliability -> IO (Either (FailedWith ConnectErrorCode) Connection)
154tcpConnect myAddress theirAddress _ = runErrorT $ do
150tcpConnect myAddress theirAddress _ = runErrorT $ do
155− sock <- socketForEndPoint myAddress theirAddress
151+ sock <- socketForEndPoint myAddress theirAddress
152+ -- TODO: test this failure
156− connIx <- liftIO $ requestNewConnection sock
153+ connIx <- maybeTToErrorT (FailedWith undefined undefined) $ requestNewConnection sock
157− return $ Connection { send = sendWithLength sock (Just connIx)
154+ return $ Connection { send = sendWithLength sock (Just connIx)
158− , close = N.sClose sock
155+ , close = N.sClose sock
161-- | Request a new connection
158-- | Request a new connection
162−requestNewConnection :: N.Socket -> IO ByteString
159+requestNewConnection :: N.Socket -> MaybeT IO ByteString
163requestNewConnection sock = do
160requestNewConnection sock = do
164− pure <$> encodeInt16 0 >>= NBS.sendMany sock
161+ liftIO $ pure <$> encodeInt16 0 >>= NBS.sendMany sock
165− NBS.recv sock 2
162+ BS.concat <$> recvExact sock 2
167-- | Find a socket to the specified endpoint
164-- | Find a socket to the specified endpoint
@@ -172,7 +169,7 @@ requestNewConnection sock = do
172socketForEndPoint :: EndPointAddress -- ^ Our address
169socketForEndPoint :: EndPointAddress -- ^ Our address
173 -> EndPointAddress -- ^ Their address
170 -> EndPointAddress -- ^ Their address
174 -> ErrorT (FailedWith ConnectErrorCode) IO N.Socket
171 -> ErrorT (FailedWith ConnectErrorCode) IO N.Socket
175−socketForEndPoint (EndPointAddress myAddress) (EndPointAddress theirAddress) = do
172+socketForEndPoint (EndPointAddress myAddress) theirAddress = do
176 (host, port, endPointIx) <- maybeToErrorT invalidAddress (decodeEndPointAddress theirAddress)
173 (host, port, endPointIx) <- maybeToErrorT invalidAddress (decodeEndPointAddress theirAddress)
177 liftIO $ do
174 liftIO $ do
178 -- Connect to the destination transport
175 -- Connect to the destination transport
@@ -188,46 +185,11 @@ socketForEndPoint (EndPointAddress myAddress) (EndPointAddress theirAddress) = d
189 invalidAddress = FailedWith ConnectInvalidAddress "Invalid address"
186 invalidAddress = FailedWith ConnectInvalidAddress "Invalid address"
191−-- | Send a bunch of bytestrings prepended with their length
192−sendWithLength :: N.Socket -- ^ Socket to send on
193− -> Maybe ByteString -- ^ Optional header to send before the length
194− -> [ByteString] -- ^ Payload
196−sendWithLength sock header payload = do
197− lengthBs <- encodeInt32 (fromIntegral . sum . map BS.length $ payload)
198− let msg = maybe id (:) header $ lengthBs : payload
199− NBS.sendMany sock msg
201−-- | Read a length and then a payload of that length
202−-- TODO: error handling
203−recvWithLength :: N.Socket -> MaybeT IO [ByteString]
204−recvWithLength sock = do
205− msgLengthBs <- recvExact sock 4
206− decodeInt32 (BS.concat msgLengthBs) >>= recvExact sock
208-- | Decode end point address
188-- | Decode end point address
209-- TODO: This uses regular expression parsing, which is nice, but unnecessary
189-- TODO: This uses regular expression parsing, which is nice, but unnecessary
210−decodeEndPointAddress :: ByteString -> Maybe (N.HostName, N.ServiceName, Int)
190+decodeEndPointAddress :: EndPointAddress -> Maybe (N.HostName, N.ServiceName, EndPointIx)
211−decodeEndPointAddress bs = match endPointAddressRE (BSC.unpack bs)
191+decodeEndPointAddress (EndPointAddress bs) = match endPointAddressRE (BSC.unpack bs)
213− endPointAddressRE :: RE Char (N.HostName, N.ServiceName, Int)
193+ endPointAddressRE :: RE Char (N.HostName, N.ServiceName, EndPointIx)
214 endPointAddressRE = (,,) <$> few anySym <*> (sym ':' *> few anySym) <*> (sym ':' *> (read <$> many anySym))
194 endPointAddressRE = (,,) <$> few anySym <*> (sym ':' *> few anySym) <*> (sym ':' *> (read <$> many anySym))
216−-- | Read an exact number of bytes from a socket
218−-- Returns 'Nothing' if the socket closes prematurely.
219−recvExact :: N.Socket -- ^ Socket to read from
220− -> Int32 -- ^ Number of bytes to read
221− -> MaybeT IO [ByteString]
222−recvExact sock len = do
223− (socketClosed, input) <- liftIO $ go [] len
224− if socketClosed then mzero else return input
226− -- Returns input read and whether the socket closed prematurely
227− go :: [ByteString] -> Int32 -> IO (Bool, [ByteString])
228− go acc 0 = return (False, reverse acc)
230− bs <- NBS.recv sock (fromIntegral l `min` 4096)
232− then return (True, reverse acc)
233− else go (bs : acc) (l - (fromIntegral $ BS.length bs))