summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bittorrent.cabal14
-rw-r--r--examples/Client.hs17
-rw-r--r--examples/Main.hs18
-rw-r--r--src/Network/BitTorrent/Client.hs33
-rw-r--r--src/Network/BitTorrent/Client/Swarm.hs49
-rw-r--r--src/Network/BitTorrent/Tracker/Wai.hs2
-rw-r--r--tests/Network/BitTorrent/Tracker/RPCSpec.hs29
7 files changed, 138 insertions, 24 deletions
diff --git a/bittorrent.cabal b/bittorrent.cabal
index e420beba..69a1207b 100644
--- a/bittorrent.cabal
+++ b/bittorrent.cabal
@@ -51,6 +51,7 @@ library
51 Data.Torrent.Tree 51 Data.Torrent.Tree
52-- Network.BitTorrent 52-- Network.BitTorrent
53 Network.BitTorrent.Client 53 Network.BitTorrent.Client
54 Network.BitTorrent.Client.Swarm
54 Network.BitTorrent.Core 55 Network.BitTorrent.Core
55 Network.BitTorrent.Core.Fingerprint 56 Network.BitTorrent.Core.Fingerprint
56 Network.BitTorrent.Core.PeerId 57 Network.BitTorrent.Core.PeerId
@@ -95,7 +96,7 @@ library
95 -- Concurrency 96 -- Concurrency
96-- , SafeSemaphore 97-- , SafeSemaphore
97-- , BoundedChan >= 1.0.1.0 98-- , BoundedChan >= 1.0.1.0
98-- , stm >= 2.4 99 , stm >= 2.4
99 100
100 -- Streaming 101 -- Streaming
101 , conduit >= 1.0 102 , conduit >= 1.0
@@ -110,7 +111,7 @@ library
110 , intset >= 0.1 111 , intset >= 0.1
111 , split >= 0.2 112 , split >= 0.2
112 , text >= 0.11.0 113 , text >= 0.11.0
113-- , unordered-containers 114 , unordered-containers
114 , vector >= 0.10 115 , vector >= 0.10
115 116
116 -- Hashing 117 -- Hashing
@@ -211,3 +212,12 @@ test-suite spec
211-- 212--
212-- , bittorrent 213-- , bittorrent
213-- ghc-options: -O2 -Wall -fno-warn-orphans 214-- ghc-options: -O2 -Wall -fno-warn-orphans
215
216executable client-example
217 default-language: Haskell2010
218 hs-source-dirs: examples
219 main-is: Client.hs
220 build-depends: base == 4.*
221 , bittorrent
222 , pretty-class
223 , data-default \ No newline at end of file
diff --git a/examples/Client.hs b/examples/Client.hs
new file mode 100644
index 00000000..efafba72
--- /dev/null
+++ b/examples/Client.hs
@@ -0,0 +1,17 @@
1module Main (main) where
2
3import Control.Concurrent
4import Data.Default
5import Data.Torrent
6import Network.BitTorrent.Client
7import System.Environment
8import Text.PrettyPrint.Class
9
10
11main :: IO ()
12main = do
13 [path] <- getArgs
14 torrent <- fromFile path
15 client <- newClient def
16 addTorrent torrent client
17 return () \ No newline at end of file
diff --git a/examples/Main.hs b/examples/Main.hs
deleted file mode 100644
index ef12cc70..00000000
--- a/examples/Main.hs
+++ /dev/null
@@ -1,18 +0,0 @@
1module Main (main) where
2
3import Control.Concurrent
4import Network.BitTorrent
5import Network.BitTorrent.Sessions
6import System.Environment
7
8main :: IO ()
9main = do
10 [path] <- getArgs
11 torrent <- fromFile path
12 print (contentLayout "./" (tInfo torrent))
13 let loc = TorrentLoc path "/tmp"
14
15 withDefaultClient (head defaultPorts) 3000 $ \ client -> do
16 openSwarmSession client loc
17 threadDelay 1000000000000
18 return ()
diff --git a/src/Network/BitTorrent/Client.hs b/src/Network/BitTorrent/Client.hs
index fc2b904a..a6151857 100644
--- a/src/Network/BitTorrent/Client.hs
+++ b/src/Network/BitTorrent/Client.hs
@@ -1,16 +1,21 @@
1module Network.BitTorrent.Client 1module Network.BitTorrent.Client
2 ( Options (..) 2 ( Options (..)
3 , Client (..) 3 , Client
4 , newClient
5 , addTorrent
4 ) where 6 ) where
5 7
8import Control.Concurrent.STM
6import Data.Default 9import Data.Default
7import Data.Function 10import Data.Function
11import Data.HashMap.Strict as HM
8import Data.Ord 12import Data.Ord
9import Data.Text 13import Data.Text
10import Network 14import Network
11 15
12import Data.Torrent 16import Data.Torrent
13import Data.Torrent.InfoHash 17import Data.Torrent.InfoHash
18import Network.BitTorrent.Client.Swarm
14import Network.BitTorrent.Core 19import Network.BitTorrent.Core
15import Network.BitTorrent.Exchange.Message 20import Network.BitTorrent.Exchange.Message
16 21
@@ -19,6 +24,7 @@ data Options = Options
19 { fingerprint :: Fingerprint 24 { fingerprint :: Fingerprint
20 , name :: Text 25 , name :: Text
21 , port :: PortNumber 26 , port :: PortNumber
27 , extensions :: [Extension]
22 } 28 }
23 29
24instance Default Options where 30instance Default Options where
@@ -26,11 +32,14 @@ instance Default Options where
26 { fingerprint = def 32 { fingerprint = def
27 , name = "hs-bittorrent" 33 , name = "hs-bittorrent"
28 , port = 6882 34 , port = 6882
35 , extensions = []
29 } 36 }
30 37
31data Client = Client 38data Client = Client
32 { clientPeerId :: !PeerId 39 { clientPeerId :: !PeerId
33 , allowedExtensions :: !Caps 40 , clientListenerPort :: !PortNumber
41 , allowedExtensions :: !Caps
42 , torrents :: TVar (HashMap InfoHash Swarm)
34 } 43 }
35 44
36instance Eq Client where 45instance Eq Client where
@@ -38,3 +47,21 @@ instance Eq Client where
38 47
39instance Ord Client where 48instance Ord Client where
40 compare = comparing clientPeerId 49 compare = comparing clientPeerId
50
51newClient :: Options -> IO Client
52newClient Options {..} = do
53 pid <- genPeerId
54 ts <- newTVarIO HM.empty
55 return Client
56 { clientPeerId = pid
57 , clientListenerPort = port
58 , allowedExtensions = toCaps extensions
59 , torrents = ts
60 }
61
62addTorrent :: Torrent -> Client -> IO ()
63addTorrent t Client {..} = do
64 leecher <- newLeecher clientPeerId clientListenerPort t
65 let ih = idInfoHash (tInfoDict t)
66 atomically $ modifyTVar' torrents (HM.insert ih leecher)
67 askPeers leecher >>= print \ No newline at end of file
diff --git a/src/Network/BitTorrent/Client/Swarm.hs b/src/Network/BitTorrent/Client/Swarm.hs
new file mode 100644
index 00000000..a9dca048
--- /dev/null
+++ b/src/Network/BitTorrent/Client/Swarm.hs
@@ -0,0 +1,49 @@
1module Network.BitTorrent.Client.Swarm
2 ( Swarm
3 , newLeecher
4 , askPeers
5 ) where
6
7import Data.Default
8import Network
9
10import Data.Torrent
11import Data.Torrent.InfoHash
12import Network.BitTorrent.Core
13import Network.BitTorrent.Tracker.Message
14import Network.BitTorrent.Tracker.RPC as RPC
15
16
17data Swarm = Swarm
18 { swarmTopic :: InfoHash
19 , thisPeerId :: PeerId
20 , listenerPort :: PortNumber
21 , trackerConn :: Tracker
22-- , infoDict ::
23 }
24
25newLeecher :: PeerId -> PortNumber -> Torrent -> IO Swarm
26newLeecher pid port Torrent {..} = do
27 tracker <- connect tAnnounce
28 return Swarm
29 { swarmTopic = idInfoHash tInfoDict
30 , thisPeerId = pid
31 , listenerPort = port
32 , trackerConn = tracker
33 }
34
35getAnnounceQuery :: Swarm -> AnnounceQuery
36getAnnounceQuery Swarm {..} = AnnounceQuery
37 { reqInfoHash = swarmTopic
38 , reqPeerId = thisPeerId
39 , reqPort = listenerPort
40 , reqProgress = def
41 , reqIP = Nothing
42 , reqNumWant = Nothing
43 , reqEvent = Nothing
44 }
45
46askPeers :: Swarm -> IO [PeerAddr]
47askPeers s @ Swarm {..} = do
48 AnnounceInfo {..} <- RPC.announce (getAnnounceQuery s) trackerConn
49 return (getPeerList respPeers)
diff --git a/src/Network/BitTorrent/Tracker/Wai.hs b/src/Network/BitTorrent/Tracker/Wai.hs
index c43c7a3a..69510fa0 100644
--- a/src/Network/BitTorrent/Tracker/Wai.hs
+++ b/src/Network/BitTorrent/Tracker/Wai.hs
@@ -79,7 +79,7 @@ getAnnounceR = undefined
79getScrapeR :: TrackerSettings -> ScrapeQuery -> ResourceT IO ScrapeInfo 79getScrapeR :: TrackerSettings -> ScrapeQuery -> ResourceT IO ScrapeInfo
80getScrapeR = undefined 80getScrapeR = undefined
81 81
82-- content-type: "text/plain" ? 82-- content-type: "text/plain"!
83tracker :: TrackerSettings -> Application 83tracker :: TrackerSettings -> Application
84tracker settings Request {..} 84tracker settings Request {..}
85 | requestMethod /= methodGet 85 | requestMethod /= methodGet
diff --git a/tests/Network/BitTorrent/Tracker/RPCSpec.hs b/tests/Network/BitTorrent/Tracker/RPCSpec.hs
new file mode 100644
index 00000000..6bc72e86
--- /dev/null
+++ b/tests/Network/BitTorrent/Tracker/RPCSpec.hs
@@ -0,0 +1,29 @@
1module Network.BitTorrent.Tracker.RPCSpec (spec) where
2import Control.Monad
3import Data.Default
4import Data.List as L
5import Network.URI
6import Test.Hspec
7
8import Network.BitTorrent.Tracker.MessageSpec hiding (spec)
9import Network.BitTorrent.Tracker.RPC.HTTPSpec as HTTP hiding (spec)
10import Network.BitTorrent.Tracker.RPC.UDPSpec as UDP hiding (spec)
11import Network.BitTorrent.Tracker.RPC as RPC
12
13uris :: [URI]
14uris = UDP.trackerURIs ++ HTTP.trackerURIs
15
16spec :: Spec
17spec = do
18 forM_ uris $ \ uri ->
19 context (show uri) $ do
20 describe "announce" $ do
21 it "have valid response" $ do
22 q <- arbitrarySample
23 info <- connect uri >>= announce q
24 validateInfo q info
25
26 describe "scrape" $ do
27 it "have valid response" $ do
28 xs <- connect uri >>= scrape [def]
29 L.length xs `shouldSatisfy` (>= 1)