diff options
author | Sam Truzjan <pxqr.sta@gmail.com> | 2014-02-04 04:02:09 +0400 |
---|---|---|
committer | Sam Truzjan <pxqr.sta@gmail.com> | 2014-02-04 04:02:09 +0400 |
commit | 01a4225f6745677b29ee2cde9408d7391205a731 (patch) | |
tree | f0ea161226b47f5c190059d695873d66bb4b4b26 /src/Network/BitTorrent/Tracker | |
parent | b0663f8ac708f7d325793b6c212a60e2bf417540 (diff) |
Add HTTP tracker manager
Diffstat (limited to 'src/Network/BitTorrent/Tracker')
-rw-r--r-- | src/Network/BitTorrent/Tracker/RPC/HTTP.hs | 111 |
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 | -- |
16 | module Network.BitTorrent.Tracker.RPC.HTTP | 16 | module 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 | ||
26 | import Control.Applicative | 30 | import Control.Applicative |
@@ -30,55 +34,88 @@ import Data.BEncode as BE | |||
30 | import Data.ByteString as B | 34 | import Data.ByteString as B |
31 | import Data.ByteString.Char8 as BC | 35 | import Data.ByteString.Char8 as BC |
32 | import Data.ByteString.Lazy as BL | 36 | import Data.ByteString.Lazy as BL |
37 | import Data.Default | ||
33 | import Data.List as L | 38 | import Data.List as L |
34 | import Data.Map as M | ||
35 | import Data.Monoid | 39 | import Data.Monoid |
36 | import Network.URI | 40 | import Network.URI |
37 | import Network.HTTP.Conduit | 41 | import Network.HTTP.Conduit hiding |
38 | import Network.HTTP.Conduit.Internal | 42 | (Manager, newManager, closeManager, withManager) |
39 | import Network.HTTP.Types.URI | 43 | import qualified Network.HTTP.Conduit as HTTP |
44 | import Network.HTTP.Conduit.Internal (setUri) | ||
45 | import Network.HTTP.Types.URI | ||
40 | 46 | ||
41 | import Data.Torrent.InfoHash | 47 | import Data.Torrent.InfoHash |
42 | import Network.BitTorrent.Tracker.Message | 48 | import Network.BitTorrent.Tracker.Message |
43 | 49 | ||
50 | {----------------------------------------------------------------------- | ||
51 | -- Manager | ||
52 | -----------------------------------------------------------------------} | ||
44 | 53 | ||
45 | data Connection = Connection | 54 | -- | HTTP tracker specific RPC options. |
46 | { announceURI :: URI | 55 | data 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 | ||
51 | putConnection :: Connection -> IO () | 66 | instance Default Options where |
52 | putConnection = undefined | 67 | def = Options |
53 | 68 | { optAnnounceExt = def | |
54 | -- TODO share manager between several threads | 69 | , optHttpProxy = Nothing |
55 | connect :: URI -> ResourceT IO Connection | 70 | , optHttpOptions = def |
56 | connect 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. | ||
74 | data Manager = Manager | ||
75 | { options :: !Options | ||
76 | , httpMgr :: !HTTP.Manager | ||
77 | } | ||
78 | |||
79 | newManager :: Options -> IO Manager | ||
80 | newManager opts = Manager opts <$> HTTP.newManager (optHttpOptions opts) | ||
81 | |||
82 | closeManager :: Manager -> IO () | ||
83 | closeManager Manager {..} = HTTP.closeManager httpMgr | ||
84 | |||
85 | withManager :: Options -> (Manager -> IO a) -> IO a | ||
86 | withManager opts = bracket (newManager opts) closeManager | ||
87 | |||
88 | {----------------------------------------------------------------------- | ||
89 | -- Queries | ||
90 | -----------------------------------------------------------------------} | ||
91 | |||
64 | setSimpleQuery :: SimpleQuery -> Request m -> Request m | 92 | setSimpleQuery :: SimpleQuery -> Request m -> Request m |
65 | setSimpleQuery q r = r | 93 | setSimpleQuery q r = r |
66 | { queryString = undefined renderSimpleQuery False q | 94 | { queryString = renderSimpleQuery False q |
67 | } | 95 | } |
68 | 96 | ||
69 | trackerHTTP :: BEncode a => SimpleQuery -> Connection -> ResourceT IO a | 97 | httpTracker :: BEncode a => Manager -> URI -> SimpleQuery -> ResourceT IO a |
70 | trackerHTTP q Connection {..} = do | 98 | httpTracker 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 | -- |
80 | announce :: AnnounceQuery -> Connection -> ResourceT IO AnnounceInfo | 112 | announce :: Manager -> URI -> AnnounceQuery -> ResourceT IO AnnounceInfo |
81 | announce q = trackerHTTP (renderAnnounceQuery q) | 113 | announce 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 | -- |
102 | scrape :: ScrapeQuery -> Connection -> ResourceT IO ScrapeInfo | 139 | scrape :: Manager -> URI -> ScrapeQuery -> ResourceT IO ScrapeInfo |
103 | scrape q conn @ Connection {..} = do | 140 | scrape 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 | -- |
110 | scrapeOne :: InfoHash -> Connection -> ResourceT IO ScrapeEntry | 147 | scrapeOne :: Manager -> URI -> InfoHash -> ResourceT IO ScrapeEntry |
111 | scrapeOne ih uri = do | 148 | scrapeOne 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 |