@@ -23,6 +23,7 @@ import Data.ByteString (ByteString)
2323import qualified Data.ByteString as BS
2424import qualified Data.ByteString.Char8 as BSC
2525import qualified Data.ByteString.Lazy.Char8 as BSLC
26+import qualified Data.ByteString.UTF8 as BSUTF8
2627import Data.Functor.Identity
2728import Data.List (uncons)
2829import Data.Map (Map)
@@ -34,6 +35,7 @@ import Data.String (fromString)
3435import Data.Text (Text)
3536import qualified Data.Text as T
3637import qualified Data.Text.Encoding as T
38+import Data.Time.Clock (getCurrentTime, addUTCTime)
3739import Language.Javascript.JSaddle.Run (syncPoint)
3840import Language.Javascript.JSaddle.WebSockets
3941import Network.HTTP.Client (Manager, defaultManagerSettings, newManager)
@@ -43,6 +45,7 @@ import Network.Socket
4345import Network.Wai (Application)
4446import qualified Network.Wai as W
4547import Network.Wai.Handler.Warp
48+import Network.Wai.Handler.WarpTLS
4649import Network.Wai.Handler.Warp.Internal (settingsHost, settingsPort)
4750import Network.WebSockets (ConnectionOptions)
4851import Network.WebSockets.Connection (defaultConnectionOptions)
@@ -50,6 +53,10 @@ import qualified Obelisk.Asset.Serve.Snap as Snap
5053import Obelisk.Backend
5154import Obelisk.Frontend
5255import Obelisk.Route.Frontend
56+import qualified OpenSSL.PEM as PEM
57+import qualified OpenSSL.RSA as RSA
58+import qualified OpenSSL.X509 as X509
59+import qualified OpenSSL.X509.Request as X509Request
5360import Reflex.Dom.Core
5461import Snap.Core (Snap)
5562import System.Environment
@@ -113,13 +120,36 @@ runWidget conf configs frontend validFullEncoder = do
113120 beforeMainLoop = do
114121 putStrLn $ "Frontend running on " <> T.unpack (URI.render uri)
115122 settings = setBeforeMainLoop beforeMainLoop (setPort port (setTimeout 3600 defaultSettings))
123+ -- Providing TLS here will also incidentally provide it to proxied requests to the backend.
124+ prepareRunner = case (uri ^? uriScheme . _Just . unRText) of
125+ Just "https" -> do
126+ -- Generate a private key and self-signed certificate for TLS
127+ privateKey <- RSA.generateRSAKey' 2048 3
128+
129+ certRequest <- X509Request.newX509Req
130+ _ <- X509Request.setPublicKey certRequest privateKey
131+ _ <- X509Request.signX509Req certRequest privateKey Nothing
132+
133+ cert <- X509.newX509 >>= X509Request.makeX509FromReq certRequest
134+ _ <- X509.setPublicKey cert privateKey
135+ now <- getCurrentTime
136+ _ <- X509.setNotBefore cert $ addUTCTime (-1) now
137+ _ <- X509.setNotAfter cert $ addUTCTime (365 * 24 * 60 * 60) now
138+ _ <- X509.signX509 cert privateKey Nothing
139+
140+ certByteString <- BSUTF8.fromString <$> PEM.writeX509 cert
141+ privateKeyByteString <- BSUTF8.fromString <$> PEM.writePKCS8PrivateKey privateKey Nothing
142+
143+ return $ runTLSSocket (tlsSettingsMemory certByteString privateKeyByteString)
144+ _ -> return runSettingsSocket
145+ runner <- prepareRunner
116146 bracket
117147 (bindPortTCPRetry settings (logPortBindErr port) (_runConfig_retryTimeout conf))
118148 close
119149 (\skt -> do
120150 man <- newManager defaultManagerSettings
121151 app <- obeliskApp configs defaultConnectionOptions frontend validFullEncoder uri $ fallbackProxy redirectHost redirectPort man
122− runSettingsSocket settings skt app)
152+ runner settings skt app)
123153
124154obeliskApp
125155 :: forall frontendRoute backendRoute