diff options
Diffstat (limited to 'src/Network/BitTorrent')
-rw-r--r-- | src/Network/BitTorrent/DHT/Routing.hs | 82 |
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 | ||
101 | type Timestamp = POSIXTime | 101 | type Timestamp = POSIXTime |
102 | 102 | ||
103 | data Routing ip result | 103 | data 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 | ||
110 | instance Functor (Routing ip) where | 109 | instance 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 | |||
115 | instance 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 | ||
117 | runRouting :: (Monad m, Eq ip) | 123 | runRouting :: (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 |
123 | runRouting ping_node find_nodes timestamper = go | 129 | runRouting 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 | |
144 | getTime :: Routing ip Timestamp | ||
145 | getTime = GetTime return | ||
146 | |||
147 | needPing :: NodeAddr ip -> Routing ip Bool | ||
148 | needPing addr = NeedPing addr return | ||
149 | |||
150 | refresh :: NodeId -> Routing ip [NodeInfo ip] | ||
151 | refresh 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 |
198 | insertBucket curTime info bucket | 207 | insertBucket 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 | ||
226 | insertNode :: Eq ip => NodeInfo ip -> Bucket ip -> ip `Routing` Bucket ip | 236 | insertNode :: Eq ip => NodeInfo ip -> Bucket ip -> ip `Routing` Bucket ip |
227 | insertNode info bucket = GetTime $ \ curTime -> insertBucket curTime info bucket | 237 | insertNode info bucket = do |
238 | curTime <- getTime | ||
239 | insertBucket curTime info bucket | ||
228 | 240 | ||
229 | type BitIx = Word | 241 | type 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 |