summaryrefslogtreecommitdiff
path: root/src/Network/SessionTransports.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Network/SessionTransports.hs')
-rw-r--r--src/Network/SessionTransports.hs97
1 files changed, 97 insertions, 0 deletions
diff --git a/src/Network/SessionTransports.hs b/src/Network/SessionTransports.hs
new file mode 100644
index 00000000..17763e4e
--- /dev/null
+++ b/src/Network/SessionTransports.hs
@@ -0,0 +1,97 @@
1{-# LANGUAGE NamedFieldPuns #-}
2module Network.SessionTransports
3 ( Sessions
4 , initSessions
5 , newSession
6 , sessionHandler
7 ) where
8
9import Control.Concurrent
10import Control.Concurrent.STM
11import Control.Monad
12import qualified Data.IntMap.Strict as IntMap
13 ;import Data.IntMap.Strict (IntMap)
14import qualified Data.Map.Strict as Map
15 ;import Data.Map.Strict (Map)
16
17import Network.Address (SockAddr,either4or6)
18import Network.QueryResponse
19import qualified Data.IntervalSet as S
20 ;import Data.IntervalSet (IntSet)
21
22data Sessions x = Sessions
23 { sessionsByAddr :: TVar (Map SockAddr (IntMap (x -> IO Bool)))
24 , sessionsById :: TVar (IntMap SockAddr)
25 , sessionIds :: TVar IntSet
26 , sessionsSendRaw :: SockAddr -> x -> IO ()
27 }
28
29initSessions :: (SockAddr -> x -> IO ()) -> IO (Sessions x)
30initSessions send = atomically $ do
31 byaddr <- newTVar Map.empty
32 byid <- newTVar IntMap.empty
33 idset <- newTVar S.empty
34 return Sessions { sessionsByAddr = byaddr
35 , sessionsById = byid
36 , sessionIds = idset
37 , sessionsSendRaw = send
38 }
39
40
41
42rmSession :: Int -> (Maybe (IntMap x)) -> (Maybe (IntMap x))
43rmSession sid Nothing = Nothing
44rmSession sid (Just m) = case IntMap.delete sid m of
45 m' | IntMap.null m' -> Nothing
46 | otherwise -> Just m'
47
48newSession :: Sessions raw
49 -> (addr -> y -> IO raw)
50 -> (SockAddr -> raw -> IO (Maybe (x, addr)))
51 -> SockAddr
52 -> IO (Maybe (TransportA err addr x y))
53newSession Sessions{sessionsByAddr,sessionsById,sessionIds,sessionsSendRaw} unwrap wrap addr0 = do
54 mvar <- newEmptyMVar
55 let saddr = -- Canonical in case of 6-mapped-4 addresses.
56 either id id $ either4or6 addr0
57 handlePacket x = do
58 m <- wrap saddr x
59 case m of
60 Nothing -> return False
61 Just x' -> do putMVar mvar $! Just $! x'
62 return True
63 msid <- atomically $ do
64 msid <- S.nearestOutsider 0 <$> readTVar sessionIds
65 forM msid $ \sid -> do
66 modifyTVar' sessionIds $ S.insert sid
67 modifyTVar' sessionsById $ IntMap.insert sid saddr
68 modifyTVar' sessionsByAddr $ Map.insertWith IntMap.union saddr
69 $ IntMap.singleton sid handlePacket
70 return sid
71 forM msid $ \sid -> do
72 return Transport
73 { awaitMessage = \kont -> do
74 x <- takeMVar mvar
75 kont $! Right <$> x
76 , sendMessage = \addr x -> do
77 x' <- unwrap addr x
78 sessionsSendRaw saddr x'
79 , closeTransport = do
80 tryTakeMVar mvar
81 putMVar mvar Nothing
82 atomically $ do
83 modifyTVar' sessionIds $ S.delete sid
84 modifyTVar' sessionsById $ IntMap.delete sid
85 modifyTVar' sessionsByAddr $ Map.alter (rmSession sid) saddr
86 }
87
88sessionHandler :: Sessions x -> (SockAddr -> x -> IO (Maybe (x -> x)))
89sessionHandler Sessions{sessionsByAddr} = \addr0 x -> do
90 let addr = -- Canonical in case of 6-mapped-4 addresses.
91 either id id $ either4or6 addr0
92 dispatch [] = return ()
93 dispatch (f:fs) = do b <- f x
94 when (not b) $ dispatch fs
95 fs <- atomically $ Map.lookup addr <$> readTVar sessionsByAddr
96 mapM_ (dispatch . IntMap.elems) fs
97 return Nothing -- consume all packets.