{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-}
module Keter.RateLimiter.IPZones
(
IPZoneIdentifier
, defaultIPZone
, ZoneSpecificCaches(..)
, createZoneCaches
, newZoneSpecificCaches
, resetSingleZoneCaches
, resetZoneCache
, sockAddrToIPZone
) where
import Data.Text (Text)
import qualified Data.Text as T
import Keter.RateLimiter.Cache
( Cache(..)
, InMemoryStore(..)
, newCache
, createInMemoryStore
, cacheReset
, Algorithm(..)
, startCustomPurgeLeakyBucket
)
import Network.Socket (SockAddr(..))
import Data.IP (fromHostAddress)
import Numeric (showHex)
import Data.Bits
import Control.Concurrent.STM (newTVarIO, atomically, readTVar)
import qualified StmContainers.Map as StmMap
type IPZoneIdentifier = Text
defaultIPZone :: IPZoneIdentifier
defaultIPZone :: IPZoneIdentifier
defaultIPZone = IPZoneIdentifier
"default"
data ZoneSpecificCaches = ZoneSpecificCaches
{ ZoneSpecificCaches -> Cache (InMemoryStore 'FixedWindow)
zscCounterCache :: Cache (InMemoryStore 'FixedWindow)
, ZoneSpecificCaches -> Cache (InMemoryStore 'SlidingWindow)
zscTimestampCache :: Cache (InMemoryStore 'SlidingWindow)
, ZoneSpecificCaches -> Cache (InMemoryStore 'TokenBucket)
zscTokenBucketCache :: Cache (InMemoryStore 'TokenBucket)
, ZoneSpecificCaches -> Cache (InMemoryStore 'LeakyBucket)
zscLeakyBucketCache :: Cache (InMemoryStore 'LeakyBucket)
, ZoneSpecificCaches -> Cache (InMemoryStore 'TinyLRU)
zscTinyLRUCache :: Cache (InMemoryStore 'TinyLRU)
}
createZoneCaches :: IO ZoneSpecificCaches
createZoneCaches :: IO ZoneSpecificCaches
createZoneCaches = do
InMemoryStore 'FixedWindow
counterStore <- forall (a :: Algorithm). CreateStore a => IO (InMemoryStore a)
createInMemoryStore @'FixedWindow
InMemoryStore 'SlidingWindow
slidingStore <- forall (a :: Algorithm). CreateStore a => IO (InMemoryStore a)
createInMemoryStore @'SlidingWindow
InMemoryStore 'TokenBucket
tokenBucketStore <- forall (a :: Algorithm). CreateStore a => IO (InMemoryStore a)
createInMemoryStore @'TokenBucket
TVar (Map IPZoneIdentifier LeakyBucketEntry)
leakyBucketTVar <- Map IPZoneIdentifier LeakyBucketEntry
-> IO (TVar (Map IPZoneIdentifier LeakyBucketEntry))
forall a. a -> IO (TVar a)
newTVarIO (Map IPZoneIdentifier LeakyBucketEntry
-> IO (TVar (Map IPZoneIdentifier LeakyBucketEntry)))
-> IO (Map IPZoneIdentifier LeakyBucketEntry)
-> IO (TVar (Map IPZoneIdentifier LeakyBucketEntry))
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< STM (Map IPZoneIdentifier LeakyBucketEntry)
-> IO (Map IPZoneIdentifier LeakyBucketEntry)
forall a. STM a -> IO a
atomically STM (Map IPZoneIdentifier LeakyBucketEntry)
forall key value. STM (Map key value)
StmMap.new
let leakyBucketStore :: InMemoryStore 'LeakyBucket
leakyBucketStore = TVar (Map IPZoneIdentifier LeakyBucketEntry)
-> InMemoryStore 'LeakyBucket
LeakyBucketStore TVar (Map IPZoneIdentifier LeakyBucketEntry)
leakyBucketTVar
Map IPZoneIdentifier LeakyBucketEntry
leakyBucketMap <- STM (Map IPZoneIdentifier LeakyBucketEntry)
-> IO (Map IPZoneIdentifier LeakyBucketEntry)
forall a. STM a -> IO a
atomically (STM (Map IPZoneIdentifier LeakyBucketEntry)
-> IO (Map IPZoneIdentifier LeakyBucketEntry))
-> STM (Map IPZoneIdentifier LeakyBucketEntry)
-> IO (Map IPZoneIdentifier LeakyBucketEntry)
forall a b. (a -> b) -> a -> b
$ TVar (Map IPZoneIdentifier LeakyBucketEntry)
-> STM (Map IPZoneIdentifier LeakyBucketEntry)
forall a. TVar a -> STM a
readTVar TVar (Map IPZoneIdentifier LeakyBucketEntry)
leakyBucketTVar
ThreadId
_ <- Map IPZoneIdentifier LeakyBucketEntry
-> Integer -> Integer -> IO ThreadId
startCustomPurgeLeakyBucket
Map IPZoneIdentifier LeakyBucketEntry
leakyBucketMap
(Integer
60 :: Integer)
(Integer
7200 :: Integer)
InMemoryStore 'TinyLRU
tinyLRUStore <- forall (a :: Algorithm). CreateStore a => IO (InMemoryStore a)
createInMemoryStore @'TinyLRU
ZoneSpecificCaches -> IO ZoneSpecificCaches
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ZoneSpecificCaches
{ zscCounterCache :: Cache (InMemoryStore 'FixedWindow)
zscCounterCache = Algorithm
-> InMemoryStore 'FixedWindow -> Cache (InMemoryStore 'FixedWindow)
forall store. Algorithm -> store -> Cache store
newCache Algorithm
FixedWindow InMemoryStore 'FixedWindow
counterStore
, zscTimestampCache :: Cache (InMemoryStore 'SlidingWindow)
zscTimestampCache = Algorithm
-> InMemoryStore 'SlidingWindow
-> Cache (InMemoryStore 'SlidingWindow)
forall store. Algorithm -> store -> Cache store
newCache Algorithm
SlidingWindow InMemoryStore 'SlidingWindow
slidingStore
, zscTokenBucketCache :: Cache (InMemoryStore 'TokenBucket)
zscTokenBucketCache = Algorithm
-> InMemoryStore 'TokenBucket -> Cache (InMemoryStore 'TokenBucket)
forall store. Algorithm -> store -> Cache store
newCache Algorithm
TokenBucket InMemoryStore 'TokenBucket
tokenBucketStore
, zscLeakyBucketCache :: Cache (InMemoryStore 'LeakyBucket)
zscLeakyBucketCache = Algorithm
-> InMemoryStore 'LeakyBucket -> Cache (InMemoryStore 'LeakyBucket)
forall store. Algorithm -> store -> Cache store
newCache Algorithm
LeakyBucket InMemoryStore 'LeakyBucket
leakyBucketStore
, zscTinyLRUCache :: Cache (InMemoryStore 'TinyLRU)
zscTinyLRUCache = Algorithm
-> InMemoryStore 'TinyLRU -> Cache (InMemoryStore 'TinyLRU)
forall store. Algorithm -> store -> Cache store
newCache Algorithm
TinyLRU InMemoryStore 'TinyLRU
tinyLRUStore
}
newZoneSpecificCaches :: IO ZoneSpecificCaches
newZoneSpecificCaches :: IO ZoneSpecificCaches
newZoneSpecificCaches = IO ZoneSpecificCaches
createZoneCaches
resetSingleZoneCaches :: ZoneSpecificCaches -> IO ()
resetSingleZoneCaches :: ZoneSpecificCaches -> IO ()
resetSingleZoneCaches ZoneSpecificCaches
zsc = do
Cache (InMemoryStore 'FixedWindow) -> IO ()
forall store. ResettableStore store => Cache store -> IO ()
cacheReset (ZoneSpecificCaches -> Cache (InMemoryStore 'FixedWindow)
zscCounterCache ZoneSpecificCaches
zsc)
Cache (InMemoryStore 'SlidingWindow) -> IO ()
forall store. ResettableStore store => Cache store -> IO ()
cacheReset (ZoneSpecificCaches -> Cache (InMemoryStore 'SlidingWindow)
zscTimestampCache ZoneSpecificCaches
zsc)
Cache (InMemoryStore 'TokenBucket) -> IO ()
forall store. ResettableStore store => Cache store -> IO ()
cacheReset (ZoneSpecificCaches -> Cache (InMemoryStore 'TokenBucket)
zscTokenBucketCache ZoneSpecificCaches
zsc)
Cache (InMemoryStore 'LeakyBucket) -> IO ()
forall store. ResettableStore store => Cache store -> IO ()
cacheReset (ZoneSpecificCaches -> Cache (InMemoryStore 'LeakyBucket)
zscLeakyBucketCache ZoneSpecificCaches
zsc)
Cache (InMemoryStore 'TinyLRU) -> IO ()
forall store. ResettableStore store => Cache store -> IO ()
cacheReset (ZoneSpecificCaches -> Cache (InMemoryStore 'TinyLRU)
zscTinyLRUCache ZoneSpecificCaches
zsc)
resetZoneCache :: ZoneSpecificCaches -> Algorithm -> IO ()
resetZoneCache :: ZoneSpecificCaches -> Algorithm -> IO ()
resetZoneCache ZoneSpecificCaches
zsc Algorithm
algorithm = case Algorithm
algorithm of
Algorithm
FixedWindow -> Cache (InMemoryStore 'FixedWindow) -> IO ()
forall store. ResettableStore store => Cache store -> IO ()
cacheReset (ZoneSpecificCaches -> Cache (InMemoryStore 'FixedWindow)
zscCounterCache ZoneSpecificCaches
zsc)
Algorithm
SlidingWindow -> Cache (InMemoryStore 'SlidingWindow) -> IO ()
forall store. ResettableStore store => Cache store -> IO ()
cacheReset (ZoneSpecificCaches -> Cache (InMemoryStore 'SlidingWindow)
zscTimestampCache ZoneSpecificCaches
zsc)
Algorithm
TokenBucket -> Cache (InMemoryStore 'TokenBucket) -> IO ()
forall store. ResettableStore store => Cache store -> IO ()
cacheReset (ZoneSpecificCaches -> Cache (InMemoryStore 'TokenBucket)
zscTokenBucketCache ZoneSpecificCaches
zsc)
Algorithm
LeakyBucket -> Cache (InMemoryStore 'LeakyBucket) -> IO ()
forall store. ResettableStore store => Cache store -> IO ()
cacheReset (ZoneSpecificCaches -> Cache (InMemoryStore 'LeakyBucket)
zscLeakyBucketCache ZoneSpecificCaches
zsc)
Algorithm
TinyLRU -> Cache (InMemoryStore 'TinyLRU) -> IO ()
forall store. ResettableStore store => Cache store -> IO ()
cacheReset (ZoneSpecificCaches -> Cache (InMemoryStore 'TinyLRU)
zscTinyLRUCache ZoneSpecificCaches
zsc)
sockAddrToIPZone :: SockAddr -> IO Text
sockAddrToIPZone :: SockAddr -> IO IPZoneIdentifier
sockAddrToIPZone (SockAddrInet PortNumber
_ Word32
hostAddr) = do
let ip :: IPv4
ip = Word32 -> IPv4
fromHostAddress Word32
hostAddr
IPZoneIdentifier -> IO IPZoneIdentifier
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (IPZoneIdentifier -> IO IPZoneIdentifier)
-> IPZoneIdentifier -> IO IPZoneIdentifier
forall a b. (a -> b) -> a -> b
$ String -> IPZoneIdentifier
T.pack (String -> IPZoneIdentifier) -> String -> IPZoneIdentifier
forall a b. (a -> b) -> a -> b
$ IPv4 -> String
forall a. Show a => a -> String
show IPv4
ip
sockAddrToIPZone (SockAddrInet6 PortNumber
_ Word32
_ (Word32
w1, Word32
w2, Word32
w3, Word32
w4) Word32
_) =
IPZoneIdentifier -> IO IPZoneIdentifier
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (IPZoneIdentifier -> IO IPZoneIdentifier)
-> IPZoneIdentifier -> IO IPZoneIdentifier
forall a b. (a -> b) -> a -> b
$ IPZoneIdentifier -> [IPZoneIdentifier] -> IPZoneIdentifier
T.intercalate IPZoneIdentifier
":" ([IPZoneIdentifier] -> IPZoneIdentifier)
-> [IPZoneIdentifier] -> IPZoneIdentifier
forall a b. (a -> b) -> a -> b
$ (Word32 -> IPZoneIdentifier) -> [Word32] -> [IPZoneIdentifier]
forall a b. (a -> b) -> [a] -> [b]
map (String -> IPZoneIdentifier
T.pack (String -> IPZoneIdentifier)
-> (Word32 -> String) -> Word32 -> IPZoneIdentifier
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word32 -> String
forall {a}. Integral a => a -> String
showHexWord)
[Word32
w1 Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftR` Int
16, Word32
w1 Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.&. Word32
0xFFFF, Word32
w2 Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftR` Int
16, Word32
w2 Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.&. Word32
0xFFFF,
Word32
w3 Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftR` Int
16, Word32
w3 Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.&. Word32
0xFFFF, Word32
w4 Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftR` Int
16, Word32
w4 Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.&. Word32
0xFFFF]
where
showHexWord :: a -> String
showHexWord a
n = let s :: String
s = a -> ShowS
forall a. Integral a => a -> ShowS
showHex a
n String
"" in if String -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
s Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
4 then Int -> Char -> String
forall a. Int -> a -> [a]
replicate (Int
4 Int -> Int -> Int
forall a. Num a => a -> a -> a
- String -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
s) Char
'0' String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
s else String
s
sockAddrToIPZone SockAddr
_ = IPZoneIdentifier -> IO IPZoneIdentifier
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return IPZoneIdentifier
defaultIPZone