11{-# LANGUAGE FlexibleContexts #-}
22{-# LANGUAGE GADTs #-}
33{-# LANGUAGE LambdaCase #-}
4+ {-# LANGUAGE NamedFieldPuns #-}
45{-# LANGUAGE RankNTypes #-}
56{-# LANGUAGE ScopedTypeVariables #-}
67
78-- | A simple server. The server doesn't control resource usage (e.g. limiting
89-- of inbound connections) and thus should only be used in a safe environment.
910--
1011-- The module should be imported qualified.
11- module Ouroboros.Network.Server.Simple where
12+ module Ouroboros.Network.Server.Simple
13+ ( with
14+ , ServerTracer (.. )
15+ ) where
1216
1317import Control.Applicative (Alternative )
1418import Control.Concurrent.JobPool qualified as JobPool
15- import Control.Monad (forever )
1619import Control.Monad.Class.MonadAsync
1720import Control.Monad.Class.MonadFork (MonadFork )
1821import Control.Monad.Class.MonadSTM
1922import Control.Monad.Class.MonadThrow
2023import Control.Monad.Class.MonadTimer.SI (MonadDelay , MonadTimer )
24+ import Control.Tracer (Tracer , traceWith )
2125import Data.ByteString.Lazy qualified as BL
2226import Data.Functor (void )
2327import Data.Typeable (Typeable )
@@ -28,9 +32,15 @@ import Network.Mux qualified as Mx
2832import Ouroboros.Network.ConnectionId
2933import Ouroboros.Network.Mux
3034import Ouroboros.Network.Protocol.Handshake
31- import Ouroboros.Network.Snocket as Snocket
35+ import Ouroboros.Network.Server (isECONNABORTED )
36+ import Ouroboros.Network.Snocket (Snocket )
37+ import Ouroboros.Network.Snocket qualified as Snocket
3238import Ouroboros.Network.Socket
3339
40+ data ServerTracer addr
41+ = AcceptException SomeException
42+ | ConnectionHandlerException (ConnectionId addr ) SomeException
43+ deriving Show
3444
3545with :: forall fd addr vNumber vData m a b .
3646 ( Alternative (STM m ),
@@ -46,51 +56,89 @@ with :: forall fd addr vNumber vData m a b.
4656 Show vNumber
4757 )
4858 => Snocket m fd addr
59+ -- ^ low level snocket API
60+ -> Tracer m (ServerTracer addr )
61+ -- ^ server tracer: must not be `nullTracer` in production
62+ -> Mx. TracersWithBearer (ConnectionId addr ) m
63+ -- ^ mux tracers
4964 -> Mx. MakeBearer m fd
5065 -> (fd -> addr -> m () )
66+ -- ^ socket configuration for both listening and connection sockets
5167 -> addr
68+ -- ^ server address to bind to
5269 -> HandshakeArguments (ConnectionId addr ) vNumber vData m
70+ -- ^ handshake arguments
5371 -> Versions vNumber vData (SomeResponderApplication addr BL. ByteString m b )
72+ -- ^ applications to run on each connection
5473 -> (addr -> Async m Void -> m a )
74+ -- ^ continuation for an internally used `withAsync` function that runs
75+ -- server accept loop
5576 -> m a
56- with sn makeBearer configureSock addr handshakeArgs versions k =
77+ with sn tracer muxTracers makeBearer configureSock addr handshakeArgs versions k =
5778 JobPool. withJobPool $ \ jobPool ->
5879 bracket
5980 (do sd <- Snocket. open sn (Snocket. addrFamily sn addr)
6081 configureSock sd addr
6182 Snocket. bind sn sd addr
6283 Snocket. listen sn sd
63- addr' <- getLocalAddr sn sd
84+ addr' <- Snocket. getLocalAddr sn sd
6485 pure (sd, addr'))
6586 (Snocket. close sn . fst )
66- (\ (sock, addr' ) ->
87+ (\ (sock, localAddress ) ->
6788 -- accept loop
68- withAsync (forever $ acceptOne jobPool sock) (k addr')
89+ withAsync (Snocket. accept sn sock >>= acceptLoop jobPool localAddress)
90+ (k localAddress)
6991 )
7092 where
71- acceptOne :: JobPool. JobPool () m () -> fd -> m ()
72- acceptOne jobPool sock = accept sn sock >>= runAccept >>= \ case
73- (Accepted sock' remoteAddr, _) -> do
74- let connThread = do
75- -- connection responder thread
76- let connId = ConnectionId addr remoteAddr
77- bearer <- Mx. getBearer makeBearer (- 1 ) sock' Nothing
78- configureSock sock' addr
79- r <- runHandshakeServer bearer connId handshakeArgs versions
80- case r of
81- Left (HandshakeProtocolLimit e) -> throwIO e
82- Left (HandshakeProtocolError e) -> throwIO e
83- Right HandshakeQueryResult {} -> error " handshake query is not supported"
84- Right (HandshakeNegotiationResult (SomeResponderApplication app) vNumber vData) -> do
85- mux <- Mx. new Mx. nullTracers (toMiniProtocolInfos (runForkPolicy noBindForkPolicy (remoteAddress connId)) app)
86- withAsync (Mx. run mux bearer) $ \ aid -> do
87- void $ simpleMuxCallback connId vNumber vData app mux aid
93+ acceptLoop :: JobPool. JobPool () m ()
94+ -> addr
95+ -> Snocket. Accept m fd addr
96+ -> m Void
97+ acceptLoop
98+ jobPool
99+ localAddress
100+ Snocket. Accept { Snocket. runAccept }
101+ = do
102+ (accepted, acceptNext) <- runAccept
103+ acceptOne accepted
104+ acceptLoop jobPool
105+ localAddress
106+ acceptNext
107+ where
108+ -- handle accept failures and fork a connection thread which performs
109+ -- a handshake and runs mux
110+ acceptOne :: Snocket. Accepted fd addr -> m ()
111+ acceptOne (Snocket. AcceptFailure e)
112+ | Just ioErr <- fromException e
113+ , isECONNABORTED ioErr
114+ = traceWith tracer (AcceptException e)
115+ acceptOne (Snocket. AcceptFailure e)
116+ = do traceWith tracer (AcceptException e)
117+ throwIO e
88118
89- errorHandler = \ e -> throwIO e
119+ acceptOne (Snocket. Accepted sock' remoteAddress) = do
120+ let connId = ConnectionId { localAddress, remoteAddress }
121+ connThread = do
122+ -- connection responder thread
123+ bearer <- Mx. getBearer makeBearer (- 1 ) sock' Nothing
124+ configureSock sock' localAddress
125+ r <- runHandshakeServer bearer connId handshakeArgs versions
126+ case r of
127+ Left (HandshakeProtocolLimit e) -> throwIO e
128+ Left (HandshakeProtocolError e) -> throwIO e
129+ Right HandshakeQueryResult {} -> error " handshake query is not supported"
130+ Right (HandshakeNegotiationResult (SomeResponderApplication app) vNumber vData) -> do
131+ mux <- Mx. new (connId `Mx.tracersWithBearer` muxTracers)
132+ (toMiniProtocolInfos
133+ (runForkPolicy noBindForkPolicy remoteAddress)
134+ app)
135+ withAsync (Mx. run mux bearer) $ \ aid -> do
136+ void $ simpleMuxCallback connId vNumber vData app mux aid
90137
91- JobPool. forkJob jobPool
92- $ JobPool. Job connThread
93- errorHandler
94- ()
95- " conn-thread"
96- (AcceptFailure e, _) -> throwIO e
138+ errorHandler = \ e -> traceWith tracer (ConnectionHandlerException connId e)
139+ >> throwIO e
140+ JobPool. forkJob jobPool
141+ $ JobPool. Job connThread
142+ errorHandler
143+ ()
144+ " conn-thread"
0 commit comments