File: Simple.hs

package info (click to toggle)
haskell-network 2.6.2.1-3~bpo8%2B1
  • links: PTS, VCS
  • area: main
  • in suites: jessie-backports
  • size: 584 kB
  • sloc: sh: 3,016; haskell: 701; ansic: 394; makefile: 3
file content (365 lines) | stat: -rw-r--r-- 11,956 bytes parent folder | download
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