diff options
author | Sam Truzjan <pxqr.sta@gmail.com> | 2014-03-17 22:38:23 +0400 |
---|---|---|
committer | Sam Truzjan <pxqr.sta@gmail.com> | 2014-03-17 22:38:23 +0400 |
commit | ba0c765b9e47335e2833f1ec72b3843792e0718d (patch) | |
tree | 643260d4c4f9a3c8ecfe287f96430e025eb41587 /src/Network/BitTorrent/Tracker | |
parent | b7761dde5a330257d4cdacf3d8098caf552f6cce (diff) |
Refactor single tracker session
Diffstat (limited to 'src/Network/BitTorrent/Tracker')
-rw-r--r-- | src/Network/BitTorrent/Tracker/Session.hs | 121 |
1 files changed, 63 insertions, 58 deletions
diff --git a/src/Network/BitTorrent/Tracker/Session.hs b/src/Network/BitTorrent/Tracker/Session.hs index 33a46898..e13bc6f0 100644 --- a/src/Network/BitTorrent/Tracker/Session.hs +++ b/src/Network/BitTorrent/Tracker/Session.hs | |||
@@ -51,18 +51,9 @@ import Network.BitTorrent.Tracker.Message | |||
51 | import Network.BitTorrent.Tracker.RPC as RPC | 51 | import Network.BitTorrent.Tracker.RPC as RPC |
52 | 52 | ||
53 | {----------------------------------------------------------------------- | 53 | {----------------------------------------------------------------------- |
54 | -- Tracker entry | 54 | -- Single tracker session |
55 | -----------------------------------------------------------------------} | 55 | -----------------------------------------------------------------------} |
56 | 56 | ||
57 | data LastScrape = LastScrape | ||
58 | { leechersCount :: Maybe Int | ||
59 | , seedersCount :: Maybe Int | ||
60 | } deriving (Show, Eq) | ||
61 | |||
62 | -- | Tracker session starts with scrape unknown. | ||
63 | instance Default LastScrape where | ||
64 | def = LastScrape Nothing Nothing | ||
65 | |||
66 | -- | Status of this client. | 57 | -- | Status of this client. |
67 | data Status | 58 | data Status |
68 | = Running -- ^ This client is announced and listenning for incoming | 59 | = Running -- ^ This client is announced and listenning for incoming |
@@ -74,29 +65,14 @@ data Status | |||
74 | instance Default Status where | 65 | instance Default Status where |
75 | def = Paused | 66 | def = Paused |
76 | 67 | ||
77 | -- | Client status after event announce succeed. | 68 | -- | Tracker session starts with scrape unknown. |
78 | nextStatus :: Maybe Event -> Status | 69 | instance Default LastScrape where |
79 | nextStatus Nothing = Running | 70 | def = LastScrape Nothing Nothing |
80 | nextStatus (Just Started ) = Running | ||
81 | nextStatus (Just Stopped ) = Paused | ||
82 | nextStatus (Just Completed) = Running | ||
83 | |||
84 | -- | Do we need to notify this /specific/ tracker? | ||
85 | needNotify :: Maybe Event -> Maybe Status -> Bool | ||
86 | needNotify Nothing _ = True | ||
87 | needNotify (Just Started) Nothing = True | ||
88 | needNotify (Just Stopped) Nothing = False | ||
89 | needNotify (Just Completed) Nothing = False | ||
90 | needNotify Nothing (Just Running) = True | ||
91 | needNotify Nothing (Just Paused ) = True | ||
92 | 71 | ||
93 | -- | Do we need to sent this event to a first working tracker or to | 72 | data LastScrape = LastScrape |
94 | -- the all known good trackers? | 73 | { leechersCount :: Maybe Int |
95 | allNotify :: Maybe Event -> Bool | 74 | , seedersCount :: Maybe Int |
96 | allNotify Nothing = False | 75 | } deriving (Show, Eq) |
97 | allNotify (Just Started) = False | ||
98 | allNotify (Just Stopped) = True | ||
99 | allNotify (Just Completed) = True | ||
100 | 76 | ||
101 | -- | Single tracker session. | 77 | -- | Single tracker session. |
102 | data TrackerEntry = TrackerEntry | 78 | data TrackerEntry = TrackerEntry |
@@ -113,9 +89,55 @@ data TrackerEntry = TrackerEntry | |||
113 | , scrapeCache :: Cached LastScrape | 89 | , scrapeCache :: Cached LastScrape |
114 | } | 90 | } |
115 | 91 | ||
92 | -- | Single tracker session with empty state. | ||
116 | nullEntry :: URI -> TrackerEntry | 93 | nullEntry :: URI -> TrackerEntry |
117 | nullEntry uri = TrackerEntry uri Nothing def def | 94 | nullEntry uri = TrackerEntry uri Nothing def def |
118 | 95 | ||
96 | -- | Do we need to notify this /specific/ tracker? | ||
97 | needNotify :: Maybe Event -> Maybe Status -> Bool | ||
98 | needNotify Nothing _ = True | ||
99 | needNotify (Just Started) Nothing = True | ||
100 | needNotify (Just Stopped) Nothing = False | ||
101 | needNotify (Just Completed) Nothing = False | ||
102 | needNotify Nothing (Just Running) = True | ||
103 | needNotify Nothing (Just Paused ) = True | ||
104 | |||
105 | -- | Client status after event announce succeed. | ||
106 | nextStatus :: Maybe Event -> Status | ||
107 | nextStatus Nothing = Running | ||
108 | nextStatus (Just Started ) = Running | ||
109 | nextStatus (Just Stopped ) = Paused | ||
110 | nextStatus (Just Completed) = Running | ||
111 | |||
112 | seconds :: Int -> NominalDiffTime | ||
113 | seconds n = realToFrac (toEnum n :: Uni) | ||
114 | |||
115 | cachePeers :: AnnounceInfo -> IO (Cached [PeerAddr IP]) | ||
116 | cachePeers AnnounceInfo {..} = | ||
117 | newCached (seconds respInterval) | ||
118 | (seconds (fromMaybe respInterval respMinInterval)) | ||
119 | (getPeerList respPeers) | ||
120 | |||
121 | cacheScrape :: AnnounceInfo -> IO (Cached LastScrape) | ||
122 | cacheScrape AnnounceInfo {..} = | ||
123 | newCached (seconds respInterval) | ||
124 | (seconds (fromMaybe respInterval respMinInterval)) | ||
125 | LastScrape | ||
126 | { seedersCount = respComplete | ||
127 | , leechersCount = respIncomplete | ||
128 | } | ||
129 | |||
130 | -- | Make announce request to specific tracker returning new state. | ||
131 | announceTo :: Manager -> InfoHash -> Maybe Event | ||
132 | -> TrackerEntry -> IO TrackerEntry | ||
133 | announceTo mgr ih mevent entry @ TrackerEntry {..} | ||
134 | | mevent `needNotify` statusSent = do | ||
135 | let q = SAnnounceQuery ih def Nothing mevent | ||
136 | res <- RPC.announce mgr trackerURI q | ||
137 | TrackerEntry trackerURI (Just (nextStatus mevent)) | ||
138 | <$> cachePeers res <*> cacheScrape res | ||
139 | | otherwise = return entry | ||
140 | |||
119 | {----------------------------------------------------------------------- | 141 | {----------------------------------------------------------------------- |
120 | -- Multitracker Session | 142 | -- Multitracker Session |
121 | -----------------------------------------------------------------------} | 143 | -----------------------------------------------------------------------} |
@@ -157,41 +179,24 @@ withSession ih uris = bracket (newSession ih uris) closeSession | |||
157 | getStatus :: Session -> IO Status | 179 | getStatus :: Session -> IO Status |
158 | getStatus Session {..} = takeMVar currentStatus | 180 | getStatus Session {..} = takeMVar currentStatus |
159 | 181 | ||
160 | seconds :: Int -> NominalDiffTime | 182 | -- | Do we need to sent this event to a first working tracker or to |
161 | seconds n = realToFrac (toEnum n :: Uni) | 183 | -- the all known good trackers? |
162 | 184 | allNotify :: Maybe Event -> Bool | |
163 | cachePeers :: AnnounceInfo -> IO (Cached [PeerAddr IP]) | 185 | allNotify Nothing = False |
164 | cachePeers AnnounceInfo {..} = | 186 | allNotify (Just Started) = False |
165 | newCached (seconds respInterval) | 187 | allNotify (Just Stopped) = True |
166 | (seconds (fromMaybe respInterval respMinInterval)) | 188 | allNotify (Just Completed) = True |
167 | (getPeerList respPeers) | ||
168 | |||
169 | cacheScrape :: AnnounceInfo -> IO (Cached LastScrape) | ||
170 | cacheScrape AnnounceInfo {..} = | ||
171 | newCached (seconds respInterval) | ||
172 | (seconds (fromMaybe respInterval respMinInterval)) | ||
173 | LastScrape | ||
174 | { seedersCount = respComplete | ||
175 | , leechersCount = respIncomplete | ||
176 | } | ||
177 | 189 | ||
178 | announceAll :: Manager -> Session -> Maybe Event -> IO () | 190 | announceAll :: Manager -> Session -> Maybe Event -> IO () |
179 | announceAll mgr Session {..} mevent = do | 191 | announceAll mgr Session {..} mevent = do |
180 | modifyMVar_ trackers (traversal announceTo) | 192 | modifyMVar_ trackers (traversal (announceTo mgr infohash mevent)) |
181 | where | 193 | where |
182 | traversal | 194 | traversal |
183 | | allNotify mevent = traverseAll | 195 | | allNotify mevent = traverseAll |
184 | | otherwise = traverseTiers | 196 | | otherwise = traverseTiers |
185 | 197 | ||
186 | announceTo entry @ TrackerEntry {..} | ||
187 | | mevent `needNotify` statusSent = do | ||
188 | let q = SAnnounceQuery infohash def Nothing mevent | ||
189 | res <- RPC.announce mgr trackerURI q | ||
190 | TrackerEntry trackerURI (Just (nextStatus mevent)) | ||
191 | <$> cachePeers res <*> cacheScrape res | ||
192 | | otherwise = return entry | ||
193 | |||
194 | -- TODO send notifications to tracker periodically. | 198 | -- TODO send notifications to tracker periodically. |
199 | -- TODO change 'currentStatus' | ||
195 | -- | | 200 | -- | |
196 | -- | 201 | -- |
197 | -- This function /may/ block until tracker query proceed. | 202 | -- This function /may/ block until tracker query proceed. |