@@ -23,6 +23,7 @@ import Control.Concurrent.Async (concurrently_)
2323import Control.Concurrent.STM
2424import Control.Exception (SomeException , throwIO , try )
2525import Control.Monad
26+ import Control.Monad.Except (runExceptT )
2627import Control.Monad.IO.Class
2728import CoreTests.MsgStoreTests (testJournalStoreCfg )
2829import Data.Bifunctor (first )
@@ -42,6 +43,7 @@ import Simplex.Messaging.Encoding
4243import Simplex.Messaging.Encoding.String
4344import Simplex.Messaging.Parsers (parseAll , parseString )
4445import Simplex.Messaging.Protocol
46+ import Simplex.Messaging.Client (chooseTransportHost , defaultNetworkConfig )
4547import Simplex.Messaging.Server (exportMessages )
4648import Simplex.Messaging.Server.Env.STM (AStoreType (.. ), MsgStore (.. ), ServerConfig (.. ), ServerStoreCfg (.. ), readWriteQueueStore )
4749import Simplex.Messaging.Server.Expiration
@@ -50,6 +52,11 @@ import Simplex.Messaging.Server.MsgStore.Types (MsgStoreClass (..), QSType (..),
5052import Simplex.Messaging.Server.Stats (PeriodStatsData (.. ), ServerStatsData (.. ))
5153import Simplex.Messaging.Server.StoreLog (StoreLogRecord (.. ), closeStoreLog )
5254import Simplex.Messaging.Transport
55+ import Simplex.Messaging.Transport.Client (TransportClientConfig (.. ), defaultTransportClientConfig , runTLSTransportClient )
56+ import Simplex.Messaging.Transport.WebSockets (WS )
57+ import Simplex.Messaging.Transport.Server (loadFileFingerprint )
58+ import Simplex.Messaging.Server.Web (attachStaticAndWS )
59+ import Data.X509.Validation (Fingerprint (.. ))
5360import Simplex.Messaging.Util (whenM )
5461import Simplex.Messaging.Version (mkVersionRange )
5562import System.Directory (doesDirectoryExist , doesFileExist , removeDirectoryRecursive , removeFile )
@@ -101,6 +108,7 @@ serverTests = do
101108 describe " Short links" $ do
102109 testInvQueueLinkData
103110 testContactQueueLinkData
111+ describe " WebSocket and TLS on same port" testWebSocketAndTLS
104112
105113pattern Resp :: CorrId -> QueueId -> BrokerMsg -> Transmission (Either ErrorType BrokerMsg )
106114pattern Resp corrId queueId command <- (corrId, queueId, Right command)
@@ -1484,3 +1492,41 @@ serverSyntaxTests (ATransport t) = do
14841492 (Maybe TAuthorizations , ByteString , ByteString , BrokerMsg ) ->
14851493 Expectation
14861494 command >#> response = withFrozenCallStack $ smpServerTest t command `shouldReturn` response
1495+
1496+ -- | Test that both native TLS and WebSocket clients can connect to the same port.
1497+ -- Native TLS uses useSNI=False, WebSocket uses useSNI=True for routing.
1498+ testWebSocketAndTLS :: SpecWith (ASrvTransport , AStoreType )
1499+ testWebSocketAndTLS =
1500+ it " native TLS and WebSocket clients work on same port" $ \ (_t, msType) -> do
1501+ Fingerprint fpHTTP <- loadFileFingerprint " tests/fixtures/web_ca.crt"
1502+ let httpKeyHash = C. KeyHash fpHTTP
1503+ attachStaticAndWS " tests/fixtures" $ \ attachHTTP ->
1504+ withSmpServerConfig (cfgWebOn msType testPort) (Just attachHTTP) $ \ _ -> do
1505+ g <- C. newRandom
1506+ (rPub, rKey) <- atomically $ C. generateAuthKeyPair C. SEd25519 g
1507+ (sPub, sKey) <- atomically $ C. generateAuthKeyPair C. SEd25519 g
1508+ (dhPub, dhPriv :: C. PrivateKeyX25519 ) <- atomically $ C. generateKeyPair g
1509+
1510+ -- Connect via native TLS (useSNI=False, default) and create a queue
1511+ (sId, rId, srvDh) <- testSMPClient @ TLS $ \ rh -> do
1512+ Resp " 1" _ (Ids rId sId srvDh) <- signSendRecv rh rKey (" 1" , NoEntity , New rPub dhPub)
1513+ Resp " 2" _ OK <- signSendRecv rh rKey (" 2" , rId, KEY sPub)
1514+ pure (sId, rId, srvDh)
1515+ let dec = decryptMsgV3 $ C. dh' srvDh dhPriv
1516+
1517+ -- Connect via WebSocket (useSNI=True) and send a message
1518+ Right useHost <- pure $ chooseTransportHost defaultNetworkConfig testHost
1519+ let wsTcConfig = defaultTransportClientConfig {useSNI = True } :: TransportClientConfig
1520+ runTLSTransportClient defaultSupportedParamsHTTPS Nothing wsTcConfig Nothing useHost testPort (Just httpKeyHash) $ \ (h :: WS 'TClient) ->
1521+ runExceptT (smpClientHandshake h Nothing testKeyHash supportedClientSMPRelayVRange False Nothing ) >>= \ case
1522+ Right sh -> do
1523+ Resp " 3" _ OK <- signSendRecv sh sKey (" 3" , sId, _SEND " hello from websocket" )
1524+ pure ()
1525+ Left e -> error $ show e
1526+
1527+ -- Verify message received via native TLS
1528+ testSMPClient @ TLS $ \ rh -> do
1529+ (Resp " 4" _ (SOK Nothing ), Resp " " _ (Msg mId msg)) <- signSendRecv2 rh rKey (" 4" , rId, SUB )
1530+ dec mId msg `shouldBe` Right " hello from websocket"
1531+ Resp " 5" _ OK <- signSendRecv rh rKey (" 5" , rId, ACK mId)
1532+ pure ()
0 commit comments