1+ module Network.Transport.QUIC (
2+ createTransport ,
3+ QUICAddr (.. ),
4+ ) where
5+
6+ import Control.Concurrent.STM (atomically )
7+ import Control.Concurrent.STM.TQueue (
8+ TQueue ,
9+ newTQueueIO ,
10+ readTQueue ,
11+ writeTQueue ,
12+ )
13+ import Data.ByteString qualified as BS
14+ import Data.ByteString.Char8 qualified as BS8
15+ import Network.QUIC qualified as QUIC
16+ import Network.QUIC.Server (defaultServerConfig )
17+ import Network.QUIC.Server qualified as QUIC.Server
18+ import Network.Transport (ConnectionId , EndPoint (.. ), EndPointAddress (EndPointAddress ), Event (.. ), NewEndPointErrorCode , Transport (.. ), TransportError (.. ))
19+
20+ import Network.Socket (HostName , ServiceName )
21+
22+ {- | Create a new Transport.
23+
24+ Only a single transport should be created per Haskell process
25+ (threads can, and should, create their own endpoints though).
26+ -}
27+ createTransport :: QUICAddr -> IO Transport
28+ createTransport quicAddr = do
29+ pure $ Transport (newEndpoint quicAddr) closeQUICTransport
30+
31+ data QUICAddr = QUICAddr
32+ { quicBindHost :: ! HostName
33+ , quicBindPort :: ! ServiceName
34+ }
35+
36+ newEndpoint :: QUICAddr -> IO (Either (TransportError NewEndPointErrorCode ) EndPoint )
37+ newEndpoint quicAddr = do
38+ eventQueue <- newTQueueIO
39+
40+ QUIC.Server. run
41+ defaultServerConfig
42+ ( \ conn -> do
43+ -- TODO: create a bidirectional stream
44+ -- which can be re-used for sending
45+ quicStream <- QUIC. acceptStream conn
46+ -- TODO: how to ensure positivity of ConnectionId? QUIC StreamID should be a 62 bit integer,
47+ -- so there's room to make it a positive 64 bit integer (ConnectionId ~ Word64)
48+ let connId = fromIntegral (QUIC. streamId quicStream)
49+ receiveLoop connId quicStream eventQueue
50+ )
51+
52+ pure . Right $
53+ EndPoint
54+ (atomically (readTQueue eventQueue))
55+ (encodeQUICAddr quicAddr)
56+ _
57+ _
58+ _
59+ _
60+ where
61+ receiveLoop ::
62+ ConnectionId ->
63+ QUIC. Stream ->
64+ TQueue Event ->
65+ IO ()
66+ receiveLoop connId stream eventQueue = do
67+ incoming <- QUIC. recvStream stream 1024 -- TODO: variable length?
68+ -- TODO: check some state whether we should stop all connections
69+ if BS. null incoming
70+ then do
71+ atomically (writeTQueue eventQueue (ConnectionClosed connId))
72+ else do
73+ atomically (writeTQueue eventQueue (Received connId [incoming]))
74+ receiveLoop connId stream eventQueue
75+
76+ encodeQUICAddr :: QUICAddr -> EndPointAddress
77+ encodeQUICAddr (QUICAddr host port) = EndPointAddress (BS8. pack $ host <> " :" <> port)
78+
79+ closeQUICTransport :: IO ()
80+ closeQUICTransport = pure ()
0 commit comments