summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bittorrent.cabal8
-rw-r--r--src/Data/Torrent/Block.hs11
-rw-r--r--src/Data/Torrent/Layout.hs14
-rw-r--r--src/Data/Torrent/Piece.hs7
-rw-r--r--src/Network/BitTorrent/Tracker/RPC/Message.hs49
-rw-r--r--src/Network/BitTorrent/Tracker/Wai.hs102
6 files changed, 167 insertions, 24 deletions
diff --git a/bittorrent.cabal b/bittorrent.cabal
index 762bff01..c5a8a476 100644
--- a/bittorrent.cabal
+++ b/bittorrent.cabal
@@ -59,21 +59,21 @@ library
59-- , Network.BitTorrent.DHT.Protocol 59-- , Network.BitTorrent.DHT.Protocol
60-- , Network.BitTorrent.DHT.Session 60-- , Network.BitTorrent.DHT.Session
61-- , Network.BitTorrent.Exchange 61-- , Network.BitTorrent.Exchange
62 , Network.BitTorrent.Exchange.Assembler 62-- , Network.BitTorrent.Exchange.Assembler
63 , Network.BitTorrent.Exchange.Message 63-- , Network.BitTorrent.Exchange.Message
64-- , Network.BitTorrent.Exchange.Session 64-- , Network.BitTorrent.Exchange.Session
65-- , Network.BitTorrent.Exchange.Status 65-- , Network.BitTorrent.Exchange.Status
66-- , Network.BitTorrent.Exchange.Wire 66-- , Network.BitTorrent.Exchange.Wire
67-- , Network.BitTorrent.Extension 67-- , Network.BitTorrent.Extension
68-- , Network.BitTorrent.Tracker 68-- , Network.BitTorrent.Tracker
69-- , Network.BitTorrent.Tracker.RPC 69-- , Network.BitTorrent.Tracker.RPC
70 , Network.BitTorrent.Tracker.Wai
70 , Network.BitTorrent.Tracker.RPC.Message 71 , Network.BitTorrent.Tracker.RPC.Message
71 , Network.BitTorrent.Tracker.RPC.HTTP 72 , Network.BitTorrent.Tracker.RPC.HTTP
72 , Network.BitTorrent.Tracker.RPC.UDP 73 , Network.BitTorrent.Tracker.RPC.UDP
73-- , Network.BitTorrent.Tracker.Session 74-- , Network.BitTorrent.Tracker.Session
74-- , Network.BitTorrent.Session 75-- , Network.BitTorrent.Session
75-- , Network.BitTorrent.Session.Types 76-- , Network.BitTorrent.Session.Types
76-- , System.IO.MMap.Fixed
77-- , System.Torrent.Storage 77-- , System.Torrent.Storage
78 other-modules: Paths_bittorrent 78 other-modules: Paths_bittorrent
79 79
@@ -86,7 +86,6 @@ library
86 -- Control 86 -- Control
87 , deepseq 87 , deepseq
88 , lens 88 , lens
89-- , mtl
90 , resourcet 89 , resourcet
91-- , transformers 90-- , transformers
92 91
@@ -131,6 +130,7 @@ library
131 , network >= 2.4 130 , network >= 2.4
132 , http-types >= 0.7 131 , http-types >= 0.7
133 , http-conduit 132 , http-conduit
133 , wai
134-- , krpc 134-- , krpc
135 135
136 -- System 136 -- System
diff --git a/src/Data/Torrent/Block.hs b/src/Data/Torrent/Block.hs
index fd12b7a0..089217fa 100644
--- a/src/Data/Torrent/Block.hs
+++ b/src/Data/Torrent/Block.hs
@@ -7,9 +7,11 @@
7-- 7--
8-- Blocks are used to transfer pieces. 8-- Blocks are used to transfer pieces.
9-- 9--
10{-# LANGUAGE GeneralizedNewtypeDeriving #-}
11{-# LANGUAGE TemplateHaskell #-}
12{-# LANGUAGE FlexibleInstances #-} 10{-# LANGUAGE FlexibleInstances #-}
11{-# LANGUAGE TemplateHaskell #-}
12{-# LANGUAGE DeriveFunctor #-}
13{-# LANGUAGE DeriveDataTypeable #-}
14{-# LANGUAGE GeneralizedNewtypeDeriving #-}
13module Data.Torrent.Block 15module Data.Torrent.Block
14 ( -- * Piece attributes 16 ( -- * Piece attributes
15 PieceIx 17 PieceIx
@@ -38,6 +40,7 @@ import qualified Data.ByteString.Lazy as Lazy
38import Data.Char 40import Data.Char
39import Data.List as L 41import Data.List as L
40import Data.Serialize as S 42import Data.Serialize as S
43import Data.Typeable
41import Text.PrettyPrint 44import Text.PrettyPrint
42import Text.PrettyPrint.Class 45import Text.PrettyPrint.Class
43 46
@@ -95,7 +98,7 @@ data BlockIx = BlockIx {
95 98
96 -- | Block size starting from offset. 99 -- | Block size starting from offset.
97 , ixLength :: {-# UNPACK #-} !BlockSize 100 , ixLength :: {-# UNPACK #-} !BlockSize
98 } deriving (Show, Eq) 101 } deriving (Show, Eq, Typeable)
99 102
100$(deriveJSON defaultOptions { fieldLabelModifier = (L.map toLower . L.dropWhile isLower) } ''BlockIx) 103$(deriveJSON defaultOptions { fieldLabelModifier = (L.map toLower . L.dropWhile isLower) } ''BlockIx)
101 104
@@ -148,7 +151,7 @@ data Block payload = Block {
148 151
149 -- | Payload bytes. 152 -- | Payload bytes.
150 , blkData :: !payload 153 , blkData :: !payload
151 } deriving (Show, Eq) 154 } deriving (Show, Eq, Functor, Typeable)
152 155
153-- | Payload is ommitted. 156-- | Payload is ommitted.
154instance Pretty (Block Lazy.ByteString) where 157instance Pretty (Block Lazy.ByteString) where
diff --git a/src/Data/Torrent/Layout.hs b/src/Data/Torrent/Layout.hs
index d39c6c04..c1e26d48 100644
--- a/src/Data/Torrent/Layout.hs
+++ b/src/Data/Torrent/Layout.hs
@@ -50,7 +50,7 @@ module Data.Torrent.Layout
50 , blockCount 50 , blockCount
51 51
52 -- * Flat file layout 52 -- * Flat file layout
53 , Layout 53 , FileLayout
54 , flatLayout 54 , flatLayout
55 , accumOffsets 55 , accumOffsets
56 , fileOffset 56 , fileOffset
@@ -286,13 +286,13 @@ blockCount blkSize ci = contentLength ci `sizeInBase` blkSize
286-- coalesce all the files in the given order to get the linear block 286-- coalesce all the files in the given order to get the linear block
287-- address space. 287-- address space.
288-- 288--
289type Layout a = [(FilePath, a)] 289type FileLayout a = [(FilePath, a)]
290 290
291-- | Extract files layout from torrent info with the given root path. 291-- | Extract files layout from torrent info with the given root path.
292flatLayout 292flatLayout
293 :: FilePath -- ^ Root path for the all torrent files. 293 :: FilePath -- ^ Root path for the all torrent files.
294 -> LayoutInfo -- ^ Torrent content information. 294 -> LayoutInfo -- ^ Torrent content information.
295 -> Layout FileSize -- ^ The all file paths prefixed with the given root. 295 -> FileLayout FileSize -- ^ The all file paths prefixed with the given root.
296flatLayout prefixPath SingleFile { liFile = FileInfo {..} } 296flatLayout prefixPath SingleFile { liFile = FileInfo {..} }
297 = [(prefixPath </> BC.unpack fiName, fiLength)] 297 = [(prefixPath </> BC.unpack fiName, fiLength)]
298flatLayout prefixPath MultiFile {..} = L.map mkPath liFiles 298flatLayout prefixPath MultiFile {..} = L.map mkPath liFiles
@@ -303,14 +303,14 @@ flatLayout prefixPath MultiFile {..} = L.map mkPath liFiles
303 </> joinPath (L.map BC.unpack fiName) 303 </> joinPath (L.map BC.unpack fiName)
304 304
305-- | Calculate offset of each file based on its length, incrementally. 305-- | Calculate offset of each file based on its length, incrementally.
306accumOffsets :: Layout FileSize -> Layout FileOffset 306accumOffsets :: FileLayout FileSize -> FileLayout FileOffset
307accumOffsets = go 0 307accumOffsets = go 0
308 where 308 where
309 go !_ [] = [] 309 go !_ [] = []
310 go !offset ((n, s) : xs) = (n, offset) : go (offset + s) xs 310 go !offset ((n, s) : xs) = (n, offset) : go (offset + s) xs
311 311
312-- | Gives global offset of a content file for a given full path. 312-- | Gives global offset of a content file for a given full path.
313fileOffset :: FilePath -> Layout FileOffset -> Maybe FileOffset 313fileOffset :: FilePath -> FileLayout FileOffset -> Maybe FileOffset
314fileOffset = lookup 314fileOffset = lookup
315{-# INLINE fileOffset #-} 315{-# INLINE fileOffset #-}
316 316
diff --git a/src/Data/Torrent/Piece.hs b/src/Data/Torrent/Piece.hs
index f7c6257b..31680ce8 100644
--- a/src/Data/Torrent/Piece.hs
+++ b/src/Data/Torrent/Piece.hs
@@ -7,8 +7,9 @@
7-- 7--
8-- Pieces are used to validate torrent content. 8-- Pieces are used to validate torrent content.
9-- 9--
10{-# LANGUAGE TemplateHaskell #-} 10{-# LANGUAGE TemplateHaskell #-}
11{-# LANGUAGE DeriveDataTypeable #-} 11{-# LANGUAGE DeriveDataTypeable #-}
12{-# LANGUAGE DeriveFunctor #-}
12{-# LANGUAGE GeneralizedNewtypeDeriving #-} 13{-# LANGUAGE GeneralizedNewtypeDeriving #-}
13module Data.Torrent.Piece 14module Data.Torrent.Piece
14 ( -- * Piece attributes 15 ( -- * Piece attributes
@@ -115,7 +116,7 @@ data Piece a = Piece
115 116
116 -- | Payload. 117 -- | Payload.
117 , pieceData :: !a 118 , pieceData :: !a
118 } deriving (Show, Read, Eq, Typeable) 119 } deriving (Show, Read, Eq, Functor, Typeable)
119 120
120$(deriveJSON defaultOptions { fieldLabelModifier = (L.map toLower . L.dropWhile isLower) } ''Piece) 121$(deriveJSON defaultOptions { fieldLabelModifier = (L.map toLower . L.dropWhile isLower) } ''Piece)
121 122
diff --git a/src/Network/BitTorrent/Tracker/RPC/Message.hs b/src/Network/BitTorrent/Tracker/RPC/Message.hs
index 213f1e83..3900ff64 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
226filterMaybes :: [(a, Maybe b)] -> [(a, b)] 237queryToSimpleQuery :: Query -> SimpleQuery
227filterMaybes = catMaybes . L.map f 238queryToSimpleQuery = 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.
233renderAnnounceQuery :: AnnounceQuery -> SimpleQuery 244renderAnnounceQuery :: AnnounceQuery -> SimpleQuery
234renderAnnounceQuery = filterMaybes . toQuery 245renderAnnounceQuery = queryToSimpleQuery . toQuery
235 246
236data QueryParam 247data 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 334data 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
339parseAnnounceQueryExt :: SimpleQuery -> AnnounceQueryExt
340parseAnnounceQueryExt = undefined
341
342renderAnnounceQueryExt :: AnnounceQueryExt -> SimpleQuery
343renderAnnounceQueryExt = undefined
344
345data AnnounceRequest = AnnounceRequest
346 { announceQuery :: AnnounceQuery
347 , announceAdvises :: AnnounceQueryExt
348 } deriving (Show, Eq, Typeable)
349
350parseAnnounceRequest :: SimpleQuery -> Either ParamParseFailure AnnounceRequest
351parseAnnounceRequest = undefined
352
353renderAnnounceRequest :: AnnounceRequest -> SimpleQuery
354renderAnnounceRequest = undefined
325 355
326{----------------------------------------------------------------------- 356{-----------------------------------------------------------------------
327-- Announce response 357-- Announce response
@@ -449,6 +479,13 @@ instance Serialize AnnounceInfo where
449defaultNumWant :: Int 479defaultNumWant :: Int
450defaultNumWant = 50 480defaultNumWant = 50
451 481
482defaultMaxNumWant :: Int
483defaultMaxNumWant = 200
484
485defaultReannounceInterval :: Int
486defaultReannounceInterval = 30 * 60
487
488
452missingOffset :: Int 489missingOffset :: Int
453missingOffset = 101 490missingOffset = 101
454 491
@@ -500,7 +537,7 @@ isScrapeParam :: BS.ByteString -> Bool
500isScrapeParam = (==) scrapeParam 537isScrapeParam = (==) scrapeParam
501 538
502renderScrapeQuery :: ScrapeQuery -> SimpleQuery 539renderScrapeQuery :: ScrapeQuery -> SimpleQuery
503renderScrapeQuery = filterMaybes . L.map mkPair 540renderScrapeQuery = 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 #-}
11module Network.BitTorrent.Tracker.Wai
12 ( tracker
13 ) where
14
15import Control.Monad.Trans.Resource
16import Data.BEncode as BE
17import Data.ByteString
18import Data.Default
19import Data.List as L
20import Network.HTTP.Types
21import Network.Wai
22
23import Data.Torrent.Progress
24import Network.BitTorrent.Core.PeerId
25import Network.BitTorrent.Core.PeerAddr
26import Network.BitTorrent.Tracker.RPC.Message
27
28
29data 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
60instance 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
72getAnnounceR :: AnnounceRequest -> ResourceT IO AnnounceInfo
73getAnnounceR = undefined
74
75getScrapeR :: ScrapeQuery -> ResourceT IO ScrapeInfo
76getScrapeR = undefined
77
78-- content-type: "text/plain" ?
79tracker :: Application
80tracker 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