diff options
author | Sam Truzjan <pxqr.sta@gmail.com> | 2013-12-02 00:17:05 +0400 |
---|---|---|
committer | Sam Truzjan <pxqr.sta@gmail.com> | 2013-12-02 00:17:05 +0400 |
commit | 10fcabb96e9f5b2d25cdfae22973cbcb99282139 (patch) | |
tree | 94ceeddd0c4f6998317f921feccddc1d7498ed7e /src/Network/BitTorrent/Tracker | |
parent | 2abd00f548b7b9565b2a6bc1bed793d2a6f8c9cd (diff) |
Add http tracker server-side skeleton
Diffstat (limited to 'src/Network/BitTorrent/Tracker')
-rw-r--r-- | src/Network/BitTorrent/Tracker/RPC/Message.hs | 49 | ||||
-rw-r--r-- | src/Network/BitTorrent/Tracker/Wai.hs | 102 |
2 files changed, 145 insertions, 6 deletions
diff --git a/src/Network/BitTorrent/Tracker/RPC/Message.hs b/src/Network/BitTorrent/Tracker/RPC/Message.hs index cec68169..cefe96d5 100644 --- a/src/Network/BitTorrent/Tracker/RPC/Message.hs +++ b/src/Network/BitTorrent/Tracker/RPC/Message.hs | |||
@@ -32,10 +32,18 @@ module Network.BitTorrent.Tracker.RPC.Message | |||
32 | , ParamParseFailure | 32 | , ParamParseFailure |
33 | , parseAnnounceQuery | 33 | , parseAnnounceQuery |
34 | 34 | ||
35 | -- ** Request | ||
36 | , AnnounceQueryExt (..) | ||
37 | , AnnounceRequest (..) | ||
38 | , parseAnnounceRequest | ||
39 | , renderAnnounceRequest | ||
40 | |||
35 | -- ** Info | 41 | -- ** Info |
36 | , PeerList (..) | 42 | , PeerList (..) |
37 | , AnnounceInfo(..) | 43 | , AnnounceInfo(..) |
38 | , defaultNumWant | 44 | , defaultNumWant |
45 | , defaultMaxNumWant | ||
46 | , defaultReannounceInterval | ||
39 | , parseFailureStatus | 47 | , parseFailureStatus |
40 | 48 | ||
41 | -- * Scrape | 49 | -- * Scrape |
@@ -47,6 +55,9 @@ module Network.BitTorrent.Tracker.RPC.Message | |||
47 | -- ** Info | 55 | -- ** Info |
48 | , ScrapeEntry (..) | 56 | , ScrapeEntry (..) |
49 | , ScrapeInfo | 57 | , ScrapeInfo |
58 | |||
59 | -- ** Extra | ||
60 | , queryToSimpleQuery | ||
50 | ) | 61 | ) |
51 | where | 62 | where |
52 | 63 | ||
@@ -223,15 +234,15 @@ instance QueryLike AnnounceQuery where | |||
223 | , ("event" , toQueryValue reqEvent) | 234 | , ("event" , toQueryValue reqEvent) |
224 | ] | 235 | ] |
225 | 236 | ||
226 | filterMaybes :: [(a, Maybe b)] -> [(a, b)] | 237 | queryToSimpleQuery :: Query -> SimpleQuery |
227 | filterMaybes = catMaybes . L.map f | 238 | queryToSimpleQuery = catMaybes . L.map f |
228 | where | 239 | where |
229 | f (_, Nothing) = Nothing | 240 | f (_, Nothing) = Nothing |
230 | f (a, Just b ) = Just (a, b) | 241 | f (a, Just b ) = Just (a, b) |
231 | 242 | ||
232 | -- | Encode announce query and add it to the base tracker URL. | 243 | -- | Encode announce query and add it to the base tracker URL. |
233 | renderAnnounceQuery :: AnnounceQuery -> SimpleQuery | 244 | renderAnnounceQuery :: AnnounceQuery -> SimpleQuery |
234 | renderAnnounceQuery = filterMaybes . toQuery | 245 | renderAnnounceQuery = queryToSimpleQuery . toQuery |
235 | 246 | ||
236 | data QueryParam | 247 | data QueryParam |
237 | = ParamInfoHash | 248 | = ParamInfoHash |
@@ -320,8 +331,27 @@ parseAnnounceQuery params = AnnounceQuery | |||
320 | <*> optParam ParamNumWant params | 331 | <*> optParam ParamNumWant params |
321 | <*> optParam ParamEvent params | 332 | <*> optParam ParamEvent params |
322 | 333 | ||
323 | -- TODO add extension datatype | 334 | data AnnounceQueryExt = AnnounceQueryExt |
324 | --type AnnounceRequest = () | 335 | { extCompact :: Maybe Bool -- | "compact" param |
336 | , extNoPeerId :: Maybe Bool -- | "no_peer_id" param | ||
337 | } deriving (Show, Eq, Typeable) | ||
338 | |||
339 | parseAnnounceQueryExt :: SimpleQuery -> AnnounceQueryExt | ||
340 | parseAnnounceQueryExt = undefined | ||
341 | |||
342 | renderAnnounceQueryExt :: AnnounceQueryExt -> SimpleQuery | ||
343 | renderAnnounceQueryExt = undefined | ||
344 | |||
345 | data AnnounceRequest = AnnounceRequest | ||
346 | { announceQuery :: AnnounceQuery | ||
347 | , announceAdvises :: AnnounceQueryExt | ||
348 | } deriving (Show, Eq, Typeable) | ||
349 | |||
350 | parseAnnounceRequest :: SimpleQuery -> Either ParamParseFailure AnnounceRequest | ||
351 | parseAnnounceRequest = undefined | ||
352 | |||
353 | renderAnnounceRequest :: AnnounceRequest -> SimpleQuery | ||
354 | renderAnnounceRequest = undefined | ||
325 | 355 | ||
326 | {----------------------------------------------------------------------- | 356 | {----------------------------------------------------------------------- |
327 | -- Announce response | 357 | -- Announce response |
@@ -449,6 +479,13 @@ instance Serialize AnnounceInfo where | |||
449 | defaultNumWant :: Int | 479 | defaultNumWant :: Int |
450 | defaultNumWant = 50 | 480 | defaultNumWant = 50 |
451 | 481 | ||
482 | defaultMaxNumWant :: Int | ||
483 | defaultMaxNumWant = 200 | ||
484 | |||
485 | defaultReannounceInterval :: Int | ||
486 | defaultReannounceInterval = 30 * 60 | ||
487 | |||
488 | |||
452 | missingOffset :: Int | 489 | missingOffset :: Int |
453 | missingOffset = 101 | 490 | missingOffset = 101 |
454 | 491 | ||
@@ -500,7 +537,7 @@ isScrapeParam :: BS.ByteString -> Bool | |||
500 | isScrapeParam = (==) scrapeParam | 537 | isScrapeParam = (==) scrapeParam |
501 | 538 | ||
502 | renderScrapeQuery :: ScrapeQuery -> SimpleQuery | 539 | renderScrapeQuery :: ScrapeQuery -> SimpleQuery |
503 | renderScrapeQuery = filterMaybes . L.map mkPair | 540 | renderScrapeQuery = queryToSimpleQuery . L.map mkPair |
504 | where | 541 | where |
505 | mkPair ih = (scrapeParam, toQueryValue ih) | 542 | mkPair ih = (scrapeParam, toQueryValue ih) |
506 | 543 | ||
diff --git a/src/Network/BitTorrent/Tracker/Wai.hs b/src/Network/BitTorrent/Tracker/Wai.hs new file mode 100644 index 00000000..f290c380 --- /dev/null +++ b/src/Network/BitTorrent/Tracker/Wai.hs | |||
@@ -0,0 +1,102 @@ | |||
1 | -- supported extensions: | ||
2 | -- | ||
3 | -- no_peer_id - do not send peer id if no_peer_id=1 specified | ||
4 | -- http://www.bittorrent.org/beps/bep_0023.html | ||
5 | -- | ||
6 | -- compact - compact=1 or compact=0 | ||
7 | -- http://permalink.gmane.org/gmane.network.bit-torrent.general/4030 | ||
8 | -- | ||
9 | -- | ||
10 | {-# LANGUAGE RecordWildCards #-} | ||
11 | module Network.BitTorrent.Tracker.Wai | ||
12 | ( tracker | ||
13 | ) where | ||
14 | |||
15 | import Control.Monad.Trans.Resource | ||
16 | import Data.BEncode as BE | ||
17 | import Data.ByteString | ||
18 | import Data.Default | ||
19 | import Data.List as L | ||
20 | import Network.HTTP.Types | ||
21 | import Network.Wai | ||
22 | |||
23 | import Data.Torrent.Progress | ||
24 | import Network.BitTorrent.Core.PeerId | ||
25 | import Network.BitTorrent.Core.PeerAddr | ||
26 | import Network.BitTorrent.Tracker.RPC.Message | ||
27 | |||
28 | |||
29 | data TrackerSettings = TrackerSettings | ||
30 | { -- | If peer did not specified the "numwant" then this value is | ||
31 | -- used. | ||
32 | defNumWant :: {-# UNPACK #-} !Int | ||
33 | |||
34 | -- | If peer specified to big numwant value. | ||
35 | , maxNumWant :: {-# UNPACK #-} !Int | ||
36 | |||
37 | -- | Recommended time interval to wait between regular announce | ||
38 | -- requests. | ||
39 | , reannounceInterval :: {-# UNPACK #-} !Int | ||
40 | |||
41 | -- | Minimum time interval to wait between regular announce | ||
42 | -- requests. | ||
43 | , reannounceMinInterval :: !(Maybe Int) | ||
44 | |||
45 | -- | Whether to send count of seeders. | ||
46 | , completePeers :: !Bool | ||
47 | |||
48 | -- | Whether to send count of leechers. | ||
49 | , incompletePeers :: !Bool | ||
50 | |||
51 | -- | Do not send peer id in response. Peer can override this value | ||
52 | -- by setting "no_peer_id" to 0 or 1. | ||
53 | , noPeerId :: !Bool | ||
54 | |||
55 | -- | Whether to send compact peer list. Peer can override this | ||
56 | -- value by setting "compact" to 0 or 1. | ||
57 | , compactPeerList :: !Bool | ||
58 | } | ||
59 | |||
60 | instance Default TrackerSettings where | ||
61 | def = TrackerSettings | ||
62 | { defNumWant = defaultNumWant | ||
63 | , maxNumWant = defaultMaxNumWant | ||
64 | , reannounceInterval = defaultReannounceInterval | ||
65 | , reannounceMinInterval = Nothing | ||
66 | , compactPeerList = False | ||
67 | , completePeers = False | ||
68 | , incompletePeers = False | ||
69 | , noPeerId = False | ||
70 | } | ||
71 | |||
72 | getAnnounceR :: AnnounceRequest -> ResourceT IO AnnounceInfo | ||
73 | getAnnounceR = undefined | ||
74 | |||
75 | getScrapeR :: ScrapeQuery -> ResourceT IO ScrapeInfo | ||
76 | getScrapeR = undefined | ||
77 | |||
78 | -- content-type: "text/plain" ? | ||
79 | tracker :: Application | ||
80 | tracker Request {..} | ||
81 | | requestMethod /= methodGet | ||
82 | = return $ responseLBS methodNotAllowed405 [] "" | ||
83 | |||
84 | | otherwise = do | ||
85 | case pathInfo of | ||
86 | ["announce"] -> | ||
87 | case parseAnnounceRequest $ queryToSimpleQuery queryString of | ||
88 | Right query -> do | ||
89 | info <- getAnnounceR query | ||
90 | return $ responseLBS ok200 [] $ BE.encode info | ||
91 | Left msg -> | ||
92 | return $ responseLBS (parseFailureStatus msg) [] "" | ||
93 | |||
94 | ["scrape"] -> | ||
95 | case Right $ parseScrapeQuery $ queryToSimpleQuery queryString of -- TODO | ||
96 | Right query -> do | ||
97 | info <- getScrapeR query | ||
98 | return $ responseLBS ok200 [] $ BE.encode info | ||
99 | Left _ -> | ||
100 | return $ responseLBS badRequest400 [] "" | ||
101 | |||
102 | _ -> undefined --badPath | ||