summaryrefslogtreecommitdiff
path: root/src/Network/BitTorrent/Tracker.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Network/BitTorrent/Tracker.hs')
-rw-r--r--src/Network/BitTorrent/Tracker.hs103
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
42import Data.Torrent.Metainfo 42import Data.Torrent.Metainfo
43import Network.BitTorrent.Peer 43import Network.BitTorrent.Peer
44import Network.BitTorrent.Tracker.Protocol 44import Network.BitTorrent.Tracker.Protocol as Tracker
45import Network.BitTorrent.Tracker.HTTP 45import 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--
85startedReq :: TConnection -> Progress -> AnnounceQuery 85startedReq :: TConnection -> Progress -> AnnounceQuery
86startedReq ses pr = (genericReq ses pr) { 86startedReq 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--
96regularReq :: Int -> TConnection -> Progress -> AnnounceQuery 95regularReq :: Int -> TConnection -> Progress -> AnnounceQuery
97regularReq numWant ses pr = (genericReq ses pr) { 96regularReq 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--
106stoppedReq :: TConnection -> Progress -> AnnounceQuery 104stoppedReq :: TConnection -> Progress -> AnnounceQuery
107stoppedReq ses pr = (genericReq ses pr) { 105stoppedReq 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--
117completedReq :: TConnection -> Progress -> AnnounceQuery 114completedReq :: TConnection -> Progress -> AnnounceQuery
118completedReq ses pr = (genericReq ses pr) { 115completedReq 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
158type PeerCount = Int 155type PeerCount = Int
@@ -167,8 +164,9 @@ getProgress :: TSession -> IO Progress
167getProgress = readTVarIO . seProgress 164getProgress = readTVarIO . seProgress
168 165
169newSession :: PeerCount -> Progress -> TimeInterval -> [PeerAddr] 166newSession :: PeerCount -> Progress -> TimeInterval -> [PeerAddr]
167 -> HTTPTracker
170 -> IO TSession 168 -> IO TSession
171newSession chanSize pr i ps 169newSession 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
187waitInterval :: TSession -> IO () 186waitInterval :: TSession -> IO ()
188waitInterval TSession {..} = do 187waitInterval 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
193announceLoop :: IO (BoundedChan PeerAddr)
194announceLoop = undefined
195
196openSession :: Progress -> TConnection -> IO TSession
197openSession 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
203closeSession :: TConnection -> TSession -> IO ()
204closeSession conn se @ TSession {..} = do
205 pr <- getProgress se
206 Tracker.announce seTracker (stoppedReq conn pr)
207 return ()
208
209syncSession :: TConnection -> TSession -> IO ()
210syncSession 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
194withTracker :: Progress -> TConnection -> (TSession -> IO a) -> IO a 230withTracker :: Progress -> TConnection -> (TSession -> IO a) -> IO a
195withTracker initProgress conn action = bracket start end (action . fst) 231withTracker 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)