module Test.WithExtras ( defaultTlsSettings , testWithTLSApplication , testWithTLSApplicationSettings , withTLSApplication , withTLSApplicationSettings ) where import Control.Concurrent import Control.Concurrent.Async import Control.Exception import Control.Monad (when) import Network.Socket (Socket, close) import Network.Wai (Application) import Network.Wai.Handler.Warp ( Port , Settings , defaultSettings , defaultShouldDisplayException , openFreePort ) import Network.Wai.Handler.Warp.Internal (settingsBeforeMainLoop) import Network.Wai.Handler.WarpTLS ( TLSSettings , runTLSSocket , tlsSettings ) import Paths_wai_middleware_delegate (getDataFileName) -- | The settings used in the integration tests defaultTlsSettings :: IO TLSSettings defaultTlsSettings = tlsSettings <$> (getDataFileName "test/certificate.pem") <*> (getDataFileName "test/key.pem") {- | Runs the given 'Application' on a free port. Passes the port to the given operation and executes it, while the 'Application' is running. Shuts down the server before returning. -} withTLSApplication :: TLSSettings -> IO Application -> (Port -> IO a) -> IO a withTLSApplication = withTLSApplicationSettings defaultSettings {- | 'withTLSApplication' with given 'Settings'. This will ignore the port value set by 'setPort' in 'Settings'. -} withTLSApplicationSettings :: Settings -> TLSSettings -> IO Application -> (Port -> IO a) -> IO a withTLSApplicationSettings settings' tlsSettings' mkApp action = do app <- mkApp withFreePort $ \(port, sock) -> do started <- mkWaiter let settings = settings' { settingsBeforeMainLoop = notify started () >> settingsBeforeMainLoop settings' } result <- race (runTLSSocket tlsSettings' settings sock app) (waitFor started >> action port) case result of Left () -> throwIO $ ErrorCall "Unexpected: runSettingsSocket exited" Right x -> return x testWithTLSApplication :: TLSSettings -> IO Application -> (Port -> IO a) -> IO a testWithTLSApplication = testWithTLSApplicationSettings defaultSettings testWithTLSApplicationSettings :: Settings -> TLSSettings -> IO Application -> (Port -> IO a) -> IO a testWithTLSApplicationSettings settings tlsSettings' mkApp action = do callingThread <- myThreadId app <- mkApp let wrappedApp request respond = app request respond `catch` \e -> do when (defaultShouldDisplayException e) (throwTo callingThread e) throwIO e withTLSApplicationSettings settings tlsSettings' (return wrappedApp) action data Waiter a = Waiter { notify :: a -> IO () , waitFor :: IO a } mkWaiter :: IO (Waiter a) mkWaiter = do mvar <- newEmptyMVar return Waiter { notify = putMVar mvar , waitFor = readMVar mvar } -- | Like 'openFreePort' but closes the socket before exiting. withFreePort :: ((Port, Socket) -> IO a) -> IO a withFreePort = bracket openFreePort (close . snd)