mirror of https://github.com/voidlizard/hbs2
tcp rewritten
This commit is contained in:
parent
b2ce060650
commit
6c51498064
|
@ -10,6 +10,6 @@ constraints:
|
||||||
, http-client >=0.7.16 && <0.8
|
, http-client >=0.7.16 && <0.8
|
||||||
|
|
||||||
-- executable-static: True
|
-- executable-static: True
|
||||||
-- profiling: True
|
profiling: True
|
||||||
--library-profiling: False
|
--library-profiling: False
|
||||||
|
|
||||||
|
|
|
@ -21,14 +21,14 @@ import HBS2.Net.Messaging.Stream
|
||||||
import HBS2.System.Logger.Simple
|
import HBS2.System.Logger.Simple
|
||||||
import HBS2.Misc.PrettyStuff
|
import HBS2.Misc.PrettyStuff
|
||||||
|
|
||||||
import Control.Concurrent.STM (flushTQueue)
|
import Control.Concurrent.STM (flushTQueue,retry)
|
||||||
import Control.Monad.Trans.Maybe
|
import Control.Monad.Trans.Maybe
|
||||||
import Data.Bits
|
import Data.Bits
|
||||||
import Data.ByteString.Lazy (ByteString)
|
import Data.ByteString.Lazy (ByteString)
|
||||||
import Data.ByteString.Lazy qualified as LBS
|
import Data.ByteString.Lazy qualified as LBS
|
||||||
import Data.Function
|
import Data.Function
|
||||||
import Data.HashMap.Strict (HashMap)
|
import Data.HashMap.Strict (HashMap)
|
||||||
import Data.HashMap.Strict qualified as HashMap
|
import Data.HashMap.Strict qualified as HM
|
||||||
import Data.List qualified as L
|
import Data.List qualified as L
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.Word
|
import Data.Word
|
||||||
|
@ -50,24 +50,20 @@ import Streaming.Prelude qualified as S
|
||||||
|
|
||||||
-- FIXME: control-recv-capacity-to-avoid-leaks
|
-- FIXME: control-recv-capacity-to-avoid-leaks
|
||||||
|
|
||||||
|
outMessageQLen :: Natural
|
||||||
|
outMessageQLen = 256
|
||||||
|
|
||||||
-- | TCP Messaging environment
|
-- | TCP Messaging environment
|
||||||
data MessagingTCP =
|
data MessagingTCP =
|
||||||
MessagingTCP
|
MessagingTCP
|
||||||
{ _tcpSOCKS5 :: Maybe (PeerAddr L4Proto)
|
{ _tcpSOCKS5 :: Maybe (PeerAddr L4Proto)
|
||||||
, _tcpOwnPeer :: Peer L4Proto
|
, _tcpOwnPeer :: Peer L4Proto
|
||||||
, _tcpCookie :: Word32
|
, _tcpCookie :: Word32
|
||||||
, _tcpConnPeer :: TVar (HashMap Word64 (Peer L4Proto))
|
|
||||||
, _tcpPeerConn :: TVar (HashMap (Peer L4Proto) Word64)
|
, _tcpPeerConn :: TVar (HashMap (Peer L4Proto) Word64)
|
||||||
, _tcpConnUsed :: TVar (HashMap Word64 Int)
|
, _tcpPeerCookie :: TVar (HashMap Word32 Int)
|
||||||
, _tcpConnQ :: TVar (HashMap Word64 (TQueue (Peer L4Proto, ByteString)))
|
, _tcpConnDemand :: TQueue (Peer L4Proto)
|
||||||
, _tcpPeerPx :: TVar (HashMap Word32 (Peer L4Proto))
|
, _tcpReceived :: TBQueue (Peer L4Proto, ByteString)
|
||||||
, _tcpPeerXp :: TVar (HashMap (Peer L4Proto) Word32)
|
, _tcpSent :: TVar (HashMap (Peer L4Proto) (TBQueue ByteString))
|
||||||
, _tcpRecv :: TQueue (Peer L4Proto, ByteString)
|
|
||||||
, _tcpDefer :: TVar (HashMap (Peer L4Proto) [(TimeSpec, ByteString)])
|
|
||||||
, _tcpDeferEv :: TQueue ()
|
|
||||||
, _tcpSpawned :: TVar Int
|
|
||||||
, _tcpFired :: TVar Int
|
|
||||||
, _tcpConnWip :: TVar (HashMap (Peer L4Proto) TimeSpec)
|
|
||||||
, _tcpProbe :: TVar AnyProbe
|
, _tcpProbe :: TVar AnyProbe
|
||||||
, _tcpOnClientStarted :: PeerAddr L4Proto -> Word64 -> IO () -- ^ Cient TCP connection succeed
|
, _tcpOnClientStarted :: PeerAddr L4Proto -> Word64 -> IO () -- ^ Cient TCP connection succeed
|
||||||
}
|
}
|
||||||
|
@ -89,55 +85,39 @@ newMessagingTCP pa = liftIO do
|
||||||
<*> randomIO
|
<*> randomIO
|
||||||
<*> newTVarIO mempty
|
<*> newTVarIO mempty
|
||||||
<*> newTVarIO mempty
|
<*> newTVarIO mempty
|
||||||
<*> newTVarIO mempty
|
|
||||||
<*> newTVarIO mempty
|
|
||||||
<*> newTVarIO mempty
|
|
||||||
<*> newTVarIO mempty
|
|
||||||
<*> newTQueueIO
|
<*> newTQueueIO
|
||||||
<*> newTVarIO mempty
|
<*> newTBQueueIO outMessageQLen
|
||||||
<*> newTQueueIO
|
|
||||||
<*> newTVarIO 0
|
|
||||||
<*> newTVarIO 0
|
|
||||||
<*> newTVarIO mempty
|
<*> newTVarIO mempty
|
||||||
<*> newTVarIO (AnyProbe ())
|
<*> newTVarIO (AnyProbe ())
|
||||||
<*> pure (\_ _ -> none) -- do nothing by default
|
<*> pure (\_ _ -> none) -- do nothing by default
|
||||||
|
|
||||||
instance Messaging MessagingTCP L4Proto ByteString where
|
instance Messaging MessagingTCP L4Proto ByteString where
|
||||||
|
|
||||||
sendTo bus (To p) (From _f) msg = liftIO do
|
sendTo MessagingTCP{..} (To p) (From _f) msg = liftIO do
|
||||||
let _own = view tcpOwnPeer bus
|
-- let _own = tcpOwnPeer
|
||||||
|
-- debug $ "!!!! FUCKING SEND TO" <+> pretty p
|
||||||
|
|
||||||
co' <- atomically $ readTVar (view tcpPeerConn bus) <&> HashMap.lookup p
|
queue <- atomically do
|
||||||
|
q' <- readTVar _tcpSent <&> HM.lookup p
|
||||||
|
|
||||||
-- debug $ "sendTo" <+> brackets (pretty own)
|
case q' of
|
||||||
-- <+> pretty p
|
Nothing -> do
|
||||||
-- <+> braces (pretty co')
|
writeTQueue _tcpConnDemand p
|
||||||
-- <+> pretty (LBS.length msg)
|
q <- newTBQueue outMessageQLen
|
||||||
|
modifyTVar _tcpSent (HM.insert p q)
|
||||||
|
pure q
|
||||||
|
|
||||||
maybe1 co' defer $ \co -> do
|
Just q -> pure q
|
||||||
-- trace $ "writing to" <+> pretty co
|
|
||||||
q' <- atomically $ readTVar (view tcpConnQ bus) <&> HashMap.lookup co
|
|
||||||
maybe1 q' (warn $ "no queue for" <+> pretty co) $ \q -> do
|
|
||||||
atomically $ writeTQueue q (p, msg)
|
|
||||||
|
|
||||||
where
|
atomically $ writeTBQueueDropSTM 10 queue msg
|
||||||
defer = do
|
|
||||||
warn $ "defer" <+> pretty p
|
|
||||||
t <- getTimeCoarse
|
|
||||||
atomically $ modifyTVar (view tcpDefer bus) (HashMap.insertWith (<>) p [(t, msg)])
|
|
||||||
atomically $ writeTQueue (view tcpDeferEv bus) ()
|
|
||||||
|
|
||||||
receive bus _ = liftIO do
|
|
||||||
let q = view tcpRecv bus
|
|
||||||
|
|
||||||
ms <- atomically do
|
|
||||||
r <- readTQueue q
|
|
||||||
rs <- flushTQueue q
|
|
||||||
pure (r:rs)
|
|
||||||
|
|
||||||
forM ms $ \(p, msg) -> pure (From p, msg)
|
|
||||||
|
|
||||||
|
-- debug $ "!!!! FUCKING SEND TO" <+> pretty p <+> "DONE"
|
||||||
|
|
||||||
|
receive MessagingTCP{..} _ = liftIO do
|
||||||
|
atomically do
|
||||||
|
s0 <- readTBQueue _tcpReceived
|
||||||
|
sx <- flushTBQueue _tcpReceived
|
||||||
|
pure $ fmap (over _1 From) ( s0 : sx )
|
||||||
|
|
||||||
connectionId :: Word32 -> Word32 -> Word64
|
connectionId :: Word32 -> Word32 -> Word64
|
||||||
connectionId a b = (fromIntegral hi `shiftL` 32) .|. fromIntegral low
|
connectionId a b = (fromIntegral hi `shiftL` 32) .|. fromIntegral low
|
||||||
|
@ -145,7 +125,6 @@ connectionId a b = (fromIntegral hi `shiftL` 32) .|. fromIntegral low
|
||||||
low = min a b
|
low = min a b
|
||||||
hi = max a b
|
hi = max a b
|
||||||
|
|
||||||
|
|
||||||
data ConnType = Server | Client
|
data ConnType = Server | Client
|
||||||
deriving (Eq,Ord,Show,Generic)
|
deriving (Eq,Ord,Show,Generic)
|
||||||
|
|
||||||
|
@ -183,352 +162,225 @@ handshake Client env so = do
|
||||||
sendCookie env so
|
sendCookie env so
|
||||||
recvCookie env so
|
recvCookie env so
|
||||||
|
|
||||||
spawnConnection :: forall m . MonadIO m
|
writeTBQueueDropSTM :: Integral n
|
||||||
=> ConnType
|
=> n
|
||||||
-> MessagingTCP
|
-> TBQueue a
|
||||||
-> Socket
|
-> a
|
||||||
-> SockAddr
|
-> STM ()
|
||||||
-> m ()
|
writeTBQueueDropSTM inQLen newInQ bs = do
|
||||||
|
flip fix inQLen $ \more j -> do
|
||||||
spawnConnection tp env@MessagingTCP{..} so sa = liftIO do
|
when (j > 0) do
|
||||||
|
full <- isFullTBQueue newInQ
|
||||||
flip runContT pure $ do
|
if not full then do
|
||||||
|
writeTBQueue newInQ bs
|
||||||
let myCookie = view tcpCookie env
|
else do
|
||||||
let own = view tcpOwnPeer env
|
void $ tryReadTBQueue newInQ
|
||||||
let newP = fromSockAddr @'TCP sa
|
more (pred j)
|
||||||
|
|
||||||
theirCookie <- handshake tp env so
|
|
||||||
|
|
||||||
let connId = connectionId myCookie theirCookie
|
|
||||||
|
|
||||||
when (tp == Client && theirCookie /= myCookie) do
|
|
||||||
pa <- toPeerAddr newP
|
|
||||||
liftIO $ view tcpOnClientStarted env pa connId -- notify if we opened client tcp connection
|
|
||||||
|
|
||||||
traceCmd own
|
|
||||||
( "spawnConnection "
|
|
||||||
<+> viaShow tp
|
|
||||||
<+> pretty myCookie
|
|
||||||
<+> pretty connId )
|
|
||||||
newP
|
|
||||||
|
|
||||||
debug $ "handshake" <+> viaShow tp
|
|
||||||
<+> brackets (pretty (view tcpOwnPeer env))
|
|
||||||
<+> pretty sa
|
|
||||||
<+> pretty theirCookie
|
|
||||||
<+> pretty connId
|
|
||||||
|
|
||||||
used <- atomically $ do
|
|
||||||
modifyTVar (view tcpConnUsed env) (HashMap.insertWith (+) connId 1)
|
|
||||||
readTVar (view tcpConnUsed env) <&> HashMap.findWithDefault 0 connId
|
|
||||||
|
|
||||||
|
|
||||||
void $ ContT $ bracket (pure connId) cleanupConn
|
killCookie :: Int -> Maybe Int
|
||||||
|
killCookie = \case
|
||||||
debug $ "USED:" <+> viaShow tp <+> pretty own <+> pretty used
|
1 -> Nothing
|
||||||
|
n -> Just (pred n)
|
||||||
-- when ( used <= 2 ) do
|
|
||||||
atomically $ modifyTVar (view tcpPeerConn env) (HashMap.insert newP connId)
|
|
||||||
|
|
||||||
when (used == 1) do
|
|
||||||
|
|
||||||
atomically $ modifyTVar _tcpSpawned succ
|
|
||||||
|
|
||||||
q <- getWriteQueue connId
|
|
||||||
updatePeer connId newP
|
|
||||||
|
|
||||||
debug $ "NEW PEER" <+> brackets (pretty own)
|
|
||||||
<+> pretty connId
|
|
||||||
<+> pretty newP
|
|
||||||
<+> parens ("used:" <+> pretty used)
|
|
||||||
|
|
||||||
rd <- ContT $ withAsync $ fix \next -> do
|
|
||||||
|
|
||||||
spx <- readFromSocket so 4 <&> LBS.toStrict
|
|
||||||
ssize <- readFromSocket so 4 <&> LBS.toStrict --- УУУ, фреейминг
|
|
||||||
let px = word32 spx -- & fromIntegral
|
|
||||||
let size = word32 ssize & fromIntegral
|
|
||||||
|
|
||||||
|
|
||||||
bs <- readFromSocket so size
|
|
||||||
|
|
||||||
memReqId newP px
|
|
||||||
|
|
||||||
pxes <- readTVarIO (view tcpPeerPx env)
|
|
||||||
|
|
||||||
let orig = fromMaybe (fromSockAddr @'TCP sa) (HashMap.lookup px pxes)
|
|
||||||
|
|
||||||
-- debug $ "RECEIVED" <+> pretty orig <+> pretty (LBS.length bs)
|
|
||||||
|
|
||||||
atomically $ writeTQueue (view tcpRecv env) (orig, bs)
|
|
||||||
|
|
||||||
next
|
|
||||||
|
|
||||||
wr <- ContT $ withAsync $ fix \next -> do
|
|
||||||
(rcpt, bs) <- atomically $ readTQueue q
|
|
||||||
|
|
||||||
pq <- makeReqId rcpt
|
|
||||||
let qids = bytestring32 pq
|
|
||||||
let size = bytestring32 (fromIntegral $ LBS.length bs)
|
|
||||||
|
|
||||||
let frame = LBS.fromStrict qids
|
|
||||||
<> LBS.fromStrict size -- req-size
|
|
||||||
<> bs -- payload
|
|
||||||
|
|
||||||
sendLazy so frame --(LBS.toStrict frame)
|
|
||||||
next
|
|
||||||
|
|
||||||
ContT $ bracket none $ \_ -> mapM cancel [rd,wr]
|
|
||||||
ContT $ bracket (pure connId) cleanupConn
|
|
||||||
ContT $ bracket none (const $ atomically $ modifyTVar _tcpSpawned pred)
|
|
||||||
|
|
||||||
void $ waitAnyCatchCancel [rd,wr]
|
|
||||||
|
|
||||||
-- gracefulClose so 1000
|
|
||||||
debug $ "spawnConnection exit" <+> pretty sa
|
|
||||||
|
|
||||||
where
|
|
||||||
|
|
||||||
memReqId newP px =
|
|
||||||
atomically $ modifyTVar (view tcpPeerXp env) (HashMap.insert newP px)
|
|
||||||
|
|
||||||
makeReqId rcpt = do
|
|
||||||
let pxes = view tcpPeerPx env
|
|
||||||
let xpes = view tcpPeerXp env
|
|
||||||
|
|
||||||
nq <- randomIO
|
|
||||||
atomically $ do
|
|
||||||
px <- readTVar xpes <&> HashMap.lookup rcpt
|
|
||||||
case px of
|
|
||||||
Just qq -> pure qq
|
|
||||||
Nothing -> do
|
|
||||||
modifyTVar pxes (HashMap.insert nq rcpt)
|
|
||||||
modifyTVar xpes (HashMap.insert rcpt nq)
|
|
||||||
pure nq
|
|
||||||
|
|
||||||
updatePeer connId newP = atomically $ do
|
|
||||||
modifyTVar (view tcpPeerConn env) (HashMap.insert newP connId)
|
|
||||||
modifyTVar (view tcpConnPeer env) (HashMap.insert connId newP)
|
|
||||||
|
|
||||||
getWriteQueue connId = atomically $ do
|
|
||||||
readTVar (view tcpConnQ env) >>= \x -> do
|
|
||||||
case HashMap.lookup connId x of
|
|
||||||
Just qq -> pure qq
|
|
||||||
Nothing -> do
|
|
||||||
newQ <- newTQueue
|
|
||||||
modifyTVar (view tcpConnQ env) (HashMap.insert connId newQ)
|
|
||||||
pure newQ
|
|
||||||
|
|
||||||
cleanupConn connId = atomically do
|
|
||||||
modifyTVar (view tcpConnUsed env) (HashMap.alter del connId)
|
|
||||||
used <- readTVar (view tcpConnUsed env) <&> HashMap.findWithDefault 0 connId
|
|
||||||
when (used == 0) do
|
|
||||||
p <- stateTVar (view tcpConnPeer env)
|
|
||||||
$ \x -> (HashMap.lookup connId x, HashMap.delete connId x)
|
|
||||||
|
|
||||||
maybe1 p none $ \pp ->
|
|
||||||
modifyTVar (view tcpPeerConn env) (HashMap.delete pp)
|
|
||||||
|
|
||||||
modifyTVar (view tcpConnQ env) (HashMap.delete connId)
|
|
||||||
|
|
||||||
where
|
|
||||||
del = \case
|
|
||||||
Nothing -> Nothing
|
|
||||||
Just n | n <= 1 -> Nothing
|
|
||||||
| otherwise -> Just (pred n)
|
|
||||||
|
|
||||||
|
|
||||||
connectPeerTCP :: MonadIO m
|
|
||||||
=> MessagingTCP
|
|
||||||
-> Peer L4Proto
|
|
||||||
-> m ()
|
|
||||||
|
|
||||||
connectPeerTCP env peer = liftIO do
|
|
||||||
pa <- toPeerAddr peer
|
|
||||||
let (L4Address _ (IPAddrPort (i,p))) = pa
|
|
||||||
|
|
||||||
|
|
||||||
here <- readTVarIO (view tcpPeerConn env) <&> HashMap.member peer
|
|
||||||
|
|
||||||
unless here do
|
|
||||||
|
|
||||||
case view tcpSOCKS5 env of
|
|
||||||
Nothing -> do
|
|
||||||
|
|
||||||
connect (show i) (show p) $ \(sock, remoteAddr) -> do
|
|
||||||
spawnConnection Client env sock remoteAddr
|
|
||||||
shutdown sock ShutdownBoth
|
|
||||||
|
|
||||||
Just socks5 -> do
|
|
||||||
|
|
||||||
let (L4Address _ (IPAddrPort (socks,socksp))) = socks5
|
|
||||||
|
|
||||||
connectSOCKS5 (show socks) (show socksp) (show i) (show p) $ \(sock, socksAddr, _) -> do
|
|
||||||
|
|
||||||
let (PeerL4{..}) = peer
|
|
||||||
|
|
||||||
debug $ "CONNECTED VIA SOCKS5" <+> pretty socksAddr <+> pretty pa
|
|
||||||
|
|
||||||
spawnConnection Client env sock _sockAddr
|
|
||||||
|
|
||||||
shutdown sock ShutdownBoth
|
|
||||||
|
|
||||||
-- FIXME: link-all-asyncs
|
|
||||||
|
|
||||||
fireTCP :: forall e m . (e ~ L4Proto, Pretty (PeerAddr e), IsPeerAddr e m, MonadUnliftIO m) => MessagingTCP -> Peer e -> m () -> m ()
|
|
||||||
fireTCP MessagingTCP{..} pip what = do
|
|
||||||
void $ do
|
|
||||||
pa <- toPeerAddr @e pip
|
|
||||||
now <- getTimeCoarse
|
|
||||||
fire <- atomically do
|
|
||||||
here <- readTVar _tcpConnWip <&> HashMap.member pip
|
|
||||||
unless here do
|
|
||||||
modifyTVar _tcpConnWip (HashMap.insert pip now)
|
|
||||||
pure (not here)
|
|
||||||
when fire do
|
|
||||||
debug $ "Fire TCP" <+> pretty pa
|
|
||||||
atomically (modifyTVar _tcpFired succ)
|
|
||||||
void $ async do
|
|
||||||
r <- U.try @_ @SomeException what
|
|
||||||
atomically (modifyTVar _tcpFired pred)
|
|
||||||
either U.throwIO dontHandle r
|
|
||||||
|
|
||||||
runMessagingTCP :: forall m . MonadIO m => MessagingTCP -> m ()
|
runMessagingTCP :: forall m . MonadIO m => MessagingTCP -> m ()
|
||||||
runMessagingTCP env@MessagingTCP{..} = liftIO do
|
runMessagingTCP env@MessagingTCP{..} = liftIO do
|
||||||
|
|
||||||
void $ flip runContT pure do
|
void $ flip runContT pure do
|
||||||
|
|
||||||
own <- toPeerAddr $ view tcpOwnPeer env
|
p1 <- ContT $ withAsync runClient
|
||||||
let (L4Address _ (IPAddrPort (i,p))) = own
|
p2 <- ContT $ withAsync runServer
|
||||||
|
|
||||||
let defs = view tcpDefer env
|
waitAnyCatchCancel [p1,p2]
|
||||||
|
-- waitAnyCatchCancel [p2]
|
||||||
|
-- waitAnyCatchCancel [p1]
|
||||||
|
|
||||||
mon <- ContT $ withAsync $ forever do
|
where
|
||||||
pause @'Seconds 30
|
|
||||||
now <- getTimeCoarse
|
|
||||||
|
|
||||||
-- FIXME: time-hardcode-again
|
runServer :: forall m . MonadIO m => m ()
|
||||||
let expire = filter (\e -> (realToFrac (toNanoSecs (now - fst e)) / (1e9 :: Double)) < 30)
|
runServer = do
|
||||||
atomically $ modifyTVar defs
|
|
||||||
$ HashMap.mapMaybe
|
|
||||||
$ \es -> let rs = expire es
|
|
||||||
in case rs of
|
|
||||||
[] -> Nothing
|
|
||||||
xs -> Just xs
|
|
||||||
|
|
||||||
con <- ContT $ withAsync $ forever do
|
own <- toPeerAddr $ view tcpOwnPeer env
|
||||||
|
let (L4Address _ (IPAddrPort (i,p))) = own
|
||||||
|
let myCookie = view tcpCookie env
|
||||||
|
|
||||||
let ev = view tcpDeferEv env
|
-- server
|
||||||
|
liftIO $ listen (Host (show i)) (show p) $ \(sock, sa) -> do
|
||||||
|
withFdSocket sock setCloseOnExecIfNeeded
|
||||||
|
debug $ "Listening on" <+> pretty sa
|
||||||
|
|
||||||
-- FIXME: wait-period-hardcode
|
forever do
|
||||||
void $ race (pause @'Seconds 0.25) (atomically $ readTQueue ev >> flushTQueue ev)
|
void $ acceptFork sock $ \(so, remote) -> void $ flip runContT pure $ callCC \exit -> do
|
||||||
|
liftIO $ withFdSocket so setCloseOnExecIfNeeded
|
||||||
|
debug $ "!!! GOT INCOMING CONNECTION FROM !!!"
|
||||||
|
<+> brackets (pretty own)
|
||||||
|
<+> brackets (pretty sa)
|
||||||
|
|
||||||
dePips <- readTVarIO defs <&> HashMap.keys
|
cookie <- handshake Server env so
|
||||||
|
|
||||||
|
when (cookie == myCookie) $ exit ()
|
||||||
|
|
||||||
forM_ dePips $ \pip -> void $ runMaybeT do
|
here <- atomically do
|
||||||
|
n <- readTVar _tcpPeerCookie <&> HM.member cookie
|
||||||
|
|
||||||
-- FIXME: make-sure-it-is-correct
|
unless n do
|
||||||
already <- readTVarIO _tcpPeerXp <&> HashMap.member pip
|
modifyTVar _tcpPeerCookie (HM.insertWith (+) cookie 1)
|
||||||
|
|
||||||
guard (not already)
|
pure n
|
||||||
|
|
||||||
msgs <- readTVarIO defs <&> HashMap.findWithDefault mempty pip
|
when here $ do
|
||||||
|
debug $ "SERVER : ALREADY CONNECTED" <+> pretty cookie <+> viaShow so
|
||||||
|
exit ()
|
||||||
|
|
||||||
unless (L.null msgs) do
|
let newP = fromSockAddr @'TCP remote :: Peer L4Proto
|
||||||
trace $ "DEFERRED FOR" <+> pretty pip <+> pretty (length msgs)
|
|
||||||
|
|
||||||
let len = length msgs
|
-- FIXME: queue-size-hardcode
|
||||||
|
let inQLen = outMessageQLen
|
||||||
|
|
||||||
when (len > 10) do
|
newInQ <- liftIO $ newTBQueueIO inQLen
|
||||||
-- FIXME: deferred-message-hardcoded
|
|
||||||
atomically $ modifyTVar defs (HashMap.adjust (L.drop (len - 10)) pip)
|
|
||||||
|
|
||||||
co' <- atomically $ readTVar (view tcpPeerConn env) <&> HashMap.lookup pip
|
newOutQ <- do
|
||||||
|
atomically do
|
||||||
|
mbQ <- readTVar _tcpSent <&> HM.lookup newP
|
||||||
|
maybe (newTBQueue outMessageQLen) pure mbQ
|
||||||
|
|
||||||
when (isNothing co') do
|
|
||||||
trace $ red "No session for" <+> pretty pip
|
|
||||||
|
|
||||||
lift $ maybe1 co' (void $ fireTCP env pip (connectPeerTCP env pip)) $ \co -> do
|
|
||||||
q' <- atomically $ readTVar (view tcpConnQ env) <&> HashMap.lookup co
|
|
||||||
maybe1 q' none $ \q -> do
|
|
||||||
atomically do
|
atomically do
|
||||||
mss <- readTVar defs <&> HashMap.findWithDefault mempty pip
|
modifyTVar _tcpSent (HM.insert newP newOutQ)
|
||||||
modifyTVar defs $ HashMap.delete pip
|
modifyTVar _tcpPeerConn (HM.insert newP (connectionId myCookie cookie))
|
||||||
forM_ mss $ \m -> writeTQueue q (pip, snd m)
|
|
||||||
|
|
||||||
stat <- ContT $ withAsync $ forever do
|
wr <- ContT $ withAsync $ forever do
|
||||||
pause @'Seconds 120
|
bs <- atomically $ readTBQueue newOutQ
|
||||||
ps <- readTVarIO $ view tcpConnPeer env
|
|
||||||
let peers = HashMap.toList ps
|
|
||||||
forM_ peers $ \(c,pip) -> do
|
|
||||||
used <- readTVarIO (view tcpConnUsed env) <&> HashMap.findWithDefault 0 c
|
|
||||||
trace $ "peer" <+> brackets (pretty own)
|
|
||||||
<+> pretty pip
|
|
||||||
<+> pretty c
|
|
||||||
<+> parens ("used:" <+> pretty used)
|
|
||||||
|
|
||||||
cleanup <- ContT $ withAsync $ forever do
|
-- FIXME: check-this!
|
||||||
pause @Seconds 60
|
let pq = myCookie -- randomIO
|
||||||
now <- getTimeCoarse
|
let qids = bytestring32 pq
|
||||||
connWip <- readTVarIO _tcpConnWip <&> HashMap.toList
|
let size = bytestring32 (fromIntegral $ LBS.length bs)
|
||||||
let connAlive = [ (k,v) | (k,v) <- connWip, not (expired (TimeoutSec 60) (now - v)) ]
|
|
||||||
atomically $ writeTVar _tcpConnWip (HashMap.fromList connAlive)
|
|
||||||
|
|
||||||
mapM_ link [mon,con,stat,cleanup]
|
let frame = LBS.fromStrict qids
|
||||||
|
<> LBS.fromStrict size -- req-size
|
||||||
|
<> bs -- payload
|
||||||
|
|
||||||
probes <- ContT $ withAsync $ forever do
|
sendLazy so frame --(LBS.toStrict frame)
|
||||||
pause @'Seconds 10
|
|
||||||
probe <- readTVarIO _tcpProbe
|
|
||||||
acceptReport probe =<< S.toList_ do
|
|
||||||
S.yield =<< atomically (readTVar _tcpConnPeer <&> ("tcpConnPeer",) . fromIntegral . HashMap.size)
|
|
||||||
S.yield =<< atomically (readTVar _tcpPeerConn <&> ("tcpPeerConn",) . fromIntegral . HashMap.size)
|
|
||||||
S.yield =<< atomically (readTVar _tcpConnUsed <&> ("tcpConnUsed",) . fromIntegral . HashMap.size)
|
|
||||||
S.yield =<< atomically (readTVar _tcpConnQ <&> ("tcpConnQ",) . fromIntegral . HashMap.size)
|
|
||||||
S.yield =<< atomically (readTVar _tcpPeerPx <&> ("tcpPeerPx",) . fromIntegral . HashMap.size)
|
|
||||||
S.yield =<< atomically (readTVar _tcpPeerXp <&> ("tcpPeerXp",) . fromIntegral . HashMap.size)
|
|
||||||
S.yield =<< atomically (readTVar _tcpDefer <&> ("tcpPeerDefer",) . fromIntegral . HashMap.size)
|
|
||||||
S.yield =<< atomically (readTVar _tcpSpawned <&> ("tcpSpawned",) . fromIntegral)
|
|
||||||
S.yield =<< atomically (readTVar _tcpFired <&> ("tcpFired",) . fromIntegral)
|
|
||||||
S.yield =<< atomically (readTVar _tcpConnWip <&> ("tcpConnWip",) . fromIntegral . HashMap.size)
|
|
||||||
|
|
||||||
ContT $ bracket (pure ()) $ \_ -> mapM_ cancel [mon,con,stat,probes,cleanup]
|
rd <- ContT $ withAsync $ forever do
|
||||||
|
|
||||||
liftIO $ listen (Host (show i)) (show p) $ \(sock, sa) -> do
|
spx <- readFromSocket so 4 <&> LBS.toStrict
|
||||||
withFdSocket sock setCloseOnExecIfNeeded
|
ssize <- readFromSocket so 4 <&> LBS.toStrict --- УУУ, фреейминг
|
||||||
debug $ "Listening on" <+> pretty sa
|
let px = word32 spx -- & fromIntegral
|
||||||
|
let size = word32 ssize & fromIntegral
|
||||||
|
|
||||||
forever do
|
bs <- readFromSocket so size
|
||||||
void $ acceptFork sock $ \(so, remote) -> do
|
|
||||||
withFdSocket so setCloseOnExecIfNeeded
|
|
||||||
trace $ "GOT INCOMING CONNECTION FROM"
|
|
||||||
<+> brackets (pretty own)
|
|
||||||
<+> brackets (pretty sa)
|
|
||||||
<+> pretty remote
|
|
||||||
|
|
||||||
void $ try @SomeException $ do
|
-- debug $ "READ SHIT FROM SOCKET!" <+> pretty remote
|
||||||
|
|
||||||
spawnConnection Server env so remote
|
atomically $ writeTBQueueDropSTM outMessageQLen _tcpReceived (newP, bs)
|
||||||
|
|
||||||
-- gracefulClose so 1000
|
void $ ContT $ bracket none $ const do
|
||||||
|
debug $ "SHUTDOWN SOCKET AND SHIT" <+> pretty remote
|
||||||
|
cancel rd
|
||||||
|
cancel wr
|
||||||
|
shutdown so ShutdownBoth
|
||||||
|
|
||||||
-- TODO: probably-cleanup-peer
|
atomically do
|
||||||
-- TODO: periodically-drop-inactive-connections
|
modifyTVar _tcpSent (HM.delete newP)
|
||||||
|
modifyTVar _tcpPeerCookie $ \m -> do
|
||||||
|
HM.update killCookie cookie m
|
||||||
|
|
||||||
debug $ "CLOSING CONNECTION" <+> pretty remote
|
void $ waitAnyCatchCancel [rd,wr]
|
||||||
shutdown so ShutdownBoth
|
|
||||||
close so -- ) -- `U.finally` mapM_ cancel [mon,con,stat]
|
|
||||||
|
|
||||||
|
|
||||||
traceCmd :: forall a ann b m . ( Pretty a
|
runClient :: forall m . MonadIO m => m ()
|
||||||
, Pretty b
|
runClient = do
|
||||||
, MonadIO m
|
|
||||||
)
|
own <- toPeerAddr $ view tcpOwnPeer env
|
||||||
=> a -> Doc ann -> b -> m ()
|
let (L4Address _ (IPAddrPort (i,p))) = own
|
||||||
|
let myCookie = view tcpCookie env
|
||||||
|
|
||||||
|
pause @'Seconds 30
|
||||||
|
|
||||||
|
forever $ void $ runMaybeT do
|
||||||
|
-- client sockets
|
||||||
|
|
||||||
|
-- смотрим к кому надо
|
||||||
|
who <- atomically $ readTQueue _tcpConnDemand
|
||||||
|
whoAddr <- toPeerAddr who
|
||||||
|
|
||||||
|
already <- atomically $ readTVar _tcpPeerConn <&> HM.member who
|
||||||
|
|
||||||
|
when already do
|
||||||
|
debug "SHIT? BUSYLOOP?"
|
||||||
|
mzero
|
||||||
|
|
||||||
|
-- FIXME: !!!
|
||||||
|
liftIO $ asyncLinked do
|
||||||
|
let (L4Address _ (IPAddrPort (ip,port))) = whoAddr
|
||||||
|
connect (show ip) (show port) $ \(so, remoteAddr) -> do
|
||||||
|
flip runContT pure $ callCC \exit -> do
|
||||||
|
|
||||||
|
debug $ "OPEN CLIENT CONNECTION" <+> pretty ip <+> pretty port <+> pretty remoteAddr
|
||||||
|
cookie <- handshake Client env so
|
||||||
|
let connId = connectionId cookie myCookie
|
||||||
|
|
||||||
|
when (cookie == myCookie) $ exit ()
|
||||||
|
|
||||||
|
here <- atomically do
|
||||||
|
n <- readTVar _tcpPeerCookie <&> HM.member cookie
|
||||||
|
|
||||||
|
unless n do
|
||||||
|
modifyTVar _tcpPeerCookie (HM.insertWith (+) cookie 1)
|
||||||
|
|
||||||
|
modifyTVar _tcpPeerConn (HM.insert who connId)
|
||||||
|
|
||||||
|
pure n
|
||||||
|
|
||||||
|
-- TODO: handshake notification
|
||||||
|
liftIO $ _tcpOnClientStarted whoAddr connId
|
||||||
|
|
||||||
|
when here do
|
||||||
|
debug $ "CLIENT: ALREADY CONNECTED" <+> pretty cookie <+> pretty ip <+> pretty port
|
||||||
|
exit ()
|
||||||
|
|
||||||
|
atomically $ modifyTVar _tcpPeerCookie (HM.insertWith (+) cookie 1)
|
||||||
|
|
||||||
|
wr <- ContT $ withAsync $ forever do
|
||||||
|
bss <- atomically do
|
||||||
|
q' <- readTVar _tcpSent <&> HM.lookup who
|
||||||
|
maybe1 q' mempty $ \q -> do
|
||||||
|
s <- readTBQueue q
|
||||||
|
sx <- flushTBQueue q
|
||||||
|
pure (s:sx)
|
||||||
|
|
||||||
|
for_ bss $ \bs -> do
|
||||||
|
-- FIXME: check-this!
|
||||||
|
let pq = myCookie -- randomIO
|
||||||
|
let qids = bytestring32 pq
|
||||||
|
let size = bytestring32 (fromIntegral $ LBS.length bs)
|
||||||
|
|
||||||
|
let frame = LBS.fromStrict qids
|
||||||
|
<> LBS.fromStrict size -- req-size
|
||||||
|
<> bs -- payload
|
||||||
|
|
||||||
|
sendLazy so frame --(LBS.toStrict frame)
|
||||||
|
|
||||||
|
void $ ContT $ bracket none (const $ cancel wr)
|
||||||
|
|
||||||
|
void $ ContT $ bracket none $ const $ do
|
||||||
|
atomically do
|
||||||
|
modifyTVar _tcpPeerConn (HM.delete who)
|
||||||
|
modifyTVar _tcpPeerCookie $ \m -> do
|
||||||
|
HM.update killCookie cookie m
|
||||||
|
|
||||||
|
forever do
|
||||||
|
|
||||||
|
spx <- readFromSocket so 4 <&> LBS.toStrict
|
||||||
|
ssize <- readFromSocket so 4 <&> LBS.toStrict --- УУУ, фреейминг
|
||||||
|
let px = word32 spx -- & fromIntegral
|
||||||
|
let size = word32 ssize & fromIntegral
|
||||||
|
|
||||||
|
bs <- readFromSocket so size
|
||||||
|
|
||||||
|
-- debug $ "READ SHIT FROM CLIENT SOCKET!" <+> pretty remoteAddr
|
||||||
|
|
||||||
|
atomically $ writeTBQueueDropSTM 10 _tcpReceived (who, bs)
|
||||||
|
|
||||||
traceCmd p1 s p2 = do
|
|
||||||
trace $ brackets (pretty p1)
|
|
||||||
<+> s
|
|
||||||
<+> parens (pretty p2)
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue