diff options
Diffstat (limited to 'src/Network/BitTorrent/Tracker/RPC.hs')
-rw-r--r-- | src/Network/BitTorrent/Tracker/RPC.hs | 142 |
1 files changed, 120 insertions, 22 deletions
diff --git a/src/Network/BitTorrent/Tracker/RPC.hs b/src/Network/BitTorrent/Tracker/RPC.hs index 04dbec1b..3fe5157c 100644 --- a/src/Network/BitTorrent/Tracker/RPC.hs +++ b/src/Network/BitTorrent/Tracker/RPC.hs | |||
@@ -5,39 +5,137 @@ | |||
5 | -- Stability : experimental | 5 | -- Stability : experimental |
6 | -- Portability : portable | 6 | -- Portability : portable |
7 | -- | 7 | -- |
8 | -- Protocol independent bittorrent tracker API. | ||
9 | -- | ||
8 | module Network.BitTorrent.Tracker.RPC | 10 | module Network.BitTorrent.Tracker.RPC |
9 | ( Tracker | 11 | ( PeerInfo (..) |
10 | , Network.BitTorrent.Tracker.RPC.connect | 12 | |
11 | , Network.BitTorrent.Tracker.RPC.announce | 13 | -- * Manager |
12 | , Network.BitTorrent.Tracker.RPC.scrape | 14 | , Options (..) |
15 | , Manager | ||
16 | , newManager | ||
17 | , closeManager | ||
18 | , withManager | ||
19 | |||
20 | -- * RPC | ||
21 | , announce | ||
22 | , scrape | ||
13 | ) where | 23 | ) where |
14 | 24 | ||
15 | import Control.Applicative | ||
16 | import Control.Exception | 25 | import Control.Exception |
17 | import Control.Monad.Trans.Resource | 26 | import Control.Monad.Trans.Resource |
27 | import Data.Default | ||
28 | import Network | ||
18 | import Network.URI | 29 | import Network.URI |
30 | import Network.Socket (HostAddress) | ||
31 | |||
32 | import Data.Torrent.InfoHash | ||
33 | import Data.Torrent.Progress | ||
34 | import Network.BitTorrent.Core | ||
35 | import Network.BitTorrent.Tracker.Message | ||
36 | import qualified Network.BitTorrent.Tracker.RPC.HTTP as HTTP | ||
37 | import qualified Network.BitTorrent.Tracker.RPC.UDP as UDP | ||
38 | |||
39 | |||
40 | {----------------------------------------------------------------------- | ||
41 | -- Simplified announce | ||
42 | -----------------------------------------------------------------------} | ||
43 | |||
44 | -- | Info to advertise to trackers. | ||
45 | data PeerInfo = PeerInfo | ||
46 | { peerId :: !PeerId | ||
47 | , peerPort :: !PortNumber | ||
48 | , peerIP :: !(Maybe HostAddress) | ||
49 | } deriving (Show, Eq) | ||
50 | |||
51 | -- | Simplified announce query. | ||
52 | data SAnnounceQuery = SAnnounceQuery | ||
53 | { sInfoHash :: InfoHash | ||
54 | , sProgress :: Progress | ||
55 | , sNumWant :: Maybe Int | ||
56 | , sEvent :: Maybe Event | ||
57 | } | ||
58 | |||
59 | fillAnnounceQuery :: PeerInfo -> SAnnounceQuery -> AnnounceQuery | ||
60 | fillAnnounceQuery PeerInfo{..} SAnnounceQuery {..} = AnnounceQuery | ||
61 | { reqInfoHash = sInfoHash | ||
62 | , reqPeerId = peerId | ||
63 | , reqPort = peerPort | ||
64 | , reqProgress = sProgress | ||
65 | , reqIP = peerIP | ||
66 | , reqNumWant = sNumWant | ||
67 | , reqEvent = sEvent | ||
68 | } | ||
19 | 69 | ||
20 | import Network.BitTorrent.Tracker.Message | 70 | {----------------------------------------------------------------------- |
21 | import Network.BitTorrent.Tracker.RPC.HTTP as HTTP | 71 | -- RPC manager |
22 | import Network.BitTorrent.Tracker.RPC.UDP as UDP | 72 | -----------------------------------------------------------------------} |
23 | 73 | ||
74 | -- | Tracker manager settings. | ||
75 | data Options = Options | ||
76 | { -- | HTTP tracker protocol specific options. | ||
77 | optHttpRPC :: !HTTP.Options | ||
24 | 78 | ||
25 | data Tracker | 79 | -- | UDP tracker protocol specific options. |
26 | = HTracker Connection | 80 | , optUdpRPC :: !UDP.Options |
27 | | UTracker UDPTracker | ||
28 | 81 | ||
29 | connect :: URI -> IO Tracker | 82 | -- | Whether to use multitracker extension. |
30 | connect uri @ URI {..} | 83 | , optMultitracker :: !Bool |
31 | | uriScheme == "http:" = HTracker <$> runResourceT (HTTP.connect uri) | 84 | } |
32 | | uriScheme == "udp:" = UTracker <$> UDP.connect uri | 85 | |
86 | instance Default Options where | ||
87 | def = Options | ||
88 | { optHttpRPC = def | ||
89 | , optUdpRPC = def | ||
90 | , optMultitracker = True | ||
91 | } | ||
92 | |||
93 | -- | Tracker RPC Manager. | ||
94 | data Manager = Manager | ||
95 | { options :: !Options | ||
96 | , peerInfo :: !PeerInfo | ||
97 | , httpMgr :: !HTTP.Manager | ||
98 | , udpMgr :: !UDP.Manager | ||
99 | } | ||
100 | |||
101 | -- | Normally a bittorrent client session need a single RPC manager | ||
102 | -- only. | ||
103 | newManager :: Options -> PeerInfo -> IO Manager | ||
104 | newManager opts info = do | ||
105 | h <- HTTP.newManager (optHttpRPC opts) | ||
106 | u <- UDP.newManager (optUdpRPC opts) `onException` HTTP.closeManager h | ||
107 | return $ Manager opts info h u | ||
108 | |||
109 | closeManager :: Manager -> IO () | ||
110 | closeManager Manager {..} = do | ||
111 | UDP.closeManager udpMgr `finally` HTTP.closeManager httpMgr | ||
112 | |||
113 | withManager :: Options -> PeerInfo -> (Manager -> IO a) -> IO a | ||
114 | withManager opts info = bracket (newManager opts info) closeManager | ||
115 | |||
116 | {----------------------------------------------------------------------- | ||
117 | -- RPC calls | ||
118 | -----------------------------------------------------------------------} | ||
119 | -- TODO Catch IO exceptions on rpc calls. | ||
120 | |||
121 | dispatch :: URI -> IO a -> IO a -> IO a | ||
122 | dispatch URI {..} http udp | ||
123 | | uriScheme == "http:" = http | ||
124 | | uriScheme == "udp:" = udp | ||
33 | | otherwise = throwIO $ userError msg | 125 | | otherwise = throwIO $ userError msg |
34 | where | 126 | where |
35 | msg = "unknown tracker protocol scheme: " ++ show uriScheme | 127 | msg = "unknown tracker protocol scheme: " ++ show uriScheme |
36 | 128 | ||
37 | announce :: AnnounceQuery -> Tracker -> IO AnnounceInfo | 129 | announce :: Manager -> URI -> SAnnounceQuery -> IO AnnounceInfo |
38 | announce q (HTracker t) = runResourceT $ HTTP.announce q t | 130 | announce Manager {..} uri simpleQuery |
39 | announce q (UTracker t) = UDP.announce q t | 131 | = dispatch uri |
132 | (runResourceT (HTTP.announce httpMgr uri annQ)) | ||
133 | (UDP.announce udpMgr uri annQ) | ||
134 | where | ||
135 | annQ = fillAnnounceQuery peerInfo simpleQuery | ||
40 | 136 | ||
41 | scrape :: ScrapeQuery -> Tracker -> IO ScrapeInfo | 137 | scrape :: Manager -> URI -> ScrapeQuery -> IO ScrapeInfo |
42 | scrape q (HTracker t) = runResourceT $ HTTP.scrape q t | 138 | scrape Manager {..} uri q |
43 | scrape q (UTracker t) = UDP.scrape q t | 139 | = dispatch uri |
140 | (runResourceT (HTTP.scrape httpMgr uri q)) | ||
141 | (UDP.scrape udpMgr uri q) | ||