summaryrefslogtreecommitdiff
path: root/src/Network/BitTorrent
diff options
context:
space:
mode:
authorSam Truzjan <pxqr.sta@gmail.com>2014-02-04 04:02:09 +0400
committerSam Truzjan <pxqr.sta@gmail.com>2014-02-04 04:02:09 +0400
commit01a4225f6745677b29ee2cde9408d7391205a731 (patch)
treef0ea161226b47f5c190059d695873d66bb4b4b26 /src/Network/BitTorrent
parentb0663f8ac708f7d325793b6c212a60e2bf417540 (diff)
Add HTTP tracker manager
Diffstat (limited to 'src/Network/BitTorrent')
-rw-r--r--src/Network/BitTorrent/Tracker/RPC/HTTP.hs111
1 files changed, 74 insertions, 37 deletions
diff --git a/src/Network/BitTorrent/Tracker/RPC/HTTP.hs b/src/Network/BitTorrent/Tracker/RPC/HTTP.hs
index 21013d20..c39f8f31 100644
--- a/src/Network/BitTorrent/Tracker/RPC/HTTP.hs
+++ b/src/Network/BitTorrent/Tracker/RPC/HTTP.hs
@@ -14,13 +14,17 @@
14-- <https://wiki.theory.org/BitTorrentSpecification#Tracker_HTTP.2FHTTPS_Protocol> 14-- <https://wiki.theory.org/BitTorrentSpecification#Tracker_HTTP.2FHTTPS_Protocol>
15-- 15--
16module Network.BitTorrent.Tracker.RPC.HTTP 16module Network.BitTorrent.Tracker.RPC.HTTP
17 ( Connection 17 ( -- * Manager
18 , putConnection 18 Options (..)
19 , Manager
20 , newManager
21 , closeManager
22 , withManager
19 23
20 -- * RPC 24 -- * RPC
21 , connect
22 , announce 25 , announce
23 , scrape 26 , scrape
27 , scrapeOne
24 ) where 28 ) where
25 29
26import Control.Applicative 30import Control.Applicative
@@ -30,55 +34,88 @@ import Data.BEncode as BE
30import Data.ByteString as B 34import Data.ByteString as B
31import Data.ByteString.Char8 as BC 35import Data.ByteString.Char8 as BC
32import Data.ByteString.Lazy as BL 36import Data.ByteString.Lazy as BL
37import Data.Default
33import Data.List as L 38import Data.List as L
34import Data.Map as M
35import Data.Monoid 39import Data.Monoid
36import Network.URI 40import Network.URI
37import Network.HTTP.Conduit 41import Network.HTTP.Conduit hiding
38import Network.HTTP.Conduit.Internal 42 (Manager, newManager, closeManager, withManager)
39import Network.HTTP.Types.URI 43import qualified Network.HTTP.Conduit as HTTP
44import Network.HTTP.Conduit.Internal (setUri)
45import Network.HTTP.Types.URI
40 46
41import Data.Torrent.InfoHash 47import Data.Torrent.InfoHash
42import Network.BitTorrent.Tracker.Message 48import Network.BitTorrent.Tracker.Message
43 49
50{-----------------------------------------------------------------------
51-- Manager
52-----------------------------------------------------------------------}
44 53
45data Connection = Connection 54-- | HTTP tracker specific RPC options.
46 { announceURI :: URI 55data Options = Options
47 , manager :: Manager 56 { -- | Global HTTP announce query preferences.
48 , connProxy :: Maybe Proxy 57 optAnnounceExt :: !AnnounceQueryExt
58
59 -- | Whether to use HTTP proxy for HTTP tracker requests.
60 , optHttpProxy :: !(Maybe Proxy)
61
62 -- | HTTP manager options.
63 , optHttpOptions :: !ManagerSettings
49 } 64 }
50 65
51putConnection :: Connection -> IO () 66instance Default Options where
52putConnection = undefined 67 def = Options
53 68 { optAnnounceExt = def
54-- TODO share manager between several threads 69 , optHttpProxy = Nothing
55connect :: URI -> ResourceT IO Connection 70 , optHttpOptions = def
56connect uri = do
57 (_, m) <- allocate (newManager def) closeManager
58 return Connection
59 { announceURI = uri
60 , manager = m
61 , connProxy = Nothing
62 } 71 }
63 72
73-- | HTTP tracker manager.
74data Manager = Manager
75 { options :: !Options
76 , httpMgr :: !HTTP.Manager
77 }
78
79newManager :: Options -> IO Manager
80newManager opts = Manager opts <$> HTTP.newManager (optHttpOptions opts)
81
82closeManager :: Manager -> IO ()
83closeManager Manager {..} = HTTP.closeManager httpMgr
84
85withManager :: Options -> (Manager -> IO a) -> IO a
86withManager opts = bracket (newManager opts) closeManager
87
88{-----------------------------------------------------------------------
89-- Queries
90-----------------------------------------------------------------------}
91
64setSimpleQuery :: SimpleQuery -> Request m -> Request m 92setSimpleQuery :: SimpleQuery -> Request m -> Request m
65setSimpleQuery q r = r 93setSimpleQuery q r = r
66 { queryString = undefined renderSimpleQuery False q 94 { queryString = renderSimpleQuery False q
67 } 95 }
68 96
69trackerHTTP :: BEncode a => SimpleQuery -> Connection -> ResourceT IO a 97httpTracker :: BEncode a => Manager -> URI -> SimpleQuery -> ResourceT IO a
70trackerHTTP q Connection {..} = do 98httpTracker Manager {..} uri q = do
71 request <- setSimpleQuery q <$> setUri def announceURI 99 request <- setSimpleQuery q <$> setUri def uri
72 response <- httpLbs request { proxy = connProxy } manager 100 response <- httpLbs request { proxy = optHttpProxy options } httpMgr
73 case BE.decode $ BL.toStrict $ responseBody response of 101 case BE.decode $ BL.toStrict $ responseBody response of
74 Left msg -> error "TODO" 102 Left msg -> error $ "httpTracker: " ++ msg
75 Right info -> return info 103 Right info -> return info
76 104
105{-----------------------------------------------------------------------
106-- RPC
107-----------------------------------------------------------------------}
108
77-- | Send request and receive response from the tracker specified in 109-- | Send request and receive response from the tracker specified in
78-- announce list. 110-- announce list.
79-- 111--
80announce :: AnnounceQuery -> Connection -> ResourceT IO AnnounceInfo 112announce :: Manager -> URI -> AnnounceQuery -> ResourceT IO AnnounceInfo
81announce q = trackerHTTP (renderAnnounceQuery q) 113announce mgr uri q = httpTracker mgr uri (renderAnnounceRequest uriQ)
114 where
115 uriQ = AnnounceRequest
116 { announceQuery = q
117 , announceAdvises = optAnnounceExt (options mgr)
118 }
82 119
83-- | Trying to convert /announce/ URL to /scrape/ URL. If 'scrapeURL' 120-- | Trying to convert /announce/ URL to /scrape/ URL. If 'scrapeURL'
84-- gives 'Nothing' then tracker do not support scraping. 121-- gives 'Nothing' then tracker do not support scraping.
@@ -99,17 +136,17 @@ scrapeURL uri = do
99-- However if the info hash list is 'null', the tracker should list 136-- However if the info hash list is 'null', the tracker should list
100-- all available torrents. 137-- all available torrents.
101-- 138--
102scrape :: ScrapeQuery -> Connection -> ResourceT IO ScrapeInfo 139scrape :: Manager -> URI -> ScrapeQuery -> ResourceT IO ScrapeInfo
103scrape q conn @ Connection {..} = do 140scrape m u q = do
104 case scrapeURL announceURI of 141 case scrapeURL u of
105 Nothing -> error "Tracker do not support scraping" 142 Nothing -> error "Tracker do not support scraping"
106 Just uri -> trackerHTTP (renderScrapeQuery q) conn { announceURI = uri } 143 Just uri -> httpTracker m uri (renderScrapeQuery q)
107 144
108-- | More particular version of 'scrape', just for one torrent. 145-- | More particular version of 'scrape', just for one torrent.
109-- 146--
110scrapeOne :: InfoHash -> Connection -> ResourceT IO ScrapeEntry 147scrapeOne :: Manager -> URI -> InfoHash -> ResourceT IO ScrapeEntry
111scrapeOne ih uri = do 148scrapeOne m uri ih = do
112 xs <- scrape [ih] uri 149 xs <- scrape m uri [ih]
113 case L.lookup ih xs of 150 case L.lookup ih xs of
114 Nothing -> error "unable to find info hash in response dict" 151 Nothing -> error "unable to find info hash in response dict"
115 Just a -> return a 152 Just a -> return a