diff options
Diffstat (limited to 'src/Network/BitTorrent/Tracker')
-rw-r--r-- | src/Network/BitTorrent/Tracker/Message.hs | 20 | ||||
-rw-r--r-- | src/Network/BitTorrent/Tracker/RPC.hs | 2 | ||||
-rw-r--r-- | src/Network/BitTorrent/Tracker/Session.hs | 49 |
3 files changed, 33 insertions, 38 deletions
diff --git a/src/Network/BitTorrent/Tracker/Message.hs b/src/Network/BitTorrent/Tracker/Message.hs index 8131ecf0..ffe36c82 100644 --- a/src/Network/BitTorrent/Tracker/Message.hs +++ b/src/Network/BitTorrent/Tracker/Message.hs | |||
@@ -30,8 +30,8 @@ | |||
30 | module Network.BitTorrent.Tracker.Message | 30 | module Network.BitTorrent.Tracker.Message |
31 | ( -- * Announce | 31 | ( -- * Announce |
32 | -- ** Query | 32 | -- ** Query |
33 | Event(..) | 33 | AnnounceEvent (..) |
34 | , AnnounceQuery(..) | 34 | , AnnounceQuery (..) |
35 | , renderAnnounceQuery | 35 | , renderAnnounceQuery |
36 | , ParamParseFailure | 36 | , ParamParseFailure |
37 | , parseAnnounceQuery | 37 | , parseAnnounceQuery |
@@ -136,7 +136,7 @@ import Network.BitTorrent.Core | |||
136 | -----------------------------------------------------------------------} | 136 | -----------------------------------------------------------------------} |
137 | 137 | ||
138 | -- | Events are used to specify which kind of announce query is performed. | 138 | -- | Events are used to specify which kind of announce query is performed. |
139 | data Event | 139 | data AnnounceEvent |
140 | -- | For the first request: when download first begins. | 140 | -- | For the first request: when download first begins. |
141 | = Started | 141 | = Started |
142 | 142 | ||
@@ -150,10 +150,10 @@ data Event | |||
150 | | Completed | 150 | | Completed |
151 | deriving (Show, Read, Eq, Ord, Enum, Bounded, Typeable) | 151 | deriving (Show, Read, Eq, Ord, Enum, Bounded, Typeable) |
152 | 152 | ||
153 | $(deriveJSON omitRecordPrefix ''Event) | 153 | $(deriveJSON omitRecordPrefix ''AnnounceEvent) |
154 | 154 | ||
155 | -- | HTTP tracker protocol compatible encoding. | 155 | -- | HTTP tracker protocol compatible encoding. |
156 | instance QueryValueLike Event where | 156 | instance QueryValueLike AnnounceEvent where |
157 | toQueryValue e = toQueryValue (Char.toLower x : xs) | 157 | toQueryValue e = toQueryValue (Char.toLower x : xs) |
158 | where | 158 | where |
159 | (x : xs) = show e -- INVARIANT: this is always nonempty list | 159 | (x : xs) = show e -- INVARIANT: this is always nonempty list |
@@ -161,17 +161,17 @@ instance QueryValueLike Event where | |||
161 | type EventId = Word32 | 161 | type EventId = Word32 |
162 | 162 | ||
163 | -- | UDP tracker encoding event codes. | 163 | -- | UDP tracker encoding event codes. |
164 | eventId :: Event -> EventId | 164 | eventId :: AnnounceEvent -> EventId |
165 | eventId Completed = 1 | 165 | eventId Completed = 1 |
166 | eventId Started = 2 | 166 | eventId Started = 2 |
167 | eventId Stopped = 3 | 167 | eventId Stopped = 3 |
168 | 168 | ||
169 | -- TODO add Regular event | 169 | -- TODO add Regular event |
170 | putEvent :: Putter (Maybe Event) | 170 | putEvent :: Putter (Maybe AnnounceEvent) |
171 | putEvent Nothing = putWord32be 0 | 171 | putEvent Nothing = putWord32be 0 |
172 | putEvent (Just e) = putWord32be (eventId e) | 172 | putEvent (Just e) = putWord32be (eventId e) |
173 | 173 | ||
174 | getEvent :: S.Get (Maybe Event) | 174 | getEvent :: S.Get (Maybe AnnounceEvent) |
175 | getEvent = do | 175 | getEvent = do |
176 | eid <- getWord32be | 176 | eid <- getWord32be |
177 | case eid of | 177 | case eid of |
@@ -221,7 +221,7 @@ data AnnounceQuery = AnnounceQuery | |||
221 | 221 | ||
222 | -- | If not specified, the request is regular periodic | 222 | -- | If not specified, the request is regular periodic |
223 | -- request. Regular request should be sent | 223 | -- request. Regular request should be sent |
224 | , reqEvent :: Maybe Event | 224 | , reqEvent :: Maybe AnnounceEvent |
225 | } deriving (Show, Eq, Typeable) | 225 | } deriving (Show, Eq, Typeable) |
226 | 226 | ||
227 | $(deriveJSON omitRecordPrefix ''AnnounceQuery) | 227 | $(deriveJSON omitRecordPrefix ''AnnounceQuery) |
@@ -351,7 +351,7 @@ instance FromParam Int where | |||
351 | instance FromParam PortNumber where | 351 | instance FromParam PortNumber where |
352 | fromParam bs = fromIntegral <$> (fromParam bs :: Maybe Word32) | 352 | fromParam bs = fromIntegral <$> (fromParam bs :: Maybe Word32) |
353 | 353 | ||
354 | instance FromParam Event where | 354 | instance FromParam AnnounceEvent where |
355 | fromParam bs = do | 355 | fromParam bs = do |
356 | (x, xs) <- BC.uncons bs | 356 | (x, xs) <- BC.uncons bs |
357 | readMaybe $ BC.unpack $ BC.cons (Char.toUpper x) xs | 357 | readMaybe $ BC.unpack $ BC.cons (Char.toUpper x) xs |
diff --git a/src/Network/BitTorrent/Tracker/RPC.hs b/src/Network/BitTorrent/Tracker/RPC.hs index 39d1b09f..dc1bd4ec 100644 --- a/src/Network/BitTorrent/Tracker/RPC.hs +++ b/src/Network/BitTorrent/Tracker/RPC.hs | |||
@@ -63,7 +63,7 @@ data SAnnounceQuery = SAnnounceQuery | |||
63 | { sInfoHash :: InfoHash | 63 | { sInfoHash :: InfoHash |
64 | , sProgress :: Progress | 64 | , sProgress :: Progress |
65 | , sNumWant :: Maybe Int | 65 | , sNumWant :: Maybe Int |
66 | , sEvent :: Maybe Event | 66 | , sEvent :: Maybe AnnounceEvent |
67 | } | 67 | } |
68 | 68 | ||
69 | fillAnnounceQuery :: PeerInfo -> SAnnounceQuery -> AnnounceQuery | 69 | fillAnnounceQuery :: PeerInfo -> SAnnounceQuery -> AnnounceQuery |
diff --git a/src/Network/BitTorrent/Tracker/Session.hs b/src/Network/BitTorrent/Tracker/Session.hs index e82501dd..5aa9c0eb 100644 --- a/src/Network/BitTorrent/Tracker/Session.hs +++ b/src/Network/BitTorrent/Tracker/Session.hs | |||
@@ -8,17 +8,18 @@ | |||
8 | -- Multitracker sessions. | 8 | -- Multitracker sessions. |
9 | -- | 9 | -- |
10 | {-# LANGUAGE FlexibleInstances #-} | 10 | {-# LANGUAGE FlexibleInstances #-} |
11 | {-# LANGUAGE TypeFamilies #-} | ||
11 | {-# LANGUAGE TypeSynonymInstances #-} | 12 | {-# LANGUAGE TypeSynonymInstances #-} |
12 | {-# LANGUAGE TemplateHaskell #-} | 13 | {-# LANGUAGE TemplateHaskell #-} |
13 | module Network.BitTorrent.Tracker.Session | 14 | module Network.BitTorrent.Tracker.Session |
14 | ( -- * Session | 15 | ( -- * Session |
15 | Session | 16 | Session |
17 | , Event (..) | ||
16 | , newSession | 18 | , newSession |
17 | , closeSession | 19 | , closeSession |
18 | , withSession | 20 | , withSession |
19 | 21 | ||
20 | -- * Client send notifications | 22 | -- * Client send notifications |
21 | , Event (..) | ||
22 | , notify | 23 | , notify |
23 | , askPeers | 24 | , askPeers |
24 | 25 | ||
@@ -39,16 +40,12 @@ module Network.BitTorrent.Tracker.Session | |||
39 | , addTracker | 40 | , addTracker |
40 | , removeTracker | 41 | , removeTracker |
41 | , getTrustedTrackers | 42 | , getTrustedTrackers |
42 | |||
43 | -- * Events | ||
44 | , SessionEvent (..) | ||
45 | , subscribe | ||
46 | ) where | 43 | ) where |
47 | 44 | ||
48 | import Control.Applicative | 45 | import Control.Applicative |
49 | import Control.Exception | 46 | import Control.Exception |
50 | import Control.Concurrent | 47 | import Control.Concurrent |
51 | import Control.Concurrent.Chan.Split | 48 | import Control.Concurrent.Chan.Split as CS |
52 | import Control.Monad | 49 | import Control.Monad |
53 | import Data.Aeson | 50 | import Data.Aeson |
54 | import Data.Aeson.TH | 51 | import Data.Aeson.TH |
@@ -66,6 +63,7 @@ import Data.Torrent.InfoHash | |||
66 | import Data.Torrent.JSON | 63 | import Data.Torrent.JSON |
67 | import Network.BitTorrent.Core | 64 | import Network.BitTorrent.Core |
68 | import Network.BitTorrent.Internal.Cache | 65 | import Network.BitTorrent.Internal.Cache |
66 | import Network.BitTorrent.Internal.Types | ||
69 | import Network.BitTorrent.Tracker.List as TL | 67 | import Network.BitTorrent.Tracker.List as TL |
70 | import Network.BitTorrent.Tracker.Message | 68 | import Network.BitTorrent.Tracker.Message |
71 | import Network.BitTorrent.Tracker.RPC as RPC | 69 | import Network.BitTorrent.Tracker.RPC as RPC |
@@ -123,7 +121,7 @@ instance Default TrackerSession where | |||
123 | def = TrackerSession Nothing def def | 121 | def = TrackerSession Nothing def def |
124 | 122 | ||
125 | -- | Do we need to notify this /specific/ tracker? | 123 | -- | Do we need to notify this /specific/ tracker? |
126 | needNotify :: Event -> Maybe Status -> Maybe Bool | 124 | needNotify :: AnnounceEvent -> Maybe Status -> Maybe Bool |
127 | needNotify Started Nothing = Just True | 125 | needNotify Started Nothing = Just True |
128 | needNotify Stopped Nothing = Just False | 126 | needNotify Stopped Nothing = Just False |
129 | needNotify Completed Nothing = Just False | 127 | needNotify Completed Nothing = Just False |
@@ -135,7 +133,7 @@ needNotify Stopped (Just Paused ) = Just False | |||
135 | needNotify Completed (Just Paused ) = Just True | 133 | needNotify Completed (Just Paused ) = Just True |
136 | 134 | ||
137 | -- | Client status after event announce succeed. | 135 | -- | Client status after event announce succeed. |
138 | nextStatus :: Event -> Maybe Status | 136 | nextStatus :: AnnounceEvent -> Maybe Status |
139 | nextStatus Started = Just Running | 137 | nextStatus Started = Just Running |
140 | nextStatus Stopped = Just Paused | 138 | nextStatus Stopped = Just Paused |
141 | nextStatus Completed = Nothing -- must keep previous status | 139 | nextStatus Completed = Nothing -- must keep previous status |
@@ -159,7 +157,7 @@ cacheScrape AnnounceInfo {..} = | |||
159 | } | 157 | } |
160 | 158 | ||
161 | -- | Make announce request to specific tracker returning new state. | 159 | -- | Make announce request to specific tracker returning new state. |
162 | notifyTo :: Manager -> Session -> Event | 160 | notifyTo :: Manager -> Session -> AnnounceEvent |
163 | -> TierEntry TrackerSession -> IO TrackerSession | 161 | -> TierEntry TrackerSession -> IO TrackerSession |
164 | notifyTo mgr s @ Session {..} event (uri, entry @ TrackerSession {..}) = do | 162 | notifyTo mgr s @ Session {..} event (uri, entry @ TrackerSession {..}) = do |
165 | let shouldNotify = needNotify event statusSent | 163 | let shouldNotify = needNotify event statusSent |
@@ -193,9 +191,20 @@ data Session = Session | |||
193 | -- must take a lock. | 191 | -- must take a lock. |
194 | , sessionTrackers :: !(MVar (TrackerList TrackerSession)) | 192 | , sessionTrackers :: !(MVar (TrackerList TrackerSession)) |
195 | 193 | ||
196 | , sessionEvents :: !(SendPort SessionEvent) | 194 | , sessionEvents :: !(SendPort (Event Session)) |
197 | } | 195 | } |
198 | 196 | ||
197 | instance EventSource Session where | ||
198 | data Event Session | ||
199 | = TrackerAdded URI | ||
200 | | TrackerConfirmed URI | ||
201 | | TrackerRemoved URI | ||
202 | | AnnouncedTo URI | ||
203 | | SessionClosed | ||
204 | |||
205 | listen Session {..} = CS.listen sessionEvents | ||
206 | |||
207 | |||
199 | -- | Create a new multitracker session in paused state. Tracker list | 208 | -- | Create a new multitracker session in paused state. Tracker list |
200 | -- must contant only /trusted/ tracker uris. To start announcing | 209 | -- must contant only /trusted/ tracker uris. To start announcing |
201 | -- client presence use 'notify'. | 210 | -- client presence use 'notify'. |
@@ -221,20 +230,6 @@ closeSession m s @ Session {..} = do | |||
221 | send sessionEvents SessionClosed | 230 | send sessionEvents SessionClosed |
222 | 231 | ||
223 | {----------------------------------------------------------------------- | 232 | {----------------------------------------------------------------------- |
224 | -- Events | ||
225 | -----------------------------------------------------------------------} | ||
226 | |||
227 | data SessionEvent | ||
228 | = TrackerAdded URI | ||
229 | | TrackerConfirmed URI | ||
230 | | TrackerRemoved URI | ||
231 | | AnnouncedTo URI | ||
232 | | SessionClosed | ||
233 | |||
234 | subscribe :: Session -> IO (ReceivePort SessionEvent) | ||
235 | subscribe Session {..} = listen sessionEvents | ||
236 | |||
237 | {----------------------------------------------------------------------- | ||
238 | -- Operations | 233 | -- Operations |
239 | -----------------------------------------------------------------------} | 234 | -----------------------------------------------------------------------} |
240 | 235 | ||
@@ -253,12 +248,12 @@ getSessionState Session {..} = TL.toList <$> readMVar sessionTrackers | |||
253 | 248 | ||
254 | -- | Do we need to sent this event to a first working tracker or to | 249 | -- | Do we need to sent this event to a first working tracker or to |
255 | -- the all known good trackers? | 250 | -- the all known good trackers? |
256 | allNotify :: Event -> Bool | 251 | allNotify :: AnnounceEvent -> Bool |
257 | allNotify Started = False | 252 | allNotify Started = False |
258 | allNotify Stopped = True | 253 | allNotify Stopped = True |
259 | allNotify Completed = True | 254 | allNotify Completed = True |
260 | 255 | ||
261 | notifyAll :: Manager -> Session -> Event -> IO () | 256 | notifyAll :: Manager -> Session -> AnnounceEvent -> IO () |
262 | notifyAll mgr s @ Session {..} event = do | 257 | notifyAll mgr s @ Session {..} event = do |
263 | modifyMVar_ sessionTrackers $ | 258 | modifyMVar_ sessionTrackers $ |
264 | (traversal (notifyTo mgr s event)) | 259 | (traversal (notifyTo mgr s event)) |
@@ -271,7 +266,7 @@ notifyAll mgr s @ Session {..} event = do | |||
271 | -- | | 266 | -- | |
272 | -- | 267 | -- |
273 | -- This function /may/ block until tracker query proceed. | 268 | -- This function /may/ block until tracker query proceed. |
274 | notify :: Manager -> Session -> Event -> IO () | 269 | notify :: Manager -> Session -> AnnounceEvent -> IO () |
275 | notify mgr ses event = do | 270 | notify mgr ses event = do |
276 | prevStatus <- atomicModifyIORef (sessionStatus ses) $ \ s -> | 271 | prevStatus <- atomicModifyIORef (sessionStatus ses) $ \ s -> |
277 | (fromMaybe s (nextStatus event), s) | 272 | (fromMaybe s (nextStatus event), s) |