diff options
Diffstat (limited to 'src/Network/BitTorrent/Tracker.hs')
-rw-r--r-- | src/Network/BitTorrent/Tracker.hs | 103 |
1 files changed, 54 insertions, 49 deletions
diff --git a/src/Network/BitTorrent/Tracker.hs b/src/Network/BitTorrent/Tracker.hs index c707cedd..0501f428 100644 --- a/src/Network/BitTorrent/Tracker.hs +++ b/src/Network/BitTorrent/Tracker.hs | |||
@@ -41,7 +41,7 @@ import Network.URI | |||
41 | 41 | ||
42 | import Data.Torrent.Metainfo | 42 | import Data.Torrent.Metainfo |
43 | import Network.BitTorrent.Peer | 43 | import Network.BitTorrent.Peer |
44 | import Network.BitTorrent.Tracker.Protocol | 44 | import Network.BitTorrent.Tracker.Protocol as Tracker |
45 | import Network.BitTorrent.Tracker.HTTP | 45 | import Network.BitTorrent.Tracker.HTTP |
46 | 46 | ||
47 | {----------------------------------------------------------------------- | 47 | {----------------------------------------------------------------------- |
@@ -83,9 +83,8 @@ genericReq ses pr = AnnounceQuery { | |||
83 | -- 'startedReq'. It includes necessary 'Started' event field. | 83 | -- 'startedReq'. It includes necessary 'Started' event field. |
84 | -- | 84 | -- |
85 | startedReq :: TConnection -> Progress -> AnnounceQuery | 85 | startedReq :: TConnection -> Progress -> AnnounceQuery |
86 | startedReq ses pr = (genericReq ses pr) { | 86 | startedReq ses pr = (genericReq ses pr) |
87 | reqIP = Nothing | 87 | { reqNumWant = Just defaultNumWant |
88 | , reqNumWant = Just defaultNumWant | ||
89 | , reqEvent = Just Started | 88 | , reqEvent = Just Started |
90 | } | 89 | } |
91 | 90 | ||
@@ -94,9 +93,8 @@ startedReq ses pr = (genericReq ses pr) { | |||
94 | -- so new peers could connect to the client. | 93 | -- so new peers could connect to the client. |
95 | -- | 94 | -- |
96 | regularReq :: Int -> TConnection -> Progress -> AnnounceQuery | 95 | regularReq :: Int -> TConnection -> Progress -> AnnounceQuery |
97 | regularReq numWant ses pr = (genericReq ses pr) { | 96 | regularReq numWant ses pr = (genericReq ses pr) |
98 | reqIP = Nothing | 97 | { reqNumWant = Just numWant |
99 | , reqNumWant = Just numWant | ||
100 | , reqEvent = Nothing | 98 | , reqEvent = Nothing |
101 | } | 99 | } |
102 | 100 | ||
@@ -104,9 +102,8 @@ regularReq numWant ses pr = (genericReq ses pr) { | |||
104 | -- gracefully. | 102 | -- gracefully. |
105 | -- | 103 | -- |
106 | stoppedReq :: TConnection -> Progress -> AnnounceQuery | 104 | stoppedReq :: TConnection -> Progress -> AnnounceQuery |
107 | stoppedReq ses pr = (genericReq ses pr) { | 105 | stoppedReq ses pr = (genericReq ses pr) |
108 | reqIP = Nothing | 106 | { reqNumWant = Nothing |
109 | , reqNumWant = Nothing | ||
110 | , reqEvent = Just Stopped | 107 | , reqEvent = Just Stopped |
111 | } | 108 | } |
112 | 109 | ||
@@ -115,9 +112,8 @@ stoppedReq ses pr = (genericReq ses pr) { | |||
115 | -- complete. | 112 | -- complete. |
116 | -- | 113 | -- |
117 | completedReq :: TConnection -> Progress -> AnnounceQuery | 114 | completedReq :: TConnection -> Progress -> AnnounceQuery |
118 | completedReq ses pr = (genericReq ses pr) { | 115 | completedReq ses pr = (genericReq ses pr) |
119 | reqIP = Nothing | 116 | { reqNumWant = Nothing |
120 | , reqNumWant = Nothing | ||
121 | , reqEvent = Just Completed | 117 | , reqEvent = Just Completed |
122 | } | 118 | } |
123 | 119 | ||
@@ -153,6 +149,7 @@ data TSession = TSession { | |||
153 | seProgress :: TVar Progress | 149 | seProgress :: TVar Progress |
154 | , seInterval :: IORef TimeInterval | 150 | , seInterval :: IORef TimeInterval |
155 | , sePeers :: BoundedChan PeerAddr | 151 | , sePeers :: BoundedChan PeerAddr |
152 | , seTracker :: HTTPTracker | ||
156 | } | 153 | } |
157 | 154 | ||
158 | type PeerCount = Int | 155 | type PeerCount = Int |
@@ -167,8 +164,9 @@ getProgress :: TSession -> IO Progress | |||
167 | getProgress = readTVarIO . seProgress | 164 | getProgress = readTVarIO . seProgress |
168 | 165 | ||
169 | newSession :: PeerCount -> Progress -> TimeInterval -> [PeerAddr] | 166 | newSession :: PeerCount -> Progress -> TimeInterval -> [PeerAddr] |
167 | -> HTTPTracker | ||
170 | -> IO TSession | 168 | -> IO TSession |
171 | newSession chanSize pr i ps | 169 | newSession chanSize pr i ps tr |
172 | | chanSize < 1 | 170 | | chanSize < 1 |
173 | = throwIO $ userError "size of chan should be more that 1" | 171 | = throwIO $ userError "size of chan should be more that 1" |
174 | 172 | ||
@@ -183,6 +181,7 @@ newSession chanSize pr i ps | |||
183 | TSession <$> newTVarIO pr | 181 | TSession <$> newTVarIO pr |
184 | <*> newIORef i | 182 | <*> newIORef i |
185 | <*> pure chan | 183 | <*> pure chan |
184 | <*> pure tr | ||
186 | 185 | ||
187 | waitInterval :: TSession -> IO () | 186 | waitInterval :: TSession -> IO () |
188 | waitInterval TSession {..} = do | 187 | waitInterval TSession {..} = do |
@@ -191,39 +190,45 @@ waitInterval TSession {..} = do | |||
191 | where | 190 | where |
192 | sec = 1000 * 1000 :: Int | 191 | sec = 1000 * 1000 :: Int |
193 | 192 | ||
193 | announceLoop :: IO (BoundedChan PeerAddr) | ||
194 | announceLoop = undefined | ||
195 | |||
196 | openSession :: Progress -> TConnection -> IO TSession | ||
197 | openSession initProgress conn = do | ||
198 | t <- Tracker.connect (tconnAnnounce conn) | ||
199 | resp <- Tracker.announce t (startedReq conn initProgress) | ||
200 | newSession defaultChanSize initProgress | ||
201 | (respInterval resp) (respPeers resp) t | ||
202 | |||
203 | closeSession :: TConnection -> TSession -> IO () | ||
204 | closeSession conn se @ TSession {..} = do | ||
205 | pr <- getProgress se | ||
206 | Tracker.announce seTracker (stoppedReq conn pr) | ||
207 | return () | ||
208 | |||
209 | syncSession :: TConnection -> TSession -> IO () | ||
210 | syncSession conn se @ TSession {..} = forever $ do | ||
211 | waitInterval se | ||
212 | pr <- getProgress se | ||
213 | resp <- tryJust isIOException $ do | ||
214 | Tracker.announce seTracker (regularReq defaultNumWant conn pr) | ||
215 | case resp of | ||
216 | Left _ -> return () | ||
217 | Right (AnnounceInfo {..}) -> do | ||
218 | writeIORef seInterval respInterval | ||
219 | |||
220 | -- we rely on the fact that union on lists is not | ||
221 | -- commutative: this implements the heuristic "old peers | ||
222 | -- in head" | ||
223 | old <- BC.getChanContents sePeers | ||
224 | let combined = L.union old respPeers | ||
225 | BC.writeList2Chan sePeers combined | ||
226 | where | ||
227 | isIOException :: IOException -> Maybe IOException | ||
228 | isIOException = return | ||
229 | |||
194 | withTracker :: Progress -> TConnection -> (TSession -> IO a) -> IO a | 230 | withTracker :: Progress -> TConnection -> (TSession -> IO a) -> IO a |
195 | withTracker initProgress conn action = bracket start end (action . fst) | 231 | withTracker initProgress conn |
196 | where | 232 | = bracket |
197 | start = do | 233 | (openSession initProgress conn) |
198 | resp <- askTracker (tconnAnnounce conn) (startedReq conn initProgress) | 234 | (closeSession conn) |
199 | se <- newSession defaultChanSize initProgress | ||
200 | (respInterval resp) (respPeers resp) | ||
201 | |||
202 | tid <- forkIO (syncSession se) | ||
203 | return (se, tid) | ||
204 | |||
205 | syncSession se @ TSession {..} = forever $ do | ||
206 | waitInterval se | ||
207 | pr <- getProgress se | ||
208 | resp <- tryJust isIOException $ do | ||
209 | askTracker (tconnAnnounce conn) (regularReq defaultNumWant conn pr) | ||
210 | case resp of | ||
211 | Right (AnnounceInfo {..}) -> do | ||
212 | writeIORef seInterval respInterval | ||
213 | |||
214 | -- we rely on the fact that union on lists is not | ||
215 | -- commutative: this implements the heuristic "old peers | ||
216 | -- in head" | ||
217 | old <- BC.getChanContents sePeers | ||
218 | let combined = L.union old respPeers | ||
219 | BC.writeList2Chan sePeers combined | ||
220 | |||
221 | _ -> return () | ||
222 | where | ||
223 | isIOException :: IOException -> Maybe IOException | ||
224 | isIOException = return | ||
225 | |||
226 | end (se, tid) = do | ||
227 | killThread tid | ||
228 | pr <- getProgress se | ||
229 | leaveTracker (tconnAnnounce conn) (stoppedReq conn pr) | ||