summaryrefslogtreecommitdiff
path: root/src/Network
diff options
context:
space:
mode:
Diffstat (limited to 'src/Network')
-rw-r--r--src/Network/BitTorrent/Tracker/RPC.hs142
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--
8module Network.BitTorrent.Tracker.RPC 10module 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
15import Control.Applicative
16import Control.Exception 25import Control.Exception
17import Control.Monad.Trans.Resource 26import Control.Monad.Trans.Resource
27import Data.Default
28import Network
18import Network.URI 29import Network.URI
30import Network.Socket (HostAddress)
31
32import Data.Torrent.InfoHash
33import Data.Torrent.Progress
34import Network.BitTorrent.Core
35import Network.BitTorrent.Tracker.Message
36import qualified Network.BitTorrent.Tracker.RPC.HTTP as HTTP
37import qualified Network.BitTorrent.Tracker.RPC.UDP as UDP
38
39
40{-----------------------------------------------------------------------
41-- Simplified announce
42-----------------------------------------------------------------------}
43
44-- | Info to advertise to trackers.
45data PeerInfo = PeerInfo
46 { peerId :: !PeerId
47 , peerPort :: !PortNumber
48 , peerIP :: !(Maybe HostAddress)
49 } deriving (Show, Eq)
50
51-- | Simplified announce query.
52data SAnnounceQuery = SAnnounceQuery
53 { sInfoHash :: InfoHash
54 , sProgress :: Progress
55 , sNumWant :: Maybe Int
56 , sEvent :: Maybe Event
57 }
58
59fillAnnounceQuery :: PeerInfo -> SAnnounceQuery -> AnnounceQuery
60fillAnnounceQuery 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
20import Network.BitTorrent.Tracker.Message 70{-----------------------------------------------------------------------
21import Network.BitTorrent.Tracker.RPC.HTTP as HTTP 71-- RPC manager
22import Network.BitTorrent.Tracker.RPC.UDP as UDP 72-----------------------------------------------------------------------}
23 73
74-- | Tracker manager settings.
75data Options = Options
76 { -- | HTTP tracker protocol specific options.
77 optHttpRPC :: !HTTP.Options
24 78
25data Tracker 79 -- | UDP tracker protocol specific options.
26 = HTracker Connection 80 , optUdpRPC :: !UDP.Options
27 | UTracker UDPTracker
28 81
29connect :: URI -> IO Tracker 82 -- | Whether to use multitracker extension.
30connect uri @ URI {..} 83 , optMultitracker :: !Bool
31 | uriScheme == "http:" = HTracker <$> runResourceT (HTTP.connect uri) 84 }
32 | uriScheme == "udp:" = UTracker <$> UDP.connect uri 85
86instance Default Options where
87 def = Options
88 { optHttpRPC = def
89 , optUdpRPC = def
90 , optMultitracker = True
91 }
92
93-- | Tracker RPC Manager.
94data 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.
103newManager :: Options -> PeerInfo -> IO Manager
104newManager 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
109closeManager :: Manager -> IO ()
110closeManager Manager {..} = do
111 UDP.closeManager udpMgr `finally` HTTP.closeManager httpMgr
112
113withManager :: Options -> PeerInfo -> (Manager -> IO a) -> IO a
114withManager opts info = bracket (newManager opts info) closeManager
115
116{-----------------------------------------------------------------------
117-- RPC calls
118-----------------------------------------------------------------------}
119-- TODO Catch IO exceptions on rpc calls.
120
121dispatch :: URI -> IO a -> IO a -> IO a
122dispatch 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
37announce :: AnnounceQuery -> Tracker -> IO AnnounceInfo 129announce :: Manager -> URI -> SAnnounceQuery -> IO AnnounceInfo
38announce q (HTracker t) = runResourceT $ HTTP.announce q t 130announce Manager {..} uri simpleQuery
39announce 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
41scrape :: ScrapeQuery -> Tracker -> IO ScrapeInfo 137scrape :: Manager -> URI -> ScrapeQuery -> IO ScrapeInfo
42scrape q (HTracker t) = runResourceT $ HTTP.scrape q t 138scrape Manager {..} uri q
43scrape q (UTracker t) = UDP.scrape q t 139 = dispatch uri
140 (runResourceT (HTTP.scrape httpMgr uri q))
141 (UDP.scrape udpMgr uri q)