summaryrefslogtreecommitdiff
path: root/src/Network/BitTorrent
diff options
context:
space:
mode:
Diffstat (limited to 'src/Network/BitTorrent')
-rw-r--r--src/Network/BitTorrent/DHT/Routing.hs82
1 files changed, 47 insertions, 35 deletions
diff --git a/src/Network/BitTorrent/DHT/Routing.hs b/src/Network/BitTorrent/DHT/Routing.hs
index 64c7bbee..984a61cc 100644
--- a/src/Network/BitTorrent/DHT/Routing.hs
+++ b/src/Network/BitTorrent/DHT/Routing.hs
@@ -95,24 +95,30 @@ insert ping (k, v) = go 0
95-} 95-}
96 96
97{----------------------------------------------------------------------- 97{-----------------------------------------------------------------------
98-- Insertion 98-- Routing monad
99-----------------------------------------------------------------------} 99-----------------------------------------------------------------------}
100 100
101type Timestamp = POSIXTime 101type Timestamp = POSIXTime
102 102
103data Routing ip result 103data Routing ip result
104 = Full result 104 = Done result
105 | Done (Timestamp -> result) 105 | GetTime ( Timestamp -> Routing ip result)
106 | GetTime ( Timestamp -> Routing ip result) 106 | NeedPing (NodeAddr ip) ( Bool -> Routing ip result)
107 | Refresh NodeId (([NodeInfo ip], Timestamp) -> Routing ip result) 107 | Refresh NodeId ([NodeInfo ip] -> Routing ip result)
108 | NeedPing (NodeAddr ip) (Maybe Timestamp -> Routing ip result)
109 108
110instance Functor (Routing ip) where 109instance Functor (Routing ip) where
111 fmap f (Full r) = Full ( f r) 110 fmap f (Done r) = Done ( f r)
112 fmap f (Done r) = Done ( f . r)
113 fmap f (GetTime g) = GetTime (fmap f . g) 111 fmap f (GetTime g) = GetTime (fmap f . g)
114 fmap f (Refresh addr g) = Refresh addr (fmap f . g)
115 fmap f (NeedPing addr g) = NeedPing addr (fmap f . g) 112 fmap f (NeedPing addr g) = NeedPing addr (fmap f . g)
113 fmap f (Refresh nid g) = Refresh nid (fmap f . g)
114
115instance Monad (Routing ip) where
116 return = Done
117
118 Done r >>= m = m r
119 GetTime f >>= m = GetTime $ \ t -> f t >>= m
120 NeedPing a f >>= m = NeedPing a $ \ p -> f p >>= m
121 Refresh n f >>= m = Refresh n $ \ i -> f i >>= m
116 122
117runRouting :: (Monad m, Eq ip) 123runRouting :: (Monad m, Eq ip)
118 => (NodeAddr ip -> m Bool) -- ^ ping_node 124 => (NodeAddr ip -> m Bool) -- ^ ping_node
@@ -122,24 +128,27 @@ runRouting :: (Monad m, Eq ip)
122 -> m f -- ^ result 128 -> m f -- ^ result
123runRouting ping_node find_nodes timestamper = go 129runRouting ping_node find_nodes timestamper = go
124 where 130 where
125 go (Full r) = return r 131 go (Done r) = return r
126 go (Done f) = liftM f timestamper
127 go (GetTime f) = do 132 go (GetTime f) = do
128 t <- timestamper 133 t <- timestamper
129 go (f t) 134 go (f t)
130 135
131 go (NeedPing addr f) = do 136 go (NeedPing addr f) = do
132 pong <- ping_node addr 137 pong <- ping_node addr
133 if pong 138 go (f pong)
134 then do
135 time <- timestamper
136 go (f (Just time))
137 else go (f Nothing)
138 139
139 go (Refresh nid f) = do 140 go (Refresh nid f) = do
140 infos <- find_nodes nid 141 infos <- find_nodes nid
141 time <- timestamper 142 go (f infos)
142 go (f (infos, time)) 143
144getTime :: Routing ip Timestamp
145getTime = GetTime return
146
147needPing :: NodeAddr ip -> Routing ip Bool
148needPing addr = NeedPing addr return
149
150refresh :: NodeId -> Routing ip [NodeInfo ip]
151refresh nid = Refresh nid return
143 152
144{----------------------------------------------------------------------- 153{-----------------------------------------------------------------------
145 Bucket 154 Bucket
@@ -197,34 +206,37 @@ insertBucket :: Eq ip => Timestamp -> NodeInfo ip -> Bucket ip
197 -> ip `Routing` Bucket ip 206 -> ip `Routing` Bucket ip
198insertBucket curTime info bucket 207insertBucket curTime info bucket
199 -- just update timestamp if a node is already in bucket 208 -- just update timestamp if a node is already in bucket
200 | Just _ <- PSQ.lookup info bucket 209 | Just _ <- PSQ.lookup info bucket = do
201 = Done $ \ t -> PSQ.insertWith max info t bucket 210 return $ PSQ.insertWith max info curTime bucket
202 211
203 -- update the all bucket if it is too outdated 212 -- update the all bucket if it is too outdated
204 | Just (NodeInfo {..} :-> lastSeen) <- lastChanged bucket 213 | Just (NodeInfo {..} :-> lastSeen) <- lastChanged bucket
205 , curTime - lastSeen > delta 214 , curTime - lastSeen > delta = do
206 = Refresh nodeId $ \ (infos, t) -> 215 infos <- refresh nodeId
207 insertNode info $ 216 refTime <- getTime
208 L.foldr (\ x -> PSQ.insertWith max x t) bucket infos 217 let newBucket = L.foldr (\ x -> PSQ.insertWith max x refTime) bucket infos
218 insertBucket refTime info newBucket
209 219
210 -- update questionable nodes, if any; then try to insert our new node 220 -- update questionable nodes, if any; then try to insert our new node
211 -- this case can remove bad nodes from bucket, so we can insert a new one 221 -- this case can remove bad nodes from bucket, so we can insert a new one
212 | Just ((old @ NodeInfo {..} :-> leastSeen), rest) <- leastRecently bucket 222 | Just ((old @ NodeInfo {..} :-> leastSeen), rest) <- leastRecently bucket
213 , curTime - leastSeen > delta 223 , curTime - leastSeen > delta = do
214 = NeedPing nodeAddr $ \ mtime -> 224 pong <- needPing nodeAddr
215 insertNode info $ 225 pongTime <- getTime
216 case mtime of 226 let newBucket = if pong then PSQ.insert old pongTime bucket else rest
217 Nothing -> rest 227 insertBucket pongTime info newBucket
218 Just pongTime -> PSQ.insert old pongTime bucket
219 228
220 -- bucket is good, but not full => we can insert a new node 229 -- bucket is good, but not full => we can insert a new node
221 | PSQ.size bucket < defaultAlpha = Done (\ t -> PSQ.insert info t bucket) 230 | PSQ.size bucket < defaultAlpha = do
231 return $ PSQ.insert info curTime bucket
222 232
223 -- bucket is full of good nodes => ignore new node 233 -- bucket is full of good nodes => ignore new node
224 | otherwise = Full bucket 234 | otherwise = return bucket
225 235
226insertNode :: Eq ip => NodeInfo ip -> Bucket ip -> ip `Routing` Bucket ip 236insertNode :: Eq ip => NodeInfo ip -> Bucket ip -> ip `Routing` Bucket ip
227insertNode info bucket = GetTime $ \ curTime -> insertBucket curTime info bucket 237insertNode info bucket = do
238 curTime <- getTime
239 insertBucket curTime info bucket
228 240
229type BitIx = Word 241type BitIx = Word
230 242
@@ -337,7 +349,7 @@ insert info @ NodeInfo {..} = go (0 :: BitIx)
337 | testIdBit nodeId i = One bucket <$> go (succ i) table 349 | testIdBit nodeId i = One bucket <$> go (succ i) table
338 | otherwise = (`One` table) <$> insertNode info bucket 350 | otherwise = (`One` table) <$> insertNode info bucket
339 go i (Tip nid n bucket) = case insertNode info bucket of 351 go i (Tip nid n bucket) = case insertNode info bucket of
340 Full kbucket 352 Done kbucket
341 | n == 0 -> Tip nid n <$> Full kbucket 353 | n == 0 -> Tip nid n <$> Done kbucket
342 | otherwise -> go (succ i) (splitTip nid n i kbucket) 354 | otherwise -> go (succ i) (splitTip nid n i kbucket)
343 result -> Tip nid n <$> result 355 result -> Tip nid n <$> result