on master help sign in
Merge pull request #297 from diogob/tls-server
44feeffAgentM
project-m36.cabal+1−1
unifiedsplit
@@ -433,7 +433,7 @@ Test-Suite test-scripts
433
433
434Executable project-m36-websocket-server
434Executable project-m36-websocket-server
435 Default-Language: Haskell2010
435 Default-Language: Haskell2010
436− Build-Depends: base, aeson, path-pieces, either, conduit, http-api-data, template-haskell, websockets, aeson, optparse-applicative, project-m36, containers, bytestring, text, vector, uuid, megaparsec, haskeline, mtl, directory, base64-bytestring, random, MonadRandom, time, semigroups, attoparsec, parser-combinators, prettyprinter, network, modern-uri, http-conduit, base16-bytestring, http-types, cryptohash-sha256, wai, wai-websockets, warp
436+ Build-Depends: base, aeson, path-pieces, either, conduit, http-api-data, template-haskell, websockets, aeson, optparse-applicative, project-m36, containers, bytestring, text, vector, uuid, megaparsec, haskeline, mtl, directory, base64-bytestring, random, MonadRandom, time, semigroups, attoparsec, parser-combinators, prettyprinter, network, modern-uri, http-conduit, base16-bytestring, http-types, cryptohash-sha256, wai, wai-websockets, warp, warp-tls
437 Main-Is: ProjectM36/Server/WebSocket/websocket-server.hs
437 Main-Is: ProjectM36/Server/WebSocket/websocket-server.hs
438 Other-Modules: ProjectM36.Client.Json, ProjectM36.Server.RemoteCallTypes.Json, ProjectM36.Server.WebSocket, TutorialD.Interpreter, TutorialD.Interpreter.Base, TutorialD.Interpreter.DatabaseContextExpr, TutorialD.Interpreter.DatabaseContextIOOperator, TutorialD.Interpreter.Export.Base, TutorialD.Interpreter.Export.CSV, TutorialD.Interpreter.Import.Base, TutorialD.Interpreter.Import.BasicExamples, TutorialD.Interpreter.Import.CSV, TutorialD.Interpreter.Import.TutorialD, TutorialD.Interpreter.InformationOperator, TutorialD.Interpreter.RODatabaseContextOperator, TutorialD.Interpreter.RelationalExpr, TutorialD.Interpreter.TransactionGraphOperator, TutorialD.Interpreter.Types, TutorialD.Interpreter.TransGraphRelationalOperator, TutorialD.Interpreter.SchemaOperator, TutorialD.Printer
438 Other-Modules: ProjectM36.Client.Json, ProjectM36.Server.RemoteCallTypes.Json, ProjectM36.Server.WebSocket, TutorialD.Interpreter, TutorialD.Interpreter.Base, TutorialD.Interpreter.DatabaseContextExpr, TutorialD.Interpreter.DatabaseContextIOOperator, TutorialD.Interpreter.Export.Base, TutorialD.Interpreter.Export.CSV, TutorialD.Interpreter.Import.Base, TutorialD.Interpreter.Import.BasicExamples, TutorialD.Interpreter.Import.CSV, TutorialD.Interpreter.Import.TutorialD, TutorialD.Interpreter.InformationOperator, TutorialD.Interpreter.RODatabaseContextOperator, TutorialD.Interpreter.RelationalExpr, TutorialD.Interpreter.TransactionGraphOperator, TutorialD.Interpreter.Types, TutorialD.Interpreter.TransGraphRelationalOperator, TutorialD.Interpreter.SchemaOperator, TutorialD.Printer
439 GHC-Options: -Wall -threaded
439 GHC-Options: -Wall -threaded
src/bin/ProjectM36/Server/WebSocket/websocket-server.hs+17−3
@@ -2,11 +2,14 @@
2
2
3import Control.Concurrent
3import Control.Concurrent
4import Control.Exception
4import Control.Exception
5+import Control.Monad (when)
6+import Data.Maybe (isJust)
5import Data.String (fromString)
7import Data.String (fromString)
6import Network.HTTP.Types (status400)
8import Network.HTTP.Types (status400)
7import Network.Socket
9import Network.Socket
8import Network.Wai (Application, responseLBS)
10import Network.Wai (Application, responseLBS)
9import Network.Wai.Handler.Warp
11import Network.Wai.Handler.Warp
12+import Network.Wai.Handler.WarpTLS (runTLS, tlsSettings)
10import qualified Network.Wai.Handler.WebSockets as WS
13import qualified Network.Wai.Handler.WebSockets as WS
11import Network.WebSockets (defaultConnectionOptions)
14import Network.WebSockets (defaultConnectionOptions)
12import ProjectM36.Server
15import ProjectM36.Server
@@ -18,12 +21,20 @@ main :: IO ()
18main = do
21main = do
19 -- launch normal project-m36-server
22 -- launch normal project-m36-server
20 addressMVar <- newEmptyMVar
23 addressMVar <- newEmptyMVar
21− serverConfig <- parseConfigWithDefaults (defaultServerConfig {bindPort = 8000, bindHost = "127.0.0.1"})
24+ wsConfig <- parseWSConfigWithDefaults (defaultServerConfig {bindPort = 8000, bindHost = "127.0.0.1"})
25+
22 --usurp the serverConfig for our websocket server and make the proxied server run locally
26 --usurp the serverConfig for our websocket server and make the proxied server run locally
27+ let serverConfig = wsServerConfig wsConfig
23− let wsHost = bindHost serverConfig
28+ wsHost = bindHost serverConfig
24 wsPort = bindPort serverConfig
29 wsPort = bindPort serverConfig
25 serverHost = "127.0.0.1"
30 serverHost = "127.0.0.1"
26 serverConfig' = serverConfig {bindPort = 0, bindHost = serverHost}
31 serverConfig' = serverConfig {bindPort = 0, bindHost = serverHost}
32+ configCertificateFile = tlsCertificatePath wsConfig
33+ configKeyFile = tlsKeyPath wsConfig
34+
35+ when (isJust configCertificateFile /= isJust configKeyFile) $
36+ throwIO $ ErrorCall "TLS_CERTIFICATE_PATH and TLS_KEY_PATH must be set in tandem"
37+
27 _ <- forkFinally (launchServer serverConfig' (Just addressMVar) >> pure ()) (either throwIO pure)
38 _ <- forkFinally (launchServer serverConfig' (Just addressMVar) >> pure ()) (either throwIO pure)
28 --wait for server to be listening
39 --wait for server to be listening
29 addr <- takeMVar addressMVar
40 addr <- takeMVar addressMVar
@@ -34,7 +45,10 @@ main = do
34 wsApp = websocketProxyServer port serverHost
45 wsApp = websocketProxyServer port serverHost
35 waiApp = WS.websocketsOr defaultConnectionOptions wsApp backupApp
46 waiApp = WS.websocketsOr defaultConnectionOptions wsApp backupApp
36 settings = warpSettings wsHost (fromIntegral wsPort)
47 settings = warpSettings wsHost (fromIntegral wsPort)
48+
49+ case (configCertificateFile, configKeyFile) of
50+ (Just certificate, Just key) -> runTLS (tlsSettings certificate key) settings waiApp
37− runSettings settings waiApp
51+ _ -> runSettings settings waiApp
38
52
39backupApp :: Application
53backupApp :: Application
40backupApp _ respond = respond $ responseLBS status400 [] "Not a WebSocket request"
54backupApp _ respond = respond $ responseLBS status400 [] "Not a WebSocket request"
src/lib/ProjectM36/Server/Config.hs+6
@@ -12,6 +12,12 @@ data ServerConfig = ServerConfig { persistenceStrategy :: PersistenceStrategy,
12 }
12 }
13 deriving (Show)
13 deriving (Show)
14
14
15+data WebsocketServerConfig = WebsocketServerConfig { wsServerConfig :: ServerConfig,
16+ tlsCertificatePath :: Maybe String,
17+ tlsKeyPath :: Maybe String
18+ }
19+ deriving (Show)
20+
15defaultServerConfig :: ServerConfig
21defaultServerConfig :: ServerConfig
16defaultServerConfig = ServerConfig { persistenceStrategy = NoPersistence,
22defaultServerConfig = ServerConfig { persistenceStrategy = NoPersistence,
17 checkFS = True,
23 checkFS = True,
src/lib/ProjectM36/Server/ParseArgs.hs+31−14
@@ -17,14 +17,14 @@ parseArgsWithDefaults defaults = ServerConfig <$>
17 many parseGhcPkgPath <*>
17 many parseGhcPkgPath <*>
18 parseTimeout (perRequestTimeout defaults) <*>
18 parseTimeout (perRequestTimeout defaults) <*>
19 parseTestMode
19 parseTestMode
20−
20+
21parsePersistenceStrategy :: Parser PersistenceStrategy
21parsePersistenceStrategy :: Parser PersistenceStrategy
22parsePersistenceStrategy = CrashSafePersistence <$> (dbdirOpt <* fsyncOpt) <|>
22parsePersistenceStrategy = CrashSafePersistence <$> (dbdirOpt <* fsyncOpt) <|>
23 MinimalPersistence <$> dbdirOpt <|>
23 MinimalPersistence <$> dbdirOpt <|>
24 pure NoPersistence
24 pure NoPersistence
25− where
25+ where
26− dbdirOpt = strOption (short 'd' <>
26+ dbdirOpt = strOption (short 'd' <>
27− long "database-directory" <>
27+ long "database-directory" <>
28 metavar "DIRECTORY" <>
28 metavar "DIRECTORY" <>
29 showDefaultWith show
29 showDefaultWith show
30 )
30 )
@@ -34,43 +34,60 @@ parsePersistenceStrategy = CrashSafePersistence <$> (dbdirOpt <* fsyncOpt) <|>
34
34
35parseTestMode :: Parser Bool
35parseTestMode :: Parser Bool
36parseTestMode = flag True False (long "test-mode" <> hidden)
36parseTestMode = flag True False (long "test-mode" <> hidden)
37−
37+
38−parseCheckFS :: Parser Bool
38+parseCheckFS :: Parser Bool
39parseCheckFS = flag True False (long "disable-fscheck" <>
39parseCheckFS = flag True False (long "disable-fscheck" <>
40 help "Disable filesystem check for journaling.")
40 help "Disable filesystem check for journaling.")
41−
41+
42parseDatabaseName :: Parser DatabaseName
42parseDatabaseName :: Parser DatabaseName
43parseDatabaseName = strOption (short 'n' <>
43parseDatabaseName = strOption (short 'n' <>
44 long "database" <>
44 long "database" <>
45 metavar "DATABASE_NAME")
45 metavar "DATABASE_NAME")
46
46
47−parseHostname :: Hostname -> Parser Hostname
47+parseHostname :: Hostname -> Parser Hostname
48parseHostname defHostname = strOption (short 'h' <>
48parseHostname defHostname = strOption (short 'h' <>
49 long "hostname" <>
49 long "hostname" <>
50 metavar "HOST_NAME" <>
50 metavar "HOST_NAME" <>
51 value defHostname)
51 value defHostname)
52−
52+
53parsePort :: Port -> Parser Port
53parsePort :: Port -> Parser Port
54parsePort defPort = option auto (short 'p' <>
54parsePort defPort = option auto (short 'p' <>
55 long "port" <>
55 long "port" <>
56 metavar "PORT_NUMBER" <>
56 metavar "PORT_NUMBER" <>
57 value defPort)
57 value defPort)
58−
58+
59parseGhcPkgPath :: Parser String
59parseGhcPkgPath :: Parser String
60parseGhcPkgPath = strOption (long "ghc-pkg-dir" <>
60parseGhcPkgPath = strOption (long "ghc-pkg-dir" <>
61 metavar "GHC_PACKAGE_DIRECTORY")
61 metavar "GHC_PACKAGE_DIRECTORY")
62−
62+
63−parseTimeout :: Int -> Parser Int
63+parseTimeout :: Int -> Parser Int
64parseTimeout defTimeout = option auto (long "timeout" <>
64parseTimeout defTimeout = option auto (long "timeout" <>
65 metavar "MICROSECONDS" <>
65 metavar "MICROSECONDS" <>
66 value defTimeout)
66 value defTimeout)
67
67
68parseConfig :: IO ServerConfig
68parseConfig :: IO ServerConfig
69parseConfig = parseConfigWithDefaults defaultServerConfig
69parseConfig = parseConfigWithDefaults defaultServerConfig
70−
70+
71parseConfigWithDefaults :: ServerConfig -> IO ServerConfig
71parseConfigWithDefaults :: ServerConfig -> IO ServerConfig
72parseConfigWithDefaults defaults = execParser (info (parseArgsWithDefaults defaults <**> helpOption) idm)
72parseConfigWithDefaults defaults = execParser (info (parseArgsWithDefaults defaults <**> helpOption) idm)
73
73
74+parseWSConfigWithDefaults :: ServerConfig -> IO WebsocketServerConfig
75+parseWSConfigWithDefaults defaults = execParser (info (parseWSArgsWithDefaults defaults <**> helpOption) idm)
76+
77+parseWSArgsWithDefaults :: ServerConfig -> Parser WebsocketServerConfig
78+parseWSArgsWithDefaults defaults = WebsocketServerConfig <$>
79+ parseArgsWithDefaults defaults <*>
80+ parseTlsCertificatePath <*>
81+ parseTlsKeyPath
82+
83+
84+parseTlsCertificatePath :: Parser (Maybe String)
85+parseTlsCertificatePath = optional $ strOption (long "tls-certificate-path" <>
86+ metavar "TLS_CERTIFICATE_PATH")
87+
88+parseTlsKeyPath :: Parser (Maybe String)
89+parseTlsKeyPath = optional $ strOption (long "tls-key-path" <>
90+ metavar "TLS_KEY_PATH")
74
91
75helpOption :: Parser (a -> a)
92helpOption :: Parser (a -> a)
76helpOption = abortOption helpText $ mconcat
93helpOption = abortOption helpText $ mconcat
@@ -80,6 +97,6 @@ helpOption = abortOption helpText $ mconcat
80 where
97 where
81#if MIN_VERSION_optparse_applicative(0,16,0)
98#if MIN_VERSION_optparse_applicative(0,16,0)
82 helpText = ShowHelpText Nothing
99 helpText = ShowHelpText Nothing
83−#else
100+#else
84 helpText = ShowHelpText
101 helpText = ShowHelpText
85#endif
102#endif