2019-08-04 00:38:30 +03:00
|
|
|
{-
|
|
|
|
TODO Implement ports file.
|
|
|
|
|
|
|
|
TODO What is this abount?
|
|
|
|
|
|
|
|
// if we don't explicitly set this field, h2o will send with
|
|
|
|
// transfer-encoding: chunked
|
|
|
|
//
|
|
|
|
if ( 1 == has_len_i ) {
|
|
|
|
rec_u->res.content_length = ( 0 == gen_u->bod_u ) ?
|
|
|
|
0 : gen_u->bod_u->len_w;
|
|
|
|
}
|
|
|
|
|
|
|
|
TODO Does this matter, is is using WAI's default behavior ok.
|
|
|
|
|
|
|
|
rec_u->res.reason = (status < 200) ? "weird" :
|
|
|
|
(status < 300) ? "ok" :
|
|
|
|
(status < 400) ? "moved" :
|
|
|
|
(status < 500) ? "missing" :
|
|
|
|
"hosed";
|
|
|
|
-}
|
|
|
|
|
2019-05-16 03:00:10 +03:00
|
|
|
module Vere.Http.Server where
|
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
import Arvo hiding (ServerId, reqBody, reqUrl, secure)
|
|
|
|
import Data.Conduit
|
2019-07-12 22:18:14 +03:00
|
|
|
import Noun
|
2019-08-03 22:26:45 +03:00
|
|
|
import UrbitPrelude hiding (Builder)
|
2019-08-02 08:07:20 +03:00
|
|
|
import Vere.Pier.Types
|
2019-05-16 03:00:10 +03:00
|
|
|
|
2019-08-03 03:09:53 +03:00
|
|
|
import Data.Binary.Builder (Builder, fromByteString)
|
|
|
|
import Data.Bits (shiftL, (.|.))
|
|
|
|
import Network.Socket (SockAddr(..))
|
|
|
|
import System.Random (randomIO)
|
2019-08-03 22:26:45 +03:00
|
|
|
import Vere.Http (convertHeaders, unconvertHeaders)
|
2019-05-31 05:53:00 +03:00
|
|
|
|
|
|
|
import qualified Network.HTTP.Types as H
|
|
|
|
import qualified Network.Wai as W
|
2019-08-03 22:26:45 +03:00
|
|
|
import qualified Network.Wai.Conduit as W
|
2019-05-31 05:53:00 +03:00
|
|
|
import qualified Network.Wai.Handler.Warp as W
|
2019-05-25 02:03:46 +03:00
|
|
|
import qualified Network.Wai.Handler.WarpTLS as W
|
2019-05-24 02:58:18 +03:00
|
|
|
|
2019-08-02 09:56:42 +03:00
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
-- RespAction -- Reorganized HttpEvent for Cleaner Processing ------------------
|
2019-08-02 09:56:42 +03:00
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
{-
|
|
|
|
The sequence of actions on a given request *should* be:
|
2019-08-02 09:56:42 +03:00
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
[%head .] [%bloc .]* %done
|
2019-08-02 09:56:42 +03:00
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
But we will actually accept anything, and mostly do the right
|
|
|
|
thing. There are two situations where we ignore ignore the data from
|
|
|
|
some actions.
|
2019-08-02 09:56:42 +03:00
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
- If you send something *after* a %done action, it will be ignored.
|
|
|
|
- If you send a %done before a %head, we will produce "444 No
|
|
|
|
Response" with an empty response body.
|
|
|
|
-}
|
|
|
|
data RespAction
|
|
|
|
= RAHead ResponseHeader
|
|
|
|
| RABloc File
|
|
|
|
| RADone
|
2019-07-23 05:35:15 +03:00
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
reorgHttpEvent :: HttpEvent -> [RespAction]
|
|
|
|
reorgHttpEvent = \case
|
|
|
|
Start head mBlk isDone -> [RAHead head]
|
|
|
|
<> toList (RABloc <$> mBlk)
|
|
|
|
<> if isDone then [RADone] else []
|
|
|
|
Cancel () -> [RADone]
|
|
|
|
Continue mBlk isDone -> toList (RABloc <$> mBlk)
|
|
|
|
<> if isDone then [RADone] else []
|
2019-07-23 05:35:15 +03:00
|
|
|
|
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
-- Generic Service Stop/Restart -- Using an MVar for Atomicity -----------------
|
2019-06-26 03:15:49 +03:00
|
|
|
|
2019-08-02 08:07:20 +03:00
|
|
|
{-
|
|
|
|
Restart a running service.
|
2019-05-16 03:00:10 +03:00
|
|
|
|
2019-08-02 08:07:20 +03:00
|
|
|
This can probably be made simpler, but it
|
2019-05-16 03:00:10 +03:00
|
|
|
|
2019-08-02 08:07:20 +03:00
|
|
|
- Sets the MVar to Nothing if there was an exception whil starting
|
|
|
|
or stopping the service.
|
2019-05-16 03:00:10 +03:00
|
|
|
|
2019-08-02 08:07:20 +03:00
|
|
|
- Keeps the MVar lock until the restart process finishes.
|
|
|
|
-}
|
|
|
|
restartService :: forall s r
|
|
|
|
. MVar (Maybe s)
|
|
|
|
-> IO (s, r)
|
|
|
|
-> (s -> IO ())
|
|
|
|
-> IO (Either SomeException r)
|
|
|
|
restartService vServ sstart kkill = do
|
|
|
|
modifyMVar vServ $ \case
|
|
|
|
Nothing -> doStart
|
|
|
|
Just sv -> doRestart sv
|
|
|
|
where
|
|
|
|
doRestart :: s -> IO (Maybe s, Either SomeException r)
|
|
|
|
doRestart serv =
|
|
|
|
try (kkill serv) >>= \case
|
|
|
|
Left exn -> pure (Nothing, Left exn)
|
|
|
|
Right () -> doStart
|
2019-05-16 03:04:21 +03:00
|
|
|
|
2019-08-02 08:07:20 +03:00
|
|
|
doStart :: IO (Maybe s, Either SomeException r)
|
|
|
|
doStart =
|
|
|
|
try sstart <&> \case
|
|
|
|
Right (s,r) -> (Just s, Right r)
|
|
|
|
Left exn -> (Nothing, Left exn)
|
|
|
|
|
|
|
|
|
|
|
|
stopService :: forall s
|
|
|
|
. MVar (Maybe s)
|
|
|
|
-> (s -> IO ())
|
|
|
|
-> IO (Either SomeException ())
|
|
|
|
stopService vServ kkill = do
|
|
|
|
modifyMVar vServ $ \case
|
|
|
|
Nothing -> pure (Nothing, Right ())
|
|
|
|
Just sv -> do res <- try (kkill sv)
|
|
|
|
pure (Nothing, res)
|
2019-05-16 03:00:10 +03:00
|
|
|
|
2019-05-24 02:58:18 +03:00
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
-- Live Requests Table -- All Requests Still Waiting for Responses -------------
|
|
|
|
|
2019-08-07 03:46:47 +03:00
|
|
|
type ReqId = Decimal
|
|
|
|
type SeqId = Decimal -- TODO Unused. Why is this a thing?
|
2019-08-03 22:26:45 +03:00
|
|
|
|
|
|
|
data LiveReqs = LiveReqs
|
|
|
|
{ nextReqId :: ReqId
|
|
|
|
, activeReqs :: Map ReqId (TMVar RespAction)
|
|
|
|
}
|
|
|
|
|
|
|
|
emptyLiveReqs :: LiveReqs
|
|
|
|
emptyLiveReqs = LiveReqs 1 mempty
|
|
|
|
|
|
|
|
respondToLiveReq :: TVar LiveReqs -> ReqId -> RespAction -> STM ()
|
|
|
|
respondToLiveReq var req ev = do
|
|
|
|
mVar <- lookup req . activeReqs <$> readTVar var
|
|
|
|
case mVar of
|
|
|
|
Nothing -> pure ()
|
|
|
|
Just tv -> putTMVar tv ev
|
|
|
|
|
|
|
|
rmLiveReq :: TVar LiveReqs -> ReqId -> STM ()
|
|
|
|
rmLiveReq var reqId = do
|
|
|
|
liv <- readTVar var
|
|
|
|
writeTVar var (liv { activeReqs = deleteMap reqId (activeReqs liv) })
|
|
|
|
|
|
|
|
newLiveReq :: TVar LiveReqs -> STM (ReqId, TMVar RespAction)
|
|
|
|
newLiveReq var = do
|
|
|
|
liv <- readTVar var
|
|
|
|
tmv <- newEmptyTMVar
|
|
|
|
|
|
|
|
let (nex, act) = (nextReqId liv, activeReqs liv)
|
|
|
|
|
|
|
|
writeTVar var (LiveReqs (nex+1) (insertMap nex tmv act))
|
|
|
|
|
|
|
|
pure (nex, tmv)
|
|
|
|
|
|
|
|
|
2019-08-02 09:56:42 +03:00
|
|
|
-- Random Helpers --------------------------------------------------------------
|
|
|
|
|
|
|
|
cordBytes :: Cord -> ByteString
|
|
|
|
cordBytes = encodeUtf8 . unCord
|
|
|
|
|
2019-08-04 00:38:30 +03:00
|
|
|
pass :: Monad m => m ()
|
|
|
|
pass = pure ()
|
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
whenJust :: Monad m => Maybe a -> (a -> m ()) -> m ()
|
|
|
|
whenJust Nothing act = pure ()
|
|
|
|
whenJust (Just a) act = act a
|
|
|
|
|
|
|
|
cookMeth :: W.Request -> Maybe Method
|
|
|
|
cookMeth = H.parseMethod . W.requestMethod >>> \case
|
|
|
|
Left _ -> Nothing
|
|
|
|
Right m -> Just m
|
|
|
|
|
|
|
|
reqIdCord :: ReqId -> Cord
|
|
|
|
reqIdCord = Cord . tshow
|
|
|
|
|
|
|
|
reqBody :: W.Request -> IO (Maybe File)
|
|
|
|
reqBody req = do
|
|
|
|
bodyLbs <- W.strictRequestBody req
|
|
|
|
if length bodyLbs == 0
|
|
|
|
then pure $ Nothing
|
|
|
|
else pure $ Just $ File $ Octs (toStrict bodyLbs)
|
|
|
|
|
|
|
|
reqAddr :: W.Request -> Address
|
|
|
|
reqAddr = W.remoteHost >>> \case
|
|
|
|
SockAddrInet _ a -> AIpv4 (Ipv4 a)
|
|
|
|
SockAddrInet6 _ _ a _ -> AIpv6 (mkIpv6 a)
|
|
|
|
_ -> error "invalid sock addr"
|
|
|
|
|
|
|
|
mkIpv6 :: (Word32, Word32, Word32, Word32) -> Ipv6
|
|
|
|
mkIpv6 (p, q, r, s) = Ipv6 (pBits .|. qBits .|. rBits .|. sBits)
|
|
|
|
where
|
|
|
|
pBits = shiftL (fromIntegral p) 0
|
|
|
|
qBits = shiftL (fromIntegral q) 32
|
|
|
|
rBits = shiftL (fromIntegral r) 64
|
|
|
|
sBits = shiftL (fromIntegral s) 96
|
|
|
|
|
|
|
|
reqUrl :: W.Request -> Cord
|
|
|
|
reqUrl = Cord . decodeUtf8 . W.rawPathInfo
|
|
|
|
|
2019-08-02 09:56:42 +03:00
|
|
|
|
|
|
|
-- Utilities for Constructing Events -------------------------------------------
|
2019-08-02 08:07:20 +03:00
|
|
|
|
2019-08-04 00:38:30 +03:00
|
|
|
data WhichServer = Secure | Insecure | Loopback
|
|
|
|
deriving (Eq)
|
|
|
|
|
2019-08-02 08:07:20 +03:00
|
|
|
servEv :: HttpServerEv -> Ev
|
|
|
|
servEv = EvBlip . BlipEvHttpServer
|
|
|
|
|
|
|
|
bornEv :: KingId -> Ev
|
2019-08-02 09:56:42 +03:00
|
|
|
bornEv king =
|
2019-08-03 22:26:45 +03:00
|
|
|
servEv $ HttpServerEvBorn (king, ()) ()
|
2019-08-02 08:07:20 +03:00
|
|
|
|
2019-08-03 03:09:53 +03:00
|
|
|
liveEv :: ServId -> Port -> Maybe Port -> Ev
|
|
|
|
liveEv sId non sec =
|
|
|
|
servEv $ HttpServerEvLive (sId, ()) non sec
|
2019-08-02 09:56:42 +03:00
|
|
|
|
2019-08-07 03:46:47 +03:00
|
|
|
cancelEv :: ServId -> ReqId -> Ev
|
|
|
|
cancelEv sId reqId =
|
|
|
|
servEv $ HttpServerEvCancelRequest (sId, reqId, 1, ()) ()
|
|
|
|
|
2019-08-04 00:38:30 +03:00
|
|
|
reqEv :: ServId -> ReqId -> WhichServer -> Address -> HttpRequest -> Ev
|
|
|
|
reqEv sId reqId which addr req =
|
|
|
|
case which of
|
|
|
|
Loopback ->
|
|
|
|
servEv $ HttpServerEvRequestLocal (sId, reqId, 1, ())
|
|
|
|
$ HttpServerReq False addr req
|
|
|
|
_ ->
|
|
|
|
servEv $ HttpServerEvRequest (sId, reqId, 1, ())
|
|
|
|
$ HttpServerReq (which == Secure) addr req
|
2019-05-25 02:03:46 +03:00
|
|
|
|
|
|
|
|
2019-07-12 22:18:14 +03:00
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
-- Http Server Flows -----------------------------------------------------------
|
2019-08-02 08:07:20 +03:00
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
{-
|
|
|
|
This accepts all action orderings so that there are no edge-cases
|
|
|
|
to be handled:
|
2019-08-02 08:07:20 +03:00
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
- If %bloc before %head, collect it and wait for %head.
|
|
|
|
- If %done before %head, ignore all chunks and produce Nothing.
|
|
|
|
-}
|
|
|
|
getHead :: TMVar RespAction -> IO (Maybe (ResponseHeader, [File]))
|
|
|
|
getHead tmv = go []
|
|
|
|
where
|
|
|
|
go çunks = atomically (readTMVar tmv) >>= \case
|
|
|
|
RAHead head -> pure $ Just (head, reverse çunks)
|
|
|
|
RABloc çunk -> go (çunk : çunks)
|
|
|
|
RADone -> pure Nothing
|
2019-08-02 08:07:20 +03:00
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
{-
|
|
|
|
- Immediatly yield all of the initial chunks
|
|
|
|
- Yield the data from %bloc action.
|
|
|
|
- Close the stream when we hit a %done action.
|
|
|
|
-}
|
|
|
|
streamBlocks :: [File] -> TMVar RespAction -> ConduitT () (Flush Builder) IO ()
|
|
|
|
streamBlocks init tmv =
|
|
|
|
for_ init yieldÇunk >> go
|
|
|
|
where
|
|
|
|
yieldFlush = \x -> yield (Chunk x) >> yield Flush
|
|
|
|
yieldÇunk = yieldFlush . fromByteString . unOcts . unFile
|
|
|
|
logDupHead = putStrLn "Multiple %head actions on one request"
|
2019-08-02 08:07:20 +03:00
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
go = atomically (readTMVar tmv) >>= \case
|
|
|
|
RAHead head -> logDupHead >> go
|
|
|
|
RABloc çunk -> yieldÇunk çunk
|
|
|
|
RADone -> pure ()
|
2019-08-02 08:07:20 +03:00
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
sendResponse :: (W.Response -> IO W.ResponseReceived)
|
|
|
|
-> TMVar RespAction
|
|
|
|
-> IO W.ResponseReceived
|
|
|
|
sendResponse cb tmv = do
|
|
|
|
getHead tmv >>= \case
|
|
|
|
Nothing -> do cb $ W.responseLBS (H.mkStatus 444 "No Response") [] ""
|
|
|
|
Just (h,i) -> do let çunks = streamBlocks i tmv
|
|
|
|
cb $ W.responseSource (hdrStatus h) (hdrHeaders h) çunks
|
2019-08-02 08:07:20 +03:00
|
|
|
where
|
2019-08-03 22:26:45 +03:00
|
|
|
hdrHeaders :: ResponseHeader -> [H.Header]
|
|
|
|
hdrHeaders = unconvertHeaders . headers
|
2019-08-02 08:07:20 +03:00
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
hdrStatus :: ResponseHeader -> H.Status
|
|
|
|
hdrStatus = toEnum . fromIntegral . statusCode
|
2019-08-02 08:07:20 +03:00
|
|
|
|
2019-08-07 03:46:47 +03:00
|
|
|
liveReq :: TVar LiveReqs -> Acquire (ReqId, TMVar RespAction)
|
|
|
|
liveReq vLiv = mkAcquire ins del
|
|
|
|
where
|
|
|
|
ins = atomically (newLiveReq vLiv)
|
|
|
|
del = atomically . rmLiveReq vLiv . fst
|
|
|
|
|
2019-08-04 00:38:30 +03:00
|
|
|
app :: ServId -> TVar LiveReqs -> (Ev -> STM ()) -> WhichServer -> W.Application
|
|
|
|
app sId liv plan which req respond = do
|
2019-08-07 03:46:47 +03:00
|
|
|
with (liveReq liv) $ \(reqId, respVar) -> do
|
2019-08-04 00:38:30 +03:00
|
|
|
body <- reqBody req
|
|
|
|
meth <- maybe (error "bad method") pure (cookMeth req)
|
2019-08-03 22:26:45 +03:00
|
|
|
|
2019-08-04 00:38:30 +03:00
|
|
|
let addr = reqAddr req
|
|
|
|
hdrs = convertHeaders $ W.requestHeaders req
|
|
|
|
evReq = HttpRequest meth (reqUrl req) hdrs body
|
2019-08-03 22:26:45 +03:00
|
|
|
|
2019-08-04 00:38:30 +03:00
|
|
|
atomically $ plan (reqEv sId reqId which addr evReq)
|
2019-05-25 02:03:46 +03:00
|
|
|
|
2019-08-07 03:46:47 +03:00
|
|
|
try (sendResponse respond respVar) >>= \case
|
|
|
|
Right rr -> pure rr
|
|
|
|
Left exn -> do atomically $ plan (cancelEv sId reqId)
|
|
|
|
throwIO (exn :: SomeException)
|
2019-05-25 02:03:46 +03:00
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
|
|
|
|
-- Top-Level Driver Interface --------------------------------------------------
|
|
|
|
|
|
|
|
newtype Drv = Drv { unDrv :: MVar (Maybe Serv) }
|
|
|
|
|
|
|
|
data Serv = Serv
|
|
|
|
{ sServId :: ServId
|
|
|
|
, sConfig :: HttpServerConf
|
2019-08-04 00:38:30 +03:00
|
|
|
, sLoopTid :: Async ()
|
2019-08-03 22:26:45 +03:00
|
|
|
, sHttpTid :: Async ()
|
|
|
|
, sHttpsTid :: Async ()
|
|
|
|
, sLiveReqs :: TVar LiveReqs
|
|
|
|
}
|
2019-08-02 08:07:20 +03:00
|
|
|
|
|
|
|
{-
|
2019-08-02 09:56:42 +03:00
|
|
|
TODO Need to find an open port.
|
2019-08-02 08:07:20 +03:00
|
|
|
-}
|
2019-08-03 03:09:53 +03:00
|
|
|
startServ :: HttpServerConf -> (Ev -> STM ())
|
|
|
|
-> IO (Serv, (ServId, Port, Maybe Port))
|
|
|
|
startServ conf plan = do
|
2019-08-02 09:56:42 +03:00
|
|
|
tls <- case (hscSecure conf) of
|
|
|
|
Nothing -> error "HACK: Implement support for missing PEMs"
|
|
|
|
Just (PEM key, PEM cert) ->
|
|
|
|
pure (W.tlsSettingsMemory (cordBytes cert) (cordBytes key))
|
2019-08-02 08:07:20 +03:00
|
|
|
|
2019-08-07 03:46:47 +03:00
|
|
|
sId <- ServId . UV . fromIntegral <$> (randomIO :: IO Word32)
|
2019-08-02 09:56:42 +03:00
|
|
|
liv <- newTVarIO emptyLiveReqs
|
2019-05-25 02:03:46 +03:00
|
|
|
|
2019-08-04 00:38:30 +03:00
|
|
|
let httpPort = 8080 -- 80 if real ship
|
|
|
|
httpsPort = 8443 -- 443 if real ship
|
|
|
|
loopPort = 12321 -- 443 if real ship
|
|
|
|
|
|
|
|
loopOpts = W.defaultSettings & W.setPort (fromIntegral loopPort)
|
|
|
|
& W.setHost "127.0.0.1"
|
|
|
|
& W.setTimeout (5 * 60)
|
|
|
|
httpOpts = W.defaultSettings & W.setPort (fromIntegral httpPort)
|
|
|
|
httpsOpts = W.defaultSettings & W.setPort (fromIntegral httpsPort)
|
2019-05-25 02:03:46 +03:00
|
|
|
|
2019-08-04 00:38:30 +03:00
|
|
|
loopTid <- async $ W.runSettings loopOpts $ app sId liv plan Loopback
|
|
|
|
httpTid <- async $ W.runSettings httpOpts $ app sId liv plan Insecure
|
|
|
|
httpsTid <- async $ W.runTLS tls httpsOpts $ app sId liv plan Secure
|
2019-05-25 02:03:46 +03:00
|
|
|
|
2019-08-04 00:38:30 +03:00
|
|
|
let res = (sId, Port httpPort, Just $ Port httpsPort)
|
2019-07-22 21:10:27 +03:00
|
|
|
|
2019-08-04 00:38:30 +03:00
|
|
|
pure (Serv sId conf loopTid httpTid httpsTid liv, res)
|
2019-08-02 09:56:42 +03:00
|
|
|
|
2019-08-03 03:09:53 +03:00
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
killServ :: Serv -> IO ()
|
2019-08-04 00:38:30 +03:00
|
|
|
killServ Serv{sLoopTid, sHttpTid, sHttpsTid} = do
|
|
|
|
cancel sLoopTid
|
2019-08-03 22:26:45 +03:00
|
|
|
cancel sHttpTid
|
|
|
|
cancel sHttpsTid
|
2019-08-04 00:38:30 +03:00
|
|
|
wait sLoopTid
|
2019-08-03 22:26:45 +03:00
|
|
|
wait sHttpTid
|
|
|
|
wait sHttpsTid
|
2019-08-02 09:56:42 +03:00
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
kill :: Drv -> IO ()
|
|
|
|
kill (Drv v) = stopService v killServ >>= fromEither
|
2019-08-03 03:09:53 +03:00
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
respond :: Drv -> ReqId -> HttpEvent -> IO ()
|
|
|
|
respond (Drv v) reqId ev = do
|
|
|
|
readMVar v >>= \case
|
|
|
|
Nothing -> pure ()
|
|
|
|
Just sv -> atomically $ for_ (reorgHttpEvent ev) $
|
|
|
|
respondToLiveReq (sLiveReqs sv) reqId
|
2019-05-25 02:03:46 +03:00
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
serv :: KingId -> QueueEv -> ([Ev], Acquire (EffCb HttpServerEf))
|
|
|
|
serv king plan =
|
|
|
|
(initialEvents, runHttpServer)
|
2019-08-03 03:09:53 +03:00
|
|
|
where
|
2019-08-03 22:26:45 +03:00
|
|
|
initialEvents :: [Ev]
|
|
|
|
initialEvents = [bornEv king]
|
2019-05-25 02:03:46 +03:00
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
runHttpServer :: Acquire (EffCb HttpServerEf)
|
|
|
|
runHttpServer = handleEf <$> mkAcquire (Drv <$> newMVar Nothing) kill
|
2019-08-02 09:56:42 +03:00
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
restart :: Drv -> HttpServerConf -> IO (ServId, Port, Maybe Port)
|
|
|
|
restart (Drv var) conf = do
|
|
|
|
fromEither =<< restartService var (startServ conf plan) killServ
|
2019-08-02 09:56:42 +03:00
|
|
|
|
2019-08-03 22:26:45 +03:00
|
|
|
handleEf :: Drv -> HttpServerEf -> IO ()
|
|
|
|
handleEf drv = \case
|
|
|
|
HSESetConfig (i, ()) conf ->
|
|
|
|
when (i == fromIntegral king) $ do
|
|
|
|
(sId, insecurePort, securePort) <- restart drv conf
|
|
|
|
atomically $ plan (liveEv sId insecurePort securePort)
|
|
|
|
HSEResponse (i, req, _seq, ()) ev ->
|
|
|
|
when (i == fromIntegral king) $
|
|
|
|
respond drv (fromIntegral req) ev
|