@@ -1,3 +1,5 @@
1module Network.Transport.TCP
3module Network.Transport.TCP
2 ( mkTransport
4 ( mkTransport
3 , TCPConfig (..)
5 , TCPConfig (..)
@@ -5,30 +7,58 @@ module Network.Transport.TCP
6import Network.Transport
8import Network.Transport
10+import Control.Applicative
8import Control.Concurrent (forkIO, ThreadId, killThread, myThreadId)
11import Control.Concurrent (forkIO, ThreadId, killThread, myThreadId)
9import Control.Concurrent.Chan
12import Control.Concurrent.Chan
10import Control.Concurrent.MVar
13import Control.Concurrent.MVar
11import Control.Exception (SomeException, IOException, AsyncException(ThreadKilled),
14import Control.Exception (SomeException, IOException, AsyncException(ThreadKilled),
12 fromException, throwTo, throw, catch, handle)
15 fromException, throwTo, throw, catch, handle)
13import Control.Monad (forever, forM_)
16import Control.Monad (forever, forM_)
14−import Data.ByteString.Lazy.Char8 (ByteString)
18+import qualified Data.ByteString.Internal as BSI
15import Data.IntMap (IntMap)
20import Data.IntMap (IntMap)
21+import qualified Data.IntMap as IntMap
16−import Data.Int
23+import Data.Int (Int32)
24+import qualified Data.Serialize as Ser
17−import Data.Word
25+import Data.Word (Word8)
27+import Foreign.ForeignPtr (withForeignPtr)
28+import Foreign.Ptr (Ptr, plusPtr)
29+import Foreign.Storable (sizeOf)
18import Network.Socket
31import Network.Socket
19 ( AddrInfoFlag (AI_PASSIVE), HostName, ServiceName, Socket
32 ( AddrInfoFlag (AI_PASSIVE), HostName, ServiceName, Socket
20 , SocketType (Stream), SocketOption (ReuseAddr)
33 , SocketType (Stream), SocketOption (ReuseAddr)
21 , accept, addrAddress, addrFlags, addrFamily, bindSocket, defaultProtocol
34 , accept, addrAddress, addrFlags, addrFamily, bindSocket, defaultProtocol
22 , getAddrInfo, listen, setSocketOption, socket, sClose, withSocketsDo )
35 , getAddrInfo, listen, setSocketOption, socket, sClose, withSocketsDo )
36+import qualified Network.Socket as N
23import Safe
40import Safe
24import System.IO (stderr, hPutStrLn)
41import System.IO (stderr, hPutStrLn)
44+import Data.ByteString.Char8 (ByteString)
26−import qualified Data.Binary as B
45+import qualified Data.ByteString.Char8 as BS
46+import qualified Network.Socket.ByteString as NBS
47+sendNBS = NBS.sendMany
27−import qualified Data.ByteString.Char8 as BSS
51+import Data.ByteString.Lazy.Char8 (ByteString)
28import qualified Data.ByteString.Lazy.Char8 as BS
52import qualified Data.ByteString.Lazy.Char8 as BS
29−import qualified Data.IntMap as IntMap
30−import qualified Network.Socket as N
31import qualified Network.Socket.ByteString.Lazy as NBS
53import qualified Network.Socket.ByteString.Lazy as NBS
54+sendNBS sock = NBS.sendAll sock . BS.concat
55+encode = Ser.encodeLazy
56+decode = Ser.decodeLazy
60+encode :: Ser.Serialize a => a -> ByteString
61+decode :: Ser.Serialize a => ByteString -> Either String a
33type ChanId = Int
63type ChanId = Int
34type Chans = MVar (ChanId, IntMap (Chan [ByteString], [(ThreadId, Socket)]))
64type Chans = MVar (ChanId, IntMap (Chan [ByteString], [(ThreadId, Socket)]))
@@ -70,7 +100,9 @@ mkTransport (TCPConfig _hints host service) = withSocketsDo $ do
70 return (mkSourceAddr host service chanId, mkTargetEnd chans chanId chan)
100 return (mkSourceAddr host service chanId, mkTargetEnd chans chanId chan)
71 , newMulticastWith = error "newMulticastWith: not defined"
101 , newMulticastWith = error "newMulticastWith: not defined"
72 , deserialize = {-# SCC "deserialize" #-} \bs ->
102 , deserialize = {-# SCC "deserialize" #-} \bs ->
73− let (host, service, chanId) = B.decode bs in
103+ let (host, service, chanId) =
104+ either (\m -> error $ printf "deserialize: %s" m)
74 Just $ mkSourceAddr host service chanId
106 Just $ mkSourceAddr host service chanId
75 , closeTransport = {-# SCC "closeTransport" #-} do
107 , closeTransport = {-# SCC "closeTransport" #-} do
76 -- Kill the transport channel process
108 -- Kill the transport channel process
@@ -87,29 +119,26 @@ mkTransport (TCPConfig _hints host service) = withSocketsDo $ do
87mkSourceAddr :: HostName -> ServiceName -> ChanId -> SourceAddr
119mkSourceAddr :: HostName -> ServiceName -> ChanId -> SourceAddr
88mkSourceAddr host service chanId = SourceAddr
120mkSourceAddr host service chanId = SourceAddr
89 { connectWith = {-# SCC "connectWith" #-} \_ -> mkSourceEnd host service chanId
121 { connectWith = {-# SCC "connectWith" #-} \_ -> mkSourceEnd host service chanId
90− , serialize = {-# SCC "serialize" #-} B.encode (host, service, chanId)
122+ , serialize = {-# SCC "serialize" #-} encode (host, service, chanId)
93mkSourceEnd :: HostName -> ServiceName -> ChanId -> IO SourceEnd
125mkSourceEnd :: HostName -> ServiceName -> ChanId -> IO SourceEnd
94mkSourceEnd host service chanId = withSocketsDo $ do
126mkSourceEnd host service chanId = withSocketsDo $ do
127+ let err m = error $ printf "mkSourceEnd: %s" m
95 serverAddrs <- getAddrInfo Nothing (Just host) (Just service)
128 serverAddrs <- getAddrInfo Nothing (Just host) (Just service)
96 let serverAddr = case serverAddrs of
129 let serverAddr = case serverAddrs of
97− [] -> error "mkSourceEnd: getAddrInfo returned []"
130+ [] -> err "getAddrInfo returned []"
98 as -> head as
131 as -> head as
99 sock <- socket (addrFamily serverAddr) Stream defaultProtocol
132 sock <- socket (addrFamily serverAddr) Stream defaultProtocol
100 setSocketOption sock ReuseAddr 1
133 setSocketOption sock ReuseAddr 1
101 N.connect sock (addrAddress serverAddr)
134 N.connect sock (addrAddress serverAddr)
102− NBS.sendAll sock $ B.encode (fromIntegral chanId :: Int64)
135+ NBS.sendAll sock $ encode (fromIntegral chanId :: Int32)
103 return SourceEnd
136 return SourceEnd
104 { send = {-# SCC "send" #-} \bss -> do
137 { send = {-# SCC "send" #-} \bss -> do
105− let size = fromIntegral (sum . map BS.length $ bss) :: Int64
138+ let size = fromIntegral (sum . map BS.length $ bss) :: Int32
106− if size < 255
139+ sizeStr | size < 255 = BS.singleton (toEnum $ fromEnum size)
107− then
140+ | otherwise = BS.cons (toEnum 255) (encode size)
108− NBS.sendAll sock (B.encode (fromIntegral size :: Word8))
141+ sendNBS sock (sizeStr:bss)
110− NBS.sendAll sock (B.encode (255 :: Word8))
111− NBS.sendAll sock (B.encode size)
112− mapM_ (NBS.sendAll sock) bss
113 , closeSourceEnd = {-# SCC "closeSourceEnd" #-} sClose sock
142 , closeSourceEnd = {-# SCC "closeSourceEnd" #-} sClose sock
@@ -133,19 +162,19 @@ mkTargetEnd chans chanId chan = TargetEnd
133-- for some reason, an error is raised.
162-- for some reason, an error is raised.
134procConnections :: Chans -> Socket -> IO ()
163procConnections :: Chans -> Socket -> IO ()
135procConnections chans sock = forever $ do
164procConnections chans sock = forever $ do
165+ let err m = error $ printf "procConnections: %s" m
136 (clientSock, _clientAddr) <- accept sock
166 (clientSock, _clientAddr) <- accept sock
137 -- decode the first message to find the correct chanId
167 -- decode the first message to find the correct chanId
138− bss <- recvExact clientSock 8
168+ bss <- recvExact clientSock (fromIntegral $ sizeOf (0 :: Int32))
139 case bss of
169 case bss of
140− [] -> error "procConnections: inbound chanId aborted"
170+ Left _ -> err "inbound chanId aborted"
141− bss -> do
171+ Right bs -> do
142− let bs = BS.concat bss
143− let chanId = fromIntegral (B.decode bs :: Int64)
172+ let chanId = either err fromIntegral (decode bs :: Either String Int32)
144 (chanId', chanMap) <- takeMVar chans
173 (chanId', chanMap) <- takeMVar chans
145 case IntMap.lookup chanId chanMap of
174 case IntMap.lookup chanId chanMap of
146 Nothing -> do
175 Nothing -> do
147 putMVar chans (chanId', chanMap)
176 putMVar chans (chanId', chanMap)
148− error "procConnections: cannot find chanId"
177+ err "cannot find chanId"
149 Just (chan, socks) -> do
178 Just (chan, socks) -> do
150 threadId <- forkWithExceptions forkIO "Message Listener" $
179 threadId <- forkWithExceptions forkIO "Message Listener" $
151 procMessages chans chanId chan clientSock
180 procMessages chans chanId chan clientSock
@@ -154,80 +183,94 @@ procConnections chans sock = forever $ do
155-- | This function first extracts a header of type Word8, which determines
184-- | This function first extracts a header of type Word8, which determines
156-- the size of the ByteString that follows. If this size is 0, this indicates
185-- the size of the ByteString that follows. If this size is 0, this indicates
157−-- that the ByteString is large, so the next value is an Int64, which
186+-- that the ByteString is large, so the next value is an Int32, which
158-- determines the size of the ByteString that follows. The ByteString is then
187-- determines the size of the ByteString that follows. The ByteString is then
159-- extracted from the socket, and then written to the Chan only when
188-- extracted from the socket, and then written to the Chan only when
160-- complete. If either of the first header size is null this indicates the
189-- complete. If either of the first header size is null this indicates the
161-- socket has closed.
190-- socket has closed.
162procMessages :: Chans -> ChanId -> Chan [ByteString] -> Socket -> IO ()
191procMessages :: Chans -> ChanId -> Chan [ByteString] -> Socket -> IO ()
163procMessages chans chanId chan sock = do
192procMessages chans chanId chan sock = do
164− sizeBSs <- recvExact sock 1
193+ esizeBS <- recvExact sock 1
165− case sizeBSs of
194+ case esizeBS of
166− [] -> closeSocket
195+ Left _ -> closeSocket
167− [onebyte] -> do
196+ Right sizeBS -> do
168− let size = fromIntegral (B.decode onebyte :: Word8)
197+ case decode sizeBS :: Either String Word8 of
169− if size == 255
198+ Right 255 -> do
171− sizeBSs' <- recvExact sock 8
199+ esizeBS' <- recvExact sock (fromIntegral $ sizeOf (0 :: Int32))
172− case sizeBSs' of
200+ case esizeBS' of
173− [] -> closeSocket
201+ Left _ -> closeSocket
174− _ -> procMessage (B.decode . BS.concat$ sizeBSs' :: Int64)
202+ Right sizeBS' -> procMessage $ decode sizeBS'
175− else procMessage size
203+ esize@(Right _) -> procMessage (fromIntegral <$> esize)
176− ls -> error "Shouldn't receive more than one bytestring when expecting a single byte!"
204+ Left msg -> error msg
206+ err m = error $ printf "procMessages: %s" m
178 closeSocket :: IO ()
207 closeSocket :: IO ()
179 closeSocket = do
208 closeSocket = do
180 (chanId', chanMap) <- takeMVar chans
209 (chanId', chanMap) <- takeMVar chans
181 case IntMap.lookup chanId chanMap of
210 case IntMap.lookup chanId chanMap of
182 Nothing -> do
211 Nothing -> do
183 putMVar chans (chanId', chanMap)
212 putMVar chans (chanId', chanMap)
184− error "procMessages: chanId not found."
213+ err "chanId not found."
185 Just (chan, socks) -> do
214 Just (chan, socks) -> do
186 let socks' = filter ((/= sock) . snd) socks
215 let socks' = filter ((/= sock) . snd) socks
187 let chanMap' = IntMap.insert chanId (chan, socks') chanMap
216 let chanMap' = IntMap.insert chanId (chan, socks') chanMap
188 putMVar chans (chanId', chanMap')
217 putMVar chans (chanId', chanMap')
189 sClose sock
218 sClose sock
190− procMessage :: Int64 -> IO ()
219+ procMessage :: Either String Int32 -> IO ()
191− procMessage size = do
220+ procMessage (Left msg) = err msg
221+ procMessage (Right size) = do
192− bss <- recvExact sock size
222+ ebs <- recvExact sock size
193− case bss of
223+ case ebs of
194− [] -> closeSocket
224+ Left _ -> closeSocket
195− _ -> do
225+ Right bs -> do
196− writeChan chan bss
226+ writeChan chan [bs]
197 procMessages chans chanId chan sock
227 procMessages chans chanId chan sock
199−-- | The result of `recvExact sock n` is a `[ByteString]` whose concatenation
229+-- | The normal result of `recvExact sock n` is `Right ByteString`
200−-- is of length `n`, received from `sock`. No more bytes than necessary are
230+-- whose string of length `n`, received from `sock`. If fewer than `n`
231+-- bytes are read from `sock` before it closes, the result is `Left
232+-- ByteString` whose string is those bytes that were received. No more
201−-- read from the socket.
233+-- bytes than necessary are read from the socket. NB: This uses
202−-- NB: This uses Network.Socket.ByteString.recv, which may *discard*
234+-- Network.Socket.ByteString.recv, which may *discard* superfluous
203−-- superfluous input depending on the socket type. Also note that
235+-- input depending on the socket type.
204−-- if `recv` returns an empty `ByteString` then this means that the socket
236+#ifndef LAZY
205−-- was closed: in this case, we return an empty list.
237+recvExact :: Socket -> Int32 -> IO (Either ByteString ByteString)
206−-- NB: It may be appropriate to change the return type to
238+recvExact sock l = interceptAllExn "recvExact" $ do
207−-- IO (Either ByteString ByteString), where `return Left bs` indicates
239+ res <- BSI.createAndTrim (fromIntegral l) (go 0)
208−-- a partial retrieval since the socket was closed, and `return Right bs`
240+ case BS.length res of
209−-- indicates success. This hasn't been implemented, since a Transport `receive`
241+ n | n == (fromIntegral l) -> return $ Right res
210−-- represents an atomic receipt of a message.
242+ n -> return $ Left res
244+ go :: Int -> Ptr Word8 -> IO Int
245+ go n ptr | n == (fromIntegral l) = return n
247+ (p, off, len) <- BSI.toForeignPtr <$> NBS.recv sock (min (fromIntegral l-n) 4096)
250+ else withForeignPtr p $ \p -> do
251+ BSI.memcpy (ptr `plusPtr` n) (p `plusPtr` off) (fromIntegral len)
211−recvExact :: Socket -> Int64 -> IO [ByteString]
254+recvExact :: Socket -> Int32 -> IO (Either ByteString ByteString)
212recvExact sock n =
255recvExact sock n =
213− interceptAllExn "recvExact" $
256+ interceptAllExn "recvExact" $
214 go [] sock n
257 go [] sock n
216− go :: [ByteString] -> Socket -> Int64 -> IO [ByteString]
259+ go :: [ByteString] -> Socket -> Int32 -> IO (Either ByteString ByteString)
217− go bss _ 0 = return (reverse bss)
260+ go bss _ 0 = return (Right $ BS.concat (reverse bss))
218 go bss sock n = do
261 go bss sock n = do
219− bs <- NBS.recv sock n
262+ bs <- NBS.recv sock (min (fromIntegral n) 4096)
220 if BS.null bs
263 if BS.null bs
221− then return []
264+ then return (Left $ BS.concat (reverse bss))
222− else go (bs:bss) sock (n - BS.length bs)
265+ else go (bs:bss) sock (n - (fromIntegral $ BS.length bs))
225interceptAllExn msg =
268interceptAllExn msg =
226 Control.Exception.handle $ \ e ->
269 Control.Exception.handle $ \ e ->
227 case fromException e of
270 case fromException e of
228 Just ThreadKilled -> throw e
271 Just ThreadKilled -> throw e
229 Nothing -> do
272 Nothing -> do
230− BSS.hPutStrLn stderr $ BSS.pack$ "Exception inside "++msg++": "++show e
273+ BS.hPutStrLn stderr $ BS.pack$ "Exception inside "++msg++": "++show e
232-- throw (e :: SomeException)
275-- throw (e :: SomeException)
@@ -242,7 +285,7 @@ forkWithExceptions forkit descr action = do
242-- BSS.hPutStrLn stderr $ BSS.pack$ "Note: Child thread killed: "++descr
285-- BSS.hPutStrLn stderr $ BSS.pack$ "Note: Child thread killed: "++descr
243 return ()
286 return ()
245− BSS.hPutStrLn stderr $ BSS.pack$ "Exception inside child thread "++descr++": "++show e
288+ BS.hPutStrLn stderr $ BS.pack$ "Exception inside child thread "++descr++": "++show e
246 throwTo parent (e::SomeException)
289 throwTo parent (e::SomeException)