summaryrefslogtreecommitdiff
path: root/src/Network
diff options
context:
space:
mode:
authorSam Truzjan <pxqr.sta@gmail.com>2014-02-15 07:21:59 +0400
committerSam Truzjan <pxqr.sta@gmail.com>2014-02-15 07:21:59 +0400
commit7e880e776082177731455842f8bb8a85c2844dc2 (patch)
tree1bcbdc3d59d75d8ce7371aee253714410799494e /src/Network
parent406e274759b325b9987634e5f9e1536760b87c8f (diff)
Add skeleton of tracker application
Diffstat (limited to 'src/Network')
-rw-r--r--src/Network/BitTorrent/Tracker/Wai.hs148
1 files changed, 138 insertions, 10 deletions
diff --git a/src/Network/BitTorrent/Tracker/Wai.hs b/src/Network/BitTorrent/Tracker/Wai.hs
index 29e0e952..df56e378 100644
--- a/src/Network/BitTorrent/Tracker/Wai.hs
+++ b/src/Network/BitTorrent/Tracker/Wai.hs
@@ -12,19 +12,30 @@ module Network.BitTorrent.Tracker.Wai
12 ( -- * Configuration 12 ( -- * Configuration
13 TrackerSettings (..) 13 TrackerSettings (..)
14 14
15 -- * Tracker
16 , Tracker
17 , newTracker
18 , closeTracker
19 , withTracker
20
15 -- * Application 21 -- * Application
16 , tracker 22 , tracker
17 ) where 23 ) where
18 24
19import Control.Applicative 25import Control.Applicative
26import Control.Concurrent.STM
27import Control.Exception
28import Control.Monad.Trans
20import Control.Monad.Trans.Resource 29import Control.Monad.Trans.Resource
21import Data.BEncode as BE 30import Data.BEncode as BE
22import Data.ByteString
23import Data.Default 31import Data.Default
32import Data.HashMap.Strict as HM
24import Data.List as L 33import Data.List as L
34import Data.Maybe
25import Network.HTTP.Types 35import Network.HTTP.Types
26import Network.Wai 36import Network.Wai
27 37
38import Data.Torrent.InfoHash
28import Data.Torrent.Progress 39import Data.Torrent.Progress
29import Network.BitTorrent.Tracker.Message 40import Network.BitTorrent.Tracker.Message
30 41
@@ -79,17 +90,134 @@ instance Default TrackerSettings where
79 , noPeerId = False 90 , noPeerId = False
80 } 91 }
81 92
93{-----------------------------------------------------------------------
94-- Swarm
95-----------------------------------------------------------------------}
96
97type PeerSet = [()]
98
99data Swarm = Swarm
100 { leechers :: !PeerSet
101 , seeders :: !PeerSet
102 , downloaded :: {-# UNPACK #-} !Int
103 }
82 104
105instance Default Swarm where
106 def = Swarm
107 { leechers = []
108 , seeders = []
109 , downloaded = 0
110 }
111{-
112started :: PeerInfo -> Swarm -> Swarm
113started info Swarm {..} = Swarm
114 { leechers = insert info leechers
115 , seeders = delete info seeders
116 , downloaded = downloaded
117 }
118
119regular :: PeerInfo -> Swarm -> Swarm
120regular info Swarm {..} = undefined
121
122stopped :: PeerInfo -> Swarm -> Swarm
123stopped info Swarm {..} = Swarm
124 { leechers = delete info leechers
125 , seeders = delete info seeders
126 , downloaded = downloaded
127 }
128
129completed :: PeerInfo -> Swarm -> Swarm
130completed info Swarm {..} = Swarm
131 { leechers = delete info leechers
132 , seeders = insert info seeders
133 , downloaded = succ downloaded
134 }
135
136event :: Maybe Event -> Swarm -> Swarm
137event = undefined
138-}
139--peerList :: TrackerSettings -> Swarm -> PeerList IP
140peerList TrackerSettings {..} Swarm {..} = undefined --envelope peers
141 where
142 envelope = if compactPeerList then CompactPeerList else PeerList
143 peers = []
144
145announceInfo :: TrackerSettings -> Swarm -> AnnounceInfo
146announceInfo settings @ TrackerSettings {..} swarm @ Swarm {..} = AnnounceInfo
147 { respComplete = Just (L.length seeders)
148 , respIncomplete = Just (L.length leechers)
149 , respInterval = reannounceInterval
150 , respMinInterval = reannounceMinInterval
151 , respPeers = undefined -- peerList settings swarm
152 , respWarning = Nothing
153 }
154
155scrapeEntry :: Swarm -> ScrapeEntry
156scrapeEntry Swarm {..} = ScrapeEntry
157 { siComplete = L.length seeders
158 , siDownloaded = downloaded
159 , siIncomplete = L.length leechers
160 , siName = Nothing
161 }
83 162
84{----------------------------------------------------------------------- 163{-----------------------------------------------------------------------
85-- Handlers 164-- Tracker state
86-----------------------------------------------------------------------} 165-----------------------------------------------------------------------}
87 166
88getAnnounceR :: TrackerSettings -> AnnounceRequest -> ResourceT IO AnnounceInfo 167type Table = HashMap InfoHash Swarm
89getAnnounceR = undefined 168
169withSwarm :: TVar Table -> InfoHash -> (Maybe Swarm -> STM (a, Swarm)) -> STM a
170withSwarm tableRef infohash action = do
171 table <- readTVar tableRef
172 (res, swarm') <- action (HM.lookup infohash table)
173 writeTVar tableRef (HM.insert infohash swarm' table)
174 return res
175
176scrapeInfo :: ScrapeQuery -> Table -> [ScrapeEntry]
177scrapeInfo query table = do
178 infohash <- query
179 swarm <- maybeToList $ HM.lookup infohash table
180 return $ scrapeEntry swarm
181
182data TrackerState = TrackerState
183 { swarms :: !(TVar Table)
184 }
185
186newState :: IO TrackerState
187newState = TrackerState <$> newTVarIO HM.empty
188
189data Tracker = Tracker
190 { options :: !TrackerSettings
191 , state :: !TrackerState
192 }
193
194newTracker :: TrackerSettings -> IO Tracker
195newTracker opts = Tracker opts <$> newState
196
197closeTracker :: Tracker -> IO ()
198closeTracker _ = return ()
199
200withTracker :: TrackerSettings -> (Tracker -> IO a) -> IO a
201withTracker opts = bracket (newTracker opts) closeTracker
202
203{-----------------------------------------------------------------------
204-- Handlers
205-----------------------------------------------------------------------}
90 206
91getScrapeR :: TrackerSettings -> ScrapeQuery -> ResourceT IO ScrapeInfo 207getAnnounceR :: Tracker -> AnnounceRequest -> ResourceT IO AnnounceInfo
92getScrapeR = undefined 208getAnnounceR Tracker {..} AnnounceRequest {..} = do
209 return undefined
210{-
211 atomically $ do
212 withSwarm (swarms state) (reqInfoHash announceQuery) $ \ mswarm ->
213 case mswarm of
214 Nothing -> return undefined
215 Just s -> return undefined
216-}
217getScrapeR :: Tracker -> ScrapeQuery -> ResourceT IO ScrapeInfo
218getScrapeR Tracker {..} query = do
219 table <- liftIO $ readTVarIO (swarms state)
220 return $ undefined $ scrapeInfo query table
93 221
94{----------------------------------------------------------------------- 222{-----------------------------------------------------------------------
95-- Routing 223-- Routing
@@ -106,19 +234,19 @@ scrapeResponse info = responseLBS ok200 headers $ BE.encode info
106 headers = [(hContentType, scrapeType)] 234 headers = [(hContentType, scrapeType)]
107 235
108-- content-type: "text/plain"! 236-- content-type: "text/plain"!
109tracker :: TrackerSettings -> Application 237tracker :: Tracker -> Application
110tracker settings @ TrackerSettings {..} Request {..} 238tracker t @ (Tracker TrackerSettings {..} _) Request {..}
111 | requestMethod /= methodGet 239 | requestMethod /= methodGet
112 = return $ responseLBS methodNotAllowed405 [] "" 240 = return $ responseLBS methodNotAllowed405 [] ""
113 241
114 | rawPathInfo == announcePath = do 242 | rawPathInfo == announcePath = do
115 case parseAnnounceRequest $ queryToSimpleQuery queryString of 243 case parseAnnounceRequest $ queryToSimpleQuery queryString of
116 Right query -> announceResponse <$> getAnnounceR settings query 244 Right query -> announceResponse <$> getAnnounceR t query
117 Left msg -> return $ responseLBS (parseFailureStatus msg) [] "" 245 Left msg -> return $ responseLBS (parseFailureStatus msg) [] ""
118 246
119 | rawPathInfo == scrapePath = do 247 | rawPathInfo == scrapePath = do
120 case Right $ parseScrapeQuery $ queryToSimpleQuery queryString of -- TODO 248 case Right $ parseScrapeQuery $ queryToSimpleQuery queryString of -- TODO
121 Right query -> scrapeResponse <$> getScrapeR settings query 249 Right query -> scrapeResponse <$> getScrapeR t query
122 Left msg -> return $ responseLBS badRequest400 [] "" 250 Left msg -> return $ responseLBS badRequest400 [] ""
123 251
124 | otherwise = undefined --badPath 252 | otherwise = undefined --badPath