Skip to content

Commit cc4f6ec

Browse files
committed
Network transport based on QUIC
1 parent 2edcdf0 commit cc4f6ec

File tree

18 files changed

+2056
-0
lines changed

18 files changed

+2056
-0
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Copyright (c) Laurent P. René de Cotret
2+
3+
Permission is hereby granted, free of charge, to any person obtaining
4+
a copy of this software and associated documentation files (the
5+
"Software"), to deal in the Software without restriction, including
6+
without limitation the rights to use, copy, modify, merge, publish,
7+
distribute, sublicense, and/or sell copies of the Software, and to
8+
permit persons to whom the Software is furnished to do so, subject to
9+
the following conditions:
10+
11+
The above copyright notice and this permission notice shall be included
12+
in all copies or substantial portions of the Software.
13+
14+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
{-# LANGUAGE LambdaCase #-}
2+
{-# LANGUAGE NamedFieldPuns #-}
3+
{-# LANGUAGE NumericUnderscores #-}
4+
{-# LANGUAGE RecordWildCards #-}
5+
{-# LANGUAGE ScopedTypeVariables #-}
6+
7+
module Main where
8+
9+
import Control.Concurrent (forkIO)
10+
import Control.Concurrent.Async (forConcurrently_)
11+
import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar)
12+
import Control.Exception (finally, throwIO)
13+
import Control.Monad (forM_, replicateM, void, when)
14+
import qualified Data.ByteString as BS
15+
import Data.IORef (
16+
atomicModifyIORef',
17+
newIORef,
18+
)
19+
import Data.List.NonEmpty (NonEmpty (..))
20+
import Network.Transport (
21+
Connection (send),
22+
EndPoint (address, connect, receive),
23+
Event (ConnectionOpened, Received),
24+
Reliability (ReliableOrdered),
25+
Transport (closeTransport, newEndPoint),
26+
defaultConnectHints,
27+
)
28+
import qualified Network.Transport.QUIC as QUIC
29+
import qualified Network.Transport.TCP as TCP
30+
import System.FilePath ((</>))
31+
import Test.Tasty (TestTree)
32+
import Test.Tasty.Bench (bench, bgroup, defaultMain, nfIO)
33+
34+
data TransportConfig = TransportConfig
35+
{ transportName :: String
36+
, mkTransport :: IO Transport
37+
}
38+
39+
tcpConfig :: TransportConfig
40+
tcpConfig =
41+
TransportConfig
42+
{ transportName = "TCP"
43+
, mkTransport = do
44+
Right t <- TCP.createTransport (TCP.defaultTCPAddr "127.0.0.1" "0") TCP.defaultTCPParameters
45+
pure t
46+
}
47+
48+
quicConfig :: TransportConfig
49+
quicConfig =
50+
TransportConfig
51+
{ transportName = "QUIC"
52+
, mkTransport =
53+
QUIC.credentialLoadX509
54+
-- Generate a self-signed x509v3 certificate using this nifty tool:
55+
-- https://certificatetools.com/
56+
("test" </> "credentials" </> "cert.crt")
57+
("test" </> "credentials" </> "cert.key")
58+
>>= \case
59+
Left errmsg -> throwIO $ userError errmsg
60+
Right credentials ->
61+
QUIC.createTransport "127.0.0.1" "0" (credentials :| [])
62+
}
63+
64+
data BenchParams = BenchParams
65+
{ messageSize :: !Int
66+
, messageCount :: !Int
67+
, connectionCount :: !Int
68+
}
69+
70+
smallMessages, mediumMessages, largeMessages :: BenchParams
71+
smallMessages = BenchParams{messageSize = 64, messageCount = 10_000, connectionCount = 1}
72+
mediumMessages = BenchParams{messageSize = 1024, messageCount = 1_000, connectionCount = 1}
73+
largeMessages = BenchParams{messageSize = 4096, messageCount = 100, connectionCount = 1}
74+
75+
multiConn :: Int -> BenchParams -> BenchParams
76+
multiConn n p = p{connectionCount = n}
77+
78+
throughputBench :: TransportConfig -> BenchParams -> IO ()
79+
throughputBench TransportConfig{mkTransport} BenchParams{messageSize, messageCount, connectionCount} = do
80+
transport <- mkTransport
81+
flip finally (closeTransport transport) $ do
82+
Right senderEP <- newEndPoint transport
83+
Right receiverEP <- newEndPoint transport
84+
85+
let payload = BS.replicate messageSize 0x42
86+
totalMessages = messageCount * connectionCount
87+
88+
receiverReady <- newEmptyMVar
89+
receiverDone <- newEmptyMVar
90+
91+
void $ forkIO $ do
92+
connsEstablished <- newIORef (0 :: Int)
93+
let waitForConnections = do
94+
event <- receive receiverEP
95+
case event of
96+
ConnectionOpened{} -> do
97+
n <- atomicModifyIORef' connsEstablished (\x -> (x + 1, x + 1))
98+
when (n < connectionCount) waitForConnections
99+
_ -> waitForConnections
100+
waitForConnections
101+
putMVar receiverReady ()
102+
103+
msgsReceived <- newIORef (0 :: Int)
104+
let recvLoop = do
105+
event <- receive receiverEP
106+
case event of
107+
Received _ _ -> do
108+
n <- atomicModifyIORef' msgsReceived (\x -> (x + 1, x + 1))
109+
when (n < totalMessages) recvLoop
110+
_ -> recvLoop
111+
recvLoop
112+
putMVar receiverDone ()
113+
114+
let receiverAddr = address receiverEP
115+
connections <-
116+
replicateM
117+
connectionCount
118+
( connect senderEP receiverAddr ReliableOrdered defaultConnectHints >>= either throwIO pure
119+
)
120+
121+
takeMVar receiverReady
122+
123+
forConcurrently_ connections $ \conn ->
124+
forM_ [0 .. messageCount] $ \_ -> send conn [payload]
125+
126+
takeMVar receiverDone
127+
128+
benchTransport :: TransportConfig -> TestTree
129+
benchTransport cfg@TransportConfig{transportName} =
130+
bgroup
131+
transportName
132+
[ bgroup
133+
"throughput"
134+
[ bgroup
135+
"single-connection"
136+
[ bench "small-msg" $ nfIO $ throughputBench cfg smallMessages
137+
, bench "default-msg" $ nfIO $ throughputBench cfg mediumMessages
138+
, bench "large-msg" $ nfIO $ throughputBench cfg largeMessages
139+
]
140+
, bgroup
141+
"multi-connection"
142+
[ bench "2-conn" $ nfIO $ throughputBench cfg smallMessages{connectionCount = 2, messageCount = 10_000}
143+
, bench "5-conn" $ nfIO $ throughputBench cfg smallMessages{connectionCount = 5, messageCount = 10_000}
144+
, bench "10-conn" $ nfIO $ throughputBench cfg smallMessages{connectionCount = 10, messageCount = 5_000}
145+
]
146+
]
147+
]
148+
149+
main :: IO ()
150+
main =
151+
defaultMain
152+
[ benchTransport tcpConfig
153+
, benchTransport quicConfig
154+
]
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
cabal-version: 3.0
2+
Name: network-transport-quic
3+
Version: 0.1.0
4+
build-Type: Simple
5+
License: BSD-3-Clause
6+
License-file: LICENSE
7+
Copyright: Laurent P. René de Cotret
8+
Author: Laurent P. René de Cotret
9+
maintainer: The Distributed Haskell team
10+
Stability: experimental
11+
Homepage: http://haskell-distributed.github.com
12+
Bug-Reports: https://github.com/haskell-distributed/distributed-process/issues
13+
Synopsis: Networking layer for Cloud Haskell based on QUIC
14+
Description: Networking layer for Cloud Haskell based on QUIC
15+
tested-with: GHC==8.10.7 GHC==9.0.2 GHC==9.2.8 GHC==9.4.5 GHC==9.6.4 GHC==9.8.2 GHC==9.10.1 GHC==9.12.1
16+
Category: Network
17+
extra-doc-files: ChangeLog
18+
extra-source-files: test/credentials/*
19+
20+
source-repository head
21+
Type: git
22+
Location: https://github.com/haskell-distributed/distributed-process
23+
SubDir: packages/network-transport-quic
24+
25+
common common
26+
ghc-options:
27+
-- warnings
28+
-Wall
29+
-Wcompat
30+
-Widentities
31+
-Wincomplete-uni-patterns
32+
-Wincomplete-record-updates
33+
-Wredundant-constraints
34+
-fhide-source-paths
35+
-Wpartial-fields
36+
-Wunused-packages
37+
-- The -threaded option is /required/ to use the quic library
38+
-threaded
39+
40+
library
41+
import: common
42+
build-depends: async
43+
, base >= 4.14 && < 5
44+
, binary >= 0.8 && < 0.10
45+
, bytestring >= 0.11 && < 0.13
46+
, containers
47+
, microlens-platform ^>=0.4
48+
, network >= 3.1 && < 3.3
49+
, network-transport >= 0.5 && < 0.6
50+
-- Prior to version 0.2.20, `quic` had issues with handling
51+
-- pending data in the stream buffer. This meant that vectored
52+
-- message sends did not work correctly at the transport layer
53+
, quic >=0.2.20 && <0.3
54+
, stm >=2.4 && <2.6
55+
, tls
56+
, tls-session-manager
57+
exposed-modules: Network.Transport.QUIC
58+
Network.Transport.QUIC.Internal
59+
other-modules: Network.Transport.QUIC.Internal.Configuration
60+
Network.Transport.QUIC.Internal.Client
61+
Network.Transport.QUIC.Internal.Messaging
62+
Network.Transport.QUIC.Internal.QUICAddr
63+
Network.Transport.QUIC.Internal.QUICTransport
64+
Network.Transport.QUIC.Internal.Server
65+
Network.Transport.QUIC.Internal.TLS
66+
default-language: Haskell2010
67+
default-extensions: ImportQualifiedPost
68+
hs-source-dirs: src
69+
70+
test-suite network-transport-quic-tests
71+
import: common
72+
default-language: Haskell2010
73+
default-extensions: ImportQualifiedPost
74+
main-is: Main.hs
75+
other-modules: Test.Network.Transport.QUIC
76+
Test.Network.Transport.QUIC.Internal.Messaging
77+
Test.Network.Transport.QUIC.Internal.QUICAddr
78+
type: exitcode-stdio-1.0
79+
hs-source-dirs: test
80+
build-depends: base
81+
, bytestring
82+
, filepath
83+
, hedgehog
84+
, network
85+
, network-transport
86+
, network-transport-quic
87+
, network-transport-tests
88+
, tasty ^>=1.5
89+
, tasty-flaky ^>= 0.1.3
90+
, tasty-hedgehog
91+
, tasty-hunit
92+
93+
benchmark network-transport-quic-bench
94+
import: common
95+
type: exitcode-stdio-1.0
96+
hs-source-dirs: bench
97+
main-is: Bench.hs
98+
default-language: Haskell2010
99+
ghc-options: -rtsopts -with-rtsopts=-N
100+
build-depends: async
101+
, base >=4.14 && <5
102+
, bytestring
103+
, filepath
104+
, network-transport
105+
, network-transport-tcp
106+
, network-transport-quic
107+
, tasty ^>=1.5
108+
, tasty-bench >=0.4
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
module Network.Transport.QUIC
2+
( -- * Main interface
3+
createTransport,
4+
5+
-- ** Transport configuration
6+
QUICTransportConfig (..),
7+
defaultQUICTransportConfig,
8+
9+
-- ** Re-export to generate credentials
10+
Credential,
11+
credentialLoadX509,
12+
)
13+
where
14+
15+
import Network.Transport.QUIC.Internal
16+
( -- \* Re-export to generate credentials
17+
Credential,
18+
createTransport,
19+
credentialLoadX509,
20+
defaultQUICTransportConfig,
21+
)
22+
import Network.Transport.QUIC.Internal.QUICTransport (QUICTransportConfig (..))

0 commit comments

Comments
 (0)