diff options
author | Sam Truzjan <pxqr.sta@gmail.com> | 2013-12-03 17:21:43 +0400 |
---|---|---|
committer | Sam Truzjan <pxqr.sta@gmail.com> | 2013-12-03 17:21:43 +0400 |
commit | 9b49605d01a1895aecbc20aa22cc13230c4ec27d (patch) | |
tree | f3353b4be422c30772854a38e2c108f52e4b4392 /src/Network | |
parent | d6a0442a56d7b977d5f1d1d162517c9086c413eb (diff) |
Document HTTP tracker extensions
Diffstat (limited to 'src/Network')
-rw-r--r-- | src/Network/BitTorrent/Tracker/RPC/Message.hs | 85 |
1 files changed, 61 insertions, 24 deletions
diff --git a/src/Network/BitTorrent/Tracker/RPC/Message.hs b/src/Network/BitTorrent/Tracker/RPC/Message.hs index 3900ff64..f13f7e97 100644 --- a/src/Network/BitTorrent/Tracker/RPC/Message.hs +++ b/src/Network/BitTorrent/Tracker/RPC/Message.hs | |||
@@ -234,13 +234,14 @@ instance QueryLike AnnounceQuery where | |||
234 | , ("event" , toQueryValue reqEvent) | 234 | , ("event" , toQueryValue reqEvent) |
235 | ] | 235 | ] |
236 | 236 | ||
237 | -- | Filter @param=value@ pairs with the unset value. | ||
237 | queryToSimpleQuery :: Query -> SimpleQuery | 238 | queryToSimpleQuery :: Query -> SimpleQuery |
238 | queryToSimpleQuery = catMaybes . L.map f | 239 | queryToSimpleQuery = catMaybes . L.map f |
239 | where | 240 | where |
240 | f (_, Nothing) = Nothing | 241 | f (_, Nothing) = Nothing |
241 | f (a, Just b ) = Just (a, b) | 242 | f (a, Just b ) = Just (a, b) |
242 | 243 | ||
243 | -- | Encode announce query and add it to the base tracker URL. | 244 | -- | Encode announce query to query string. |
244 | renderAnnounceQuery :: AnnounceQuery -> SimpleQuery | 245 | renderAnnounceQuery :: AnnounceQuery -> SimpleQuery |
245 | renderAnnounceQuery = queryToSimpleQuery . toQuery | 246 | renderAnnounceQuery = queryToSimpleQuery . toQuery |
246 | 247 | ||
@@ -293,35 +294,40 @@ instance FromParam Event where | |||
293 | (x, xs) <- BC.uncons bs | 294 | (x, xs) <- BC.uncons bs |
294 | readMaybe $ BC.unpack $ BC.cons (Char.toUpper x) xs | 295 | readMaybe $ BC.unpack $ BC.cons (Char.toUpper x) xs |
295 | 296 | ||
297 | -- | 'ParamParseFailure' represent errors can occur while parsing HTTP | ||
298 | -- tracker requests. In case of failure, this can be used to provide | ||
299 | -- more informative 'statusCode' and 'statusMessage' in tracker | ||
300 | -- responses. | ||
301 | -- | ||
296 | data ParamParseFailure | 302 | data ParamParseFailure |
297 | = Missing QueryParam -- ^ param not found in query string; | 303 | = Missing QueryParam -- ^ param not found in query string; |
298 | | Invalid QueryParam BS.ByteString -- ^ param present but not valid. | 304 | | Invalid QueryParam BS.ByteString -- ^ param present but not valid. |
299 | deriving (Show, Eq) | 305 | deriving (Show, Eq) |
300 | 306 | ||
301 | type Result = Either ParamParseFailure | 307 | type ParseResult = Either ParamParseFailure |
302 | 308 | ||
303 | withError :: ParamParseFailure -> Maybe a -> Result a | 309 | withError :: ParamParseFailure -> Maybe a -> ParseResult a |
304 | withError e = maybe (Left e) Right | 310 | withError e = maybe (Left e) Right |
305 | 311 | ||
306 | reqParam :: FromParam a => QueryParam -> SimpleQuery -> Result a | 312 | reqParam :: FromParam a => QueryParam -> SimpleQuery -> ParseResult a |
307 | reqParam param xs = do | 313 | reqParam param xs = do |
308 | val <- withError (Missing param) $ L.lookup (paramName param) xs | 314 | val <- withError (Missing param) $ L.lookup (paramName param) xs |
309 | withError (Invalid param val) (fromParam val) | 315 | withError (Invalid param val) (fromParam val) |
310 | 316 | ||
311 | optParam :: FromParam a => QueryParam -> SimpleQuery -> Result (Maybe a) | 317 | optParam :: FromParam a => QueryParam -> SimpleQuery -> ParseResult (Maybe a) |
312 | optParam param ps | 318 | optParam param ps |
313 | | Just x <- L.lookup (paramName param) ps | 319 | | Just x <- L.lookup (paramName param) ps |
314 | = pure <$> withError (Invalid param x) (fromParam x) | 320 | = pure <$> withError (Invalid param x) (fromParam x) |
315 | | otherwise = pure Nothing | 321 | | otherwise = pure Nothing |
316 | 322 | ||
317 | parseProgress :: SimpleQuery -> Result Progress | 323 | parseProgress :: SimpleQuery -> ParseResult Progress |
318 | parseProgress params = Progress | 324 | parseProgress params = Progress |
319 | <$> reqParam ParamDownloaded params | 325 | <$> reqParam ParamDownloaded params |
320 | <*> reqParam ParamLeft params | 326 | <*> reqParam ParamLeft params |
321 | <*> reqParam ParamUploaded params | 327 | <*> reqParam ParamUploaded params |
322 | 328 | ||
323 | -- | Parse announce request from a query string. | 329 | -- | Parse announce request from a query string. |
324 | parseAnnounceQuery :: SimpleQuery -> Either ParamParseFailure AnnounceQuery | 330 | parseAnnounceQuery :: SimpleQuery -> ParseResult AnnounceQuery |
325 | parseAnnounceQuery params = AnnounceQuery | 331 | parseAnnounceQuery params = AnnounceQuery |
326 | <$> reqParam ParamInfoHash params | 332 | <$> reqParam ParamInfoHash params |
327 | <*> reqParam ParamPeerId params | 333 | <*> reqParam ParamPeerId params |
@@ -331,25 +337,51 @@ parseAnnounceQuery params = AnnounceQuery | |||
331 | <*> optParam ParamNumWant params | 337 | <*> optParam ParamNumWant params |
332 | <*> optParam ParamEvent params | 338 | <*> optParam ParamEvent params |
333 | 339 | ||
340 | -- | Extensions for HTTP tracker protocol. | ||
334 | data AnnounceQueryExt = AnnounceQueryExt | 341 | data AnnounceQueryExt = AnnounceQueryExt |
335 | { extCompact :: Maybe Bool -- | "compact" param | 342 | { -- | If specified, "compact" parameter is used to advise the |
336 | , extNoPeerId :: Maybe Bool -- | "no_peer_id" param | 343 | -- tracker to send peer id list as: |
344 | -- | ||
345 | -- * bencoded list (extCompact = Just False); | ||
346 | -- * or more compact binary string (extCompact = Just True). | ||
347 | -- | ||
348 | -- The later is prefered since compact peer list will reduce the | ||
349 | -- size of tracker responses. Hovewer, if tracker do not support | ||
350 | -- this extension then it can return peer list in either form. | ||
351 | -- | ||
352 | -- For more info see: <http://www.bittorrent.org/beps/bep_0023.html> | ||
353 | -- | ||
354 | extCompact :: !(Maybe Bool) | ||
355 | |||
356 | -- | If specified, "no_peer_id" parameter is used advise tracker | ||
357 | -- to either send or not to send peer id in tracker response. | ||
358 | -- Tracker may not support this extension as well. | ||
359 | -- | ||
360 | -- For more info see: | ||
361 | -- <http://permalink.gmane.org/gmane.network.bit-torrent.general/4030> | ||
362 | -- | ||
363 | , extNoPeerId :: !(Maybe Bool) | ||
337 | } deriving (Show, Eq, Typeable) | 364 | } deriving (Show, Eq, Typeable) |
338 | 365 | ||
366 | -- | Parse announce query extended part from query string. | ||
339 | parseAnnounceQueryExt :: SimpleQuery -> AnnounceQueryExt | 367 | parseAnnounceQueryExt :: SimpleQuery -> AnnounceQueryExt |
340 | parseAnnounceQueryExt = undefined | 368 | parseAnnounceQueryExt = undefined |
341 | 369 | ||
370 | -- | Render announce query extended part to query string. | ||
342 | renderAnnounceQueryExt :: AnnounceQueryExt -> SimpleQuery | 371 | renderAnnounceQueryExt :: AnnounceQueryExt -> SimpleQuery |
343 | renderAnnounceQueryExt = undefined | 372 | renderAnnounceQueryExt = undefined |
344 | 373 | ||
374 | -- | HTTP tracker request with extensions. | ||
345 | data AnnounceRequest = AnnounceRequest | 375 | data AnnounceRequest = AnnounceRequest |
346 | { announceQuery :: AnnounceQuery | 376 | { announceQuery :: AnnounceQuery -- ^ Request query params. |
347 | , announceAdvises :: AnnounceQueryExt | 377 | , announceAdvises :: AnnounceQueryExt -- ^ Optional advises to the tracker. |
348 | } deriving (Show, Eq, Typeable) | 378 | } deriving (Show, Eq, Typeable) |
349 | 379 | ||
350 | parseAnnounceRequest :: SimpleQuery -> Either ParamParseFailure AnnounceRequest | 380 | -- | Parse announce request from query string. |
381 | parseAnnounceRequest :: SimpleQuery -> ParseResult AnnounceRequest | ||
351 | parseAnnounceRequest = undefined | 382 | parseAnnounceRequest = undefined |
352 | 383 | ||
384 | -- | Render announce request to query string. | ||
353 | renderAnnounceRequest :: AnnounceRequest -> SimpleQuery | 385 | renderAnnounceRequest :: AnnounceRequest -> SimpleQuery |
354 | renderAnnounceRequest = undefined | 386 | renderAnnounceRequest = undefined |
355 | 387 | ||
@@ -479,35 +511,35 @@ instance Serialize AnnounceInfo where | |||
479 | defaultNumWant :: Int | 511 | defaultNumWant :: Int |
480 | defaultNumWant = 50 | 512 | defaultNumWant = 50 |
481 | 513 | ||
514 | -- | Reasonable upper bound of numwant parameter. | ||
482 | defaultMaxNumWant :: Int | 515 | defaultMaxNumWant :: Int |
483 | defaultMaxNumWant = 200 | 516 | defaultMaxNumWant = 200 |
484 | 517 | ||
518 | -- | Widely used reannounce interval. Note: tracker clients should not | ||
519 | -- use this value! | ||
485 | defaultReannounceInterval :: Int | 520 | defaultReannounceInterval :: Int |
486 | defaultReannounceInterval = 30 * 60 | 521 | defaultReannounceInterval = 30 * 60 |
487 | 522 | ||
488 | |||
489 | missingOffset :: Int | 523 | missingOffset :: Int |
490 | missingOffset = 101 | 524 | missingOffset = 101 |
491 | 525 | ||
492 | invalidOffset :: Int | 526 | invalidOffset :: Int |
493 | invalidOffset = 150 | 527 | invalidOffset = 150 |
494 | 528 | ||
495 | -- | Get HTTP response error code from a announce params parse | ||
496 | -- failure. | ||
497 | -- | ||
498 | -- For more info see: | ||
499 | -- <https://wiki.theory.org/BitTorrent_Tracker_Protocol#Response_Codes> | ||
500 | -- | ||
501 | parseFailureCode :: ParamParseFailure -> Int | 529 | parseFailureCode :: ParamParseFailure -> Int |
502 | parseFailureCode (Missing param ) = missingOffset + fromEnum param | 530 | parseFailureCode (Missing param ) = missingOffset + fromEnum param |
503 | parseFailureCode (Invalid param _) = invalidOffset + fromEnum param | 531 | parseFailureCode (Invalid param _) = invalidOffset + fromEnum param |
504 | 532 | ||
505 | -- | Human readable message | ||
506 | parseFailureMessage :: ParamParseFailure -> BS.ByteString | 533 | parseFailureMessage :: ParamParseFailure -> BS.ByteString |
507 | parseFailureMessage e = BS.concat $ case e of | 534 | parseFailureMessage e = BS.concat $ case e of |
508 | Missing p -> ["Missing parameter: ", paramName p] | 535 | Missing p -> ["Missing parameter: ", paramName p] |
509 | Invalid p v -> ["Invalid parameter: ", paramName p, " = ", v] | 536 | Invalid p v -> ["Invalid parameter: ", paramName p, " = ", v] |
510 | 537 | ||
538 | -- | Get HTTP response status from a announce params parse failure. | ||
539 | -- | ||
540 | -- For more info see: | ||
541 | -- <https://wiki.theory.org/BitTorrent_Tracker_Protocol#Response_Codes> | ||
542 | -- | ||
511 | parseFailureStatus :: ParamParseFailure -> Status | 543 | parseFailureStatus :: ParamParseFailure -> Status |
512 | parseFailureStatus = mkStatus <$> parseFailureCode <*> parseFailureMessage | 544 | parseFailureStatus = mkStatus <$> parseFailureCode <*> parseFailureMessage |
513 | 545 | ||
@@ -515,6 +547,9 @@ parseFailureStatus = mkStatus <$> parseFailureCode <*> parseFailureMessage | |||
515 | Scrape message | 547 | Scrape message |
516 | -----------------------------------------------------------------------} | 548 | -----------------------------------------------------------------------} |
517 | 549 | ||
550 | -- | Scrape query used to specify a set of torrent to scrape. | ||
551 | -- If list is empty then tracker should return scrape info about each | ||
552 | -- torrent. | ||
518 | type ScrapeQuery = [InfoHash] | 553 | type ScrapeQuery = [InfoHash] |
519 | 554 | ||
520 | -- TODO | 555 | -- TODO |
@@ -536,15 +571,17 @@ scrapeParam = "info_hash" | |||
536 | isScrapeParam :: BS.ByteString -> Bool | 571 | isScrapeParam :: BS.ByteString -> Bool |
537 | isScrapeParam = (==) scrapeParam | 572 | isScrapeParam = (==) scrapeParam |
538 | 573 | ||
574 | -- | Parse scrape query to query string. | ||
575 | parseScrapeQuery :: SimpleQuery -> ScrapeQuery | ||
576 | parseScrapeQuery | ||
577 | = catMaybes . L.map (fromParam . snd) . L.filter (isScrapeParam . fst) | ||
578 | |||
579 | -- | Render scrape query to query string. | ||
539 | renderScrapeQuery :: ScrapeQuery -> SimpleQuery | 580 | renderScrapeQuery :: ScrapeQuery -> SimpleQuery |
540 | renderScrapeQuery = queryToSimpleQuery . L.map mkPair | 581 | renderScrapeQuery = queryToSimpleQuery . L.map mkPair |
541 | where | 582 | where |
542 | mkPair ih = (scrapeParam, toQueryValue ih) | 583 | mkPair ih = (scrapeParam, toQueryValue ih) |
543 | 584 | ||
544 | parseScrapeQuery :: SimpleQuery -> ScrapeQuery | ||
545 | parseScrapeQuery | ||
546 | = catMaybes . L.map (fromParam . snd) . L.filter (isScrapeParam . fst) | ||
547 | |||
548 | -- | Overall information about particular torrent. | 585 | -- | Overall information about particular torrent. |
549 | data ScrapeEntry = ScrapeEntry { | 586 | data ScrapeEntry = ScrapeEntry { |
550 | -- | Number of seeders - peers with the entire file. | 587 | -- | Number of seeders - peers with the entire file. |