1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
|
{-# LANGUAGE CPP, ScopedTypeVariables #-}
module Main where
import Control.Concurrent (ThreadId, forkIO, myThreadId)
import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar, readMVar)
import qualified Control.Exception as E
import Control.Monad (liftM, when)
import qualified Data.ByteString as S
import qualified Data.ByteString.Char8 as C
import Data.Maybe (fromJust)
import Network.Socket hiding (recv, recvFrom, send, sendTo)
import Network.Socket.ByteString
--- To tests for AF_CAN on Linux, you need to bring up a virtual (or real can
--- interface.). Run as root:
--- # modprobe can
--- # modprobe can_raw
--- # modprobe vcan
--- # sudo ip link add dev vcan0 type vcan
--- # ip link show vcan0
--- 3: can0: <NOARP,UP,LOWER_UP> mtu 16 qdisc noqueue state UNKNOWN link/can
--- Define HAVE_LINUX_CAN to run CAN tests as well.
--- #define HAVE_LINUX_CAN 1
-- #include "../include/HsNetworkConfig.h"
#if defined(HAVE_LINUX_CAN_H)
import Network.BSD (ifNameToIndex)
#endif
import Test.Framework (Test, defaultMain, testGroup)
import Test.Framework.Providers.HUnit (testCase)
import Test.HUnit (Assertion, (@=?))
------------------------------------------------------------------------
serverAddr :: String
serverAddr = "127.0.0.1"
testMsg :: S.ByteString
testMsg = C.pack "This is a test message."
------------------------------------------------------------------------
-- Tests
------------------------------------------------------------------------
-- Sending and receiving
testSend :: Assertion
testSend = tcpTest client server
where
server sock = recv sock 1024 >>= (@=?) testMsg
client sock = send sock testMsg
testSendAll :: Assertion
testSendAll = tcpTest client server
where
server sock = recv sock 1024 >>= (@=?) testMsg
client sock = sendAll sock testMsg
testSendTo :: Assertion
testSendTo = udpTest client server
where
server sock = recv sock 1024 >>= (@=?) testMsg
client sock serverPort = do
addr <- inet_addr serverAddr
sendTo sock testMsg (SockAddrInet serverPort addr)
testSendAllTo :: Assertion
testSendAllTo = udpTest client server
where
server sock = recv sock 1024 >>= (@=?) testMsg
client sock serverPort = do
addr <- inet_addr serverAddr
sendAllTo sock testMsg (SockAddrInet serverPort addr)
testSendMany :: Assertion
testSendMany = tcpTest client server
where
server sock = recv sock 1024 >>= (@=?) (S.append seg1 seg2)
client sock = sendMany sock [seg1, seg2]
seg1 = C.pack "This is a "
seg2 = C.pack "test message."
testSendManyTo :: Assertion
testSendManyTo = udpTest client server
where
server sock = recv sock 1024 >>= (@=?) (S.append seg1 seg2)
client sock serverPort = do
addr <- inet_addr serverAddr
sendManyTo sock [seg1, seg2] (SockAddrInet serverPort addr)
seg1 = C.pack "This is a "
seg2 = C.pack "test message."
testRecv :: Assertion
testRecv = tcpTest client server
where
server sock = recv sock 1024 >>= (@=?) testMsg
client sock = send sock testMsg
testOverFlowRecv :: Assertion
testOverFlowRecv = tcpTest client server
where
server sock = do seg1 <- recv sock (S.length testMsg - 3)
seg2 <- recv sock 1024
let msg = S.append seg1 seg2
testMsg @=? msg
client sock = send sock testMsg
testRecvFrom :: Assertion
testRecvFrom = tcpTest client server
where
server sock = do (msg, _) <- recvFrom sock 1024
testMsg @=? msg
client sock = do
serverPort <- getPeerPort sock
addr <- inet_addr serverAddr
sendTo sock testMsg (SockAddrInet serverPort addr)
testOverFlowRecvFrom :: Assertion
testOverFlowRecvFrom = tcpTest client server
where
server sock = do (seg1, _) <- recvFrom sock (S.length testMsg - 3)
(seg2, _) <- recvFrom sock 1024
let msg = S.append seg1 seg2
testMsg @=? msg
client sock = send sock testMsg
testUserTimeout :: Assertion
testUserTimeout = do
when (isSupportedSocketOption UserTimeout) $ do
sock <- socket AF_INET Stream defaultProtocol
setSocketOption sock UserTimeout 1000
getSocketOption sock UserTimeout >>= (@=?) 1000
setSocketOption sock UserTimeout 2000
getSocketOption sock UserTimeout >>= (@=?) 2000
sClose sock
{-
testGetPeerCred:: Assertion
testGetPeerCred =
test clientSetup clientAct serverSetup server
where
clientSetup = do
sock <- socket AF_UNIX Stream defaultProtocol
connect sock $ SockAddrUnix addr
return sock
serverSetup = do
sock <- socket AF_UNIX Stream defaultProtocol
bindSocket sock $ SockAddrUnix addr
listen sock 1
return sock
server sock = do
(clientSock, _) <- accept sock
serverAct clientSock
sClose clientSock
addr = "/tmp/testAddr1"
clientAct sock = withSocketsDo $ do
sendAll sock testMsg
(pid,uid,gid) <- getPeerCred sock
putStrLn $ unwords ["pid=",show pid,"uid=",show uid, "gid=", show gid]
serverAct sock = withSocketsDo $ do
msg <- recv sock 1024
putStrLn $ C.unpack msg
testGetPeerEid :: Assertion
testGetPeerEid =
test clientSetup clientAct serverSetup server
where
clientSetup = do
sock <- socket AF_UNIX Stream defaultProtocol
connect sock $ SockAddrUnix addr
return sock
serverSetup = do
sock <- socket AF_UNIX Stream defaultProtocol
bindSocket sock $ SockAddrUnix addr
listen sock 1
return sock
server sock = do
(clientSock, _) <- accept sock
serverAct clientSock
sClose clientSock
addr = "/tmp/testAddr2"
clientAct sock = withSocketsDo $ do
sendAll sock testMsg
(uid,gid) <- getPeerEid sock
putStrLn $ unwords ["uid=",show uid, "gid=", show gid]
serverAct sock = withSocketsDo $ do
msg <- recv sock 1024
putStrLn $ C.unpack msg
-}
#if defined(HAVE_LINUX_CAN_H)
canTestMsg = S.pack [ 0,0,0,0 -- can ID = 0
, 4,0,0,0 -- data length counter = 2 (bytes)
, 0x80,123,321,55 -- SYNC with some random extra bytes
, 0, 0, 0, 0 -- padding
]
testCanSend :: Assertion
testCanSend = canTest "vcan0" client server
where
server sock = recv sock 1024 >>= (@=?) canTestMsg
client sock = send sock canTestMsg
canTest :: String -> (Socket -> IO a) -> (Socket -> IO b) -> IO ()
canTest ifname clientAct serverAct = do
ifIndex <- liftM fromJust $ ifNameToIndex ifname
test (clientSetup ifIndex) clientAct (serverSetup ifIndex) serverAct
where
clientSetup ifIndex = do
sock <- socket AF_CAN Raw 1 -- protocol 1 = raw CAN
-- bind the socket to the interface
bind sock (SockAddrCan $ fromIntegral $ ifIndex)
return sock
serverSetup = clientSetup
#endif
------------------------------------------------------------------------
-- Other
------------------------------------------------------------------------
-- List of all tests
basicTests :: Test
basicTests = testGroup "Basic socket operations"
[
-- Sending and receiving
testCase "testSend" testSend
, testCase "testSendAll" testSendAll
, testCase "testSendTo" testSendTo
, testCase "testSendAllTo" testSendAllTo
, testCase "testSendMany" testSendMany
, testCase "testSendManyTo" testSendManyTo
, testCase "testRecv" testRecv
, testCase "testOverFlowRecv" testOverFlowRecv
, testCase "testRecvFrom" testRecvFrom
, testCase "testOverFlowRecvFrom" testOverFlowRecvFrom
, testCase "testUserTimeout" testUserTimeout
-- , testCase "testGetPeerCred" testGetPeerCred
-- , testCase "testGetPeerEid" testGetPeerEid
#if defined(HAVE_LINUX_CAN_H)
, testCase "testCanSend" testCanSend
#endif
]
tests :: [Test]
tests = [basicTests]
------------------------------------------------------------------------
-- Test helpers
-- | Returns the 'PortNumber' of the peer. Will throw an 'error' if
-- used on a non-IP socket.
getPeerPort :: Socket -> IO PortNumber
getPeerPort sock = do
sockAddr <- getPeerName sock
case sockAddr of
(SockAddrInet port _) -> return port
(SockAddrInet6 port _ _ _) -> return port
_ -> error "getPeerPort: only works with IP sockets"
-- | Establish a connection between client and server and then run
-- 'clientAct' and 'serverAct', in different threads. Both actions
-- get passed a connected 'Socket', used for communicating between
-- client and server. 'tcpTest' makes sure that the 'Socket' is
-- closed after the actions have run.
tcpTest :: (Socket -> IO a) -> (Socket -> IO b) -> IO ()
tcpTest clientAct serverAct = do
portVar <- newEmptyMVar
test (clientSetup portVar) clientAct (serverSetup portVar) server
where
clientSetup portVar = do
sock <- socket AF_INET Stream defaultProtocol
addr <- inet_addr serverAddr
serverPort <- readMVar portVar
connect sock $ SockAddrInet serverPort addr
return sock
serverSetup portVar = do
sock <- socket AF_INET Stream defaultProtocol
setSocketOption sock ReuseAddr 1
addr <- inet_addr serverAddr
bindSocket sock (SockAddrInet aNY_PORT addr)
listen sock 1
serverPort <- socketPort sock
putMVar portVar serverPort
return sock
server sock = do
(clientSock, _) <- accept sock
serverAct clientSock
sClose clientSock
-- | Create an unconnected 'Socket' for sending UDP and receiving
-- datagrams and then run 'clientAct' and 'serverAct'.
udpTest :: (Socket -> PortNumber -> IO a) -> (Socket -> IO b) -> IO ()
udpTest clientAct serverAct = do
portVar <- newEmptyMVar
test clientSetup (client portVar) (serverSetup portVar) serverAct
where
clientSetup = socket AF_INET Datagram defaultProtocol
client portVar sock = do
serverPort <- readMVar portVar
clientAct sock serverPort
serverSetup portVar = do
sock <- socket AF_INET Datagram defaultProtocol
setSocketOption sock ReuseAddr 1
addr <- inet_addr serverAddr
bindSocket sock (SockAddrInet aNY_PORT addr)
serverPort <- socketPort sock
putMVar portVar serverPort
return sock
-- | Run a client/server pair and synchronize them so that the server
-- is started before the client and the specified server action is
-- finished before the client closes the 'Socket'.
test :: IO Socket -> (Socket -> IO b) -> IO Socket -> (Socket -> IO c) -> IO ()
test clientSetup clientAct serverSetup serverAct = do
tid <- myThreadId
barrier <- newEmptyMVar
forkIO $ server barrier
client tid barrier
where
server barrier = do
E.bracket serverSetup sClose $ \sock -> do
serverReady
serverAct sock
putMVar barrier ()
where
-- | Signal to the client that it can proceed.
serverReady = putMVar barrier ()
client tid barrier = do
takeMVar barrier
-- Transfer exceptions to the main thread.
bracketWithReraise tid clientSetup sClose $ \res -> do
clientAct res
takeMVar barrier
-- | Like 'bracket' but catches and reraises the exception in another
-- thread, specified by the first argument.
bracketWithReraise :: ThreadId -> IO a -> (a -> IO b) -> (a -> IO ()) -> IO ()
bracketWithReraise tid before after thing =
E.bracket before after thing
`E.catch` \ (e :: E.SomeException) -> E.throwTo tid e
------------------------------------------------------------------------
-- Test harness
main :: IO ()
main = withSocketsDo $ defaultMain tests
|