packages feed

project-m36-0.9.5: src/bin/ProjectM36/Server/WebSocket/websocket-server.hs

{-# LANGUAGE CPP #-}

import Control.Concurrent
import Control.Exception
import Control.Monad (when, void)
import Data.Maybe (isJust)
import Data.String (fromString)
import Network.HTTP.Types (status400)
import Network.Socket
import Network.Wai (Application, responseLBS)
import Network.Wai.Handler.Warp
import Network.Wai.Handler.WarpTLS (runTLS, tlsSettings)
import qualified Network.Wai.Handler.WebSockets as WS
import Network.WebSockets (defaultConnectionOptions)
import ProjectM36.Server
import ProjectM36.Server.Config
import ProjectM36.Server.ParseArgs
import ProjectM36.Server.WebSocket

main :: IO ()
main = do
  -- launch normal project-m36-server
  addressMVar <- newEmptyMVar
  wsConfig <- parseWSConfigWithDefaults (defaultServerConfig {bindPort = 8000, bindHost = "127.0.0.1"})

  --usurp the serverConfig for our websocket server and make the proxied server run locally
  let serverConfig = wsServerConfig wsConfig
      wsHost = bindHost serverConfig
      wsPort = bindPort serverConfig
      serverHost = "127.0.0.1"
      serverConfig' = serverConfig {bindPort = 0, bindHost = serverHost}
      configCertificateFile = tlsCertificatePath wsConfig
      configKeyFile = tlsKeyPath wsConfig

  when (isJust configCertificateFile /= isJust configKeyFile) $
    throwIO $ ErrorCall "TLS_CERTIFICATE_PATH and TLS_KEY_PATH must be set in tandem"

  _ <- forkFinally (void (launchServer serverConfig' (Just addressMVar))) (either throwIO pure)
  --wait for server to be listening
  addr <- takeMVar addressMVar
  let port =
        case addr of
          SockAddrInet port' _ -> fromIntegral port'
          _ -> error "unsupported socket address (IPv4 only currently)"
      wsApp = websocketProxyServer port serverHost
      waiApp = WS.websocketsOr defaultConnectionOptions wsApp backupApp
      settings = warpSettings wsHost (fromIntegral wsPort)

  case (configCertificateFile, configKeyFile) of
    (Just certificate, Just key) -> runTLS (tlsSettings certificate key) settings waiApp
    _ -> runSettings settings waiApp

backupApp :: Application
backupApp _ respond = respond $ responseLBS status400 [] "Not a WebSocket request"

warpSettings :: HostName -> Port -> Settings
warpSettings host port =
  setHost (fromString host)
    . setPort port
    . setServerName "project-m36"
    . setTimeout 3600
    . setGracefulShutdownTimeout (Just 5)
    $ defaultSettings