diff options
Diffstat (limited to 'src/Network/BitTorrent/Tracker/Wai.hs')
-rw-r--r-- | src/Network/BitTorrent/Tracker/Wai.hs | 252 |
1 files changed, 0 insertions, 252 deletions
diff --git a/src/Network/BitTorrent/Tracker/Wai.hs b/src/Network/BitTorrent/Tracker/Wai.hs deleted file mode 100644 index df56e378..00000000 --- a/src/Network/BitTorrent/Tracker/Wai.hs +++ /dev/null | |||
@@ -1,252 +0,0 @@ | |||
1 | -- | | ||
2 | -- Copyright : (c) Sam Truzjan 2013 | ||
3 | -- License : BSD3 | ||
4 | -- Maintainer : pxqr.sta@gmail.com | ||
5 | -- Stability : experimental | ||
6 | -- Portability : portable | ||
7 | -- | ||
8 | -- Tracker WAI application. | ||
9 | -- | ||
10 | {-# LANGUAGE RecordWildCards #-} | ||
11 | module Network.BitTorrent.Tracker.Wai | ||
12 | ( -- * Configuration | ||
13 | TrackerSettings (..) | ||
14 | |||
15 | -- * Tracker | ||
16 | , Tracker | ||
17 | , newTracker | ||
18 | , closeTracker | ||
19 | , withTracker | ||
20 | |||
21 | -- * Application | ||
22 | , tracker | ||
23 | ) where | ||
24 | |||
25 | import Control.Applicative | ||
26 | import Control.Concurrent.STM | ||
27 | import Control.Exception | ||
28 | import Control.Monad.Trans | ||
29 | import Control.Monad.Trans.Resource | ||
30 | import Data.BEncode as BE | ||
31 | import Data.Default | ||
32 | import Data.HashMap.Strict as HM | ||
33 | import Data.List as L | ||
34 | import Data.Maybe | ||
35 | import Network.HTTP.Types | ||
36 | import Network.Wai | ||
37 | |||
38 | import Data.Torrent.InfoHash | ||
39 | import Data.Torrent.Progress | ||
40 | import Network.BitTorrent.Tracker.Message | ||
41 | |||
42 | |||
43 | -- | Various configuration settings used to generate tracker response. | ||
44 | data TrackerSettings = TrackerSettings | ||
45 | { announcePath :: !RawPath | ||
46 | , scrapePath :: !RawPath | ||
47 | |||
48 | -- | If peer did not specified the "numwant" then this value is | ||
49 | -- used. | ||
50 | , defNumWant :: {-# UNPACK #-} !Int | ||
51 | |||
52 | -- | If peer specified too big numwant value. | ||
53 | , maxNumWant :: {-# UNPACK #-} !Int | ||
54 | |||
55 | -- | Recommended time interval to wait between regular announce | ||
56 | -- requests. | ||
57 | , reannounceInterval :: {-# UNPACK #-} !Int | ||
58 | |||
59 | -- | Minimum time interval to wait between regular announce | ||
60 | -- requests. | ||
61 | , reannounceMinInterval :: !(Maybe Int) | ||
62 | |||
63 | -- | Whether to send count of seeders. | ||
64 | , completePeers :: !Bool | ||
65 | |||
66 | -- | Whether to send count of leechers. | ||
67 | , incompletePeers :: !Bool | ||
68 | |||
69 | -- | Do not send peer id in response. Peer can override this value | ||
70 | -- by setting "no_peer_id" to 0 or 1. | ||
71 | , noPeerId :: !Bool | ||
72 | |||
73 | -- | Whether to send compact peer list. Peer can override this | ||
74 | -- value by setting "compact" to 0 or 1. | ||
75 | , compactPeerList :: !Bool | ||
76 | } deriving (Show, Read, Eq) | ||
77 | |||
78 | -- | Conservative tracker settings compatible with any client. | ||
79 | instance Default TrackerSettings where | ||
80 | def = TrackerSettings | ||
81 | { announcePath = defaultAnnouncePath | ||
82 | , scrapePath = defaultScrapePath | ||
83 | , defNumWant = defaultNumWant | ||
84 | , maxNumWant = defaultMaxNumWant | ||
85 | , reannounceInterval = defaultReannounceInterval | ||
86 | , reannounceMinInterval = Nothing | ||
87 | , compactPeerList = False | ||
88 | , completePeers = False | ||
89 | , incompletePeers = False | ||
90 | , noPeerId = False | ||
91 | } | ||
92 | |||
93 | {----------------------------------------------------------------------- | ||
94 | -- Swarm | ||
95 | -----------------------------------------------------------------------} | ||
96 | |||
97 | type PeerSet = [()] | ||
98 | |||
99 | data Swarm = Swarm | ||
100 | { leechers :: !PeerSet | ||
101 | , seeders :: !PeerSet | ||
102 | , downloaded :: {-# UNPACK #-} !Int | ||
103 | } | ||
104 | |||
105 | instance Default Swarm where | ||
106 | def = Swarm | ||
107 | { leechers = [] | ||
108 | , seeders = [] | ||
109 | , downloaded = 0 | ||
110 | } | ||
111 | {- | ||
112 | started :: PeerInfo -> Swarm -> Swarm | ||
113 | started info Swarm {..} = Swarm | ||
114 | { leechers = insert info leechers | ||
115 | , seeders = delete info seeders | ||
116 | , downloaded = downloaded | ||
117 | } | ||
118 | |||
119 | regular :: PeerInfo -> Swarm -> Swarm | ||
120 | regular info Swarm {..} = undefined | ||
121 | |||
122 | stopped :: PeerInfo -> Swarm -> Swarm | ||
123 | stopped info Swarm {..} = Swarm | ||
124 | { leechers = delete info leechers | ||
125 | , seeders = delete info seeders | ||
126 | , downloaded = downloaded | ||
127 | } | ||
128 | |||
129 | completed :: PeerInfo -> Swarm -> Swarm | ||
130 | completed info Swarm {..} = Swarm | ||
131 | { leechers = delete info leechers | ||
132 | , seeders = insert info seeders | ||
133 | , downloaded = succ downloaded | ||
134 | } | ||
135 | |||
136 | event :: Maybe Event -> Swarm -> Swarm | ||
137 | event = undefined | ||
138 | -} | ||
139 | --peerList :: TrackerSettings -> Swarm -> PeerList IP | ||
140 | peerList TrackerSettings {..} Swarm {..} = undefined --envelope peers | ||
141 | where | ||
142 | envelope = if compactPeerList then CompactPeerList else PeerList | ||
143 | peers = [] | ||
144 | |||
145 | announceInfo :: TrackerSettings -> Swarm -> AnnounceInfo | ||
146 | announceInfo 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 | |||
155 | scrapeEntry :: Swarm -> ScrapeEntry | ||
156 | scrapeEntry Swarm {..} = ScrapeEntry | ||
157 | { siComplete = L.length seeders | ||
158 | , siDownloaded = downloaded | ||
159 | , siIncomplete = L.length leechers | ||
160 | , siName = Nothing | ||
161 | } | ||
162 | |||
163 | {----------------------------------------------------------------------- | ||
164 | -- Tracker state | ||
165 | -----------------------------------------------------------------------} | ||
166 | |||
167 | type Table = HashMap InfoHash Swarm | ||
168 | |||
169 | withSwarm :: TVar Table -> InfoHash -> (Maybe Swarm -> STM (a, Swarm)) -> STM a | ||
170 | withSwarm 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 | |||
176 | scrapeInfo :: ScrapeQuery -> Table -> [ScrapeEntry] | ||
177 | scrapeInfo query table = do | ||
178 | infohash <- query | ||
179 | swarm <- maybeToList $ HM.lookup infohash table | ||
180 | return $ scrapeEntry swarm | ||
181 | |||
182 | data TrackerState = TrackerState | ||
183 | { swarms :: !(TVar Table) | ||
184 | } | ||
185 | |||
186 | newState :: IO TrackerState | ||
187 | newState = TrackerState <$> newTVarIO HM.empty | ||
188 | |||
189 | data Tracker = Tracker | ||
190 | { options :: !TrackerSettings | ||
191 | , state :: !TrackerState | ||
192 | } | ||
193 | |||
194 | newTracker :: TrackerSettings -> IO Tracker | ||
195 | newTracker opts = Tracker opts <$> newState | ||
196 | |||
197 | closeTracker :: Tracker -> IO () | ||
198 | closeTracker _ = return () | ||
199 | |||
200 | withTracker :: TrackerSettings -> (Tracker -> IO a) -> IO a | ||
201 | withTracker opts = bracket (newTracker opts) closeTracker | ||
202 | |||
203 | {----------------------------------------------------------------------- | ||
204 | -- Handlers | ||
205 | -----------------------------------------------------------------------} | ||
206 | |||
207 | getAnnounceR :: Tracker -> AnnounceRequest -> ResourceT IO AnnounceInfo | ||
208 | getAnnounceR 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 | -} | ||
217 | getScrapeR :: Tracker -> ScrapeQuery -> ResourceT IO ScrapeInfo | ||
218 | getScrapeR Tracker {..} query = do | ||
219 | table <- liftIO $ readTVarIO (swarms state) | ||
220 | return $ undefined $ scrapeInfo query table | ||
221 | |||
222 | {----------------------------------------------------------------------- | ||
223 | -- Routing | ||
224 | -----------------------------------------------------------------------} | ||
225 | |||
226 | announceResponse :: AnnounceInfo -> Response | ||
227 | announceResponse info = responseLBS ok200 headers $ BE.encode info | ||
228 | where | ||
229 | headers = [(hContentType, announceType)] | ||
230 | |||
231 | scrapeResponse :: ScrapeInfo -> Response | ||
232 | scrapeResponse info = responseLBS ok200 headers $ BE.encode info | ||
233 | where | ||
234 | headers = [(hContentType, scrapeType)] | ||
235 | |||
236 | -- content-type: "text/plain"! | ||
237 | tracker :: Tracker -> Application | ||
238 | tracker t @ (Tracker TrackerSettings {..} _) Request {..} | ||
239 | | requestMethod /= methodGet | ||
240 | = return $ responseLBS methodNotAllowed405 [] "" | ||
241 | |||
242 | | rawPathInfo == announcePath = do | ||
243 | case parseAnnounceRequest $ queryToSimpleQuery queryString of | ||
244 | Right query -> announceResponse <$> getAnnounceR t query | ||
245 | Left msg -> return $ responseLBS (parseFailureStatus msg) [] "" | ||
246 | |||
247 | | rawPathInfo == scrapePath = do | ||
248 | case Right $ parseScrapeQuery $ queryToSimpleQuery queryString of -- TODO | ||
249 | Right query -> scrapeResponse <$> getScrapeR t query | ||
250 | Left msg -> return $ responseLBS badRequest400 [] "" | ||
251 | |||
252 | | otherwise = undefined --badPath | ||