2020-01-23 07:16:09 +03:00
|
|
|
{-|
|
|
|
|
Http Server Driver
|
2019-12-21 00:47:20 +03:00
|
|
|
|
2020-01-23 07:16:09 +03:00
|
|
|
TODO Make sure that HTTP sockets get closed on shutdown.
|
2019-12-20 00:20:31 +03:00
|
|
|
|
2019-08-08 01:24:02 +03:00
|
|
|
TODO What is this about?
|
2019-08-04 00:38:30 +03:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2019-08-08 01:24:02 +03:00
|
|
|
TODO Does this matter, is is using WAI's default behavior ok?
|
2019-08-04 00:38:30 +03:00
|
|
|
|
|
|
|
rec_u->res.reason = (status < 200) ? "weird" :
|
|
|
|
(status < 300) ? "ok" :
|
|
|
|
(status < 400) ? "moved" :
|
|
|
|
(status < 500) ? "missing" :
|
|
|
|
"hosed";
|
|
|
|
-}
|
|
|
|
|
2020-05-08 21:29:18 +03:00
|
|
|
module Urbit.Vere.Eyre
|
|
|
|
( eyre
|
2020-05-08 02:35:49 +03:00
|
|
|
, multiServ
|
|
|
|
, ShipAPI(..)
|
|
|
|
)
|
|
|
|
where
|
2019-05-16 03:00:10 +03:00
|
|
|
|
2020-01-24 08:28:38 +03:00
|
|
|
import Urbit.Prelude hiding (Builder)
|
2020-05-08 02:35:49 +03:00
|
|
|
|
2020-05-08 21:29:18 +03:00
|
|
|
import Urbit.Arvo hiding (ServerId, reqUrl, secure)
|
2020-05-08 02:35:49 +03:00
|
|
|
import Urbit.King.Config
|
2020-05-08 21:29:18 +03:00
|
|
|
import Urbit.Vere.Eyre.Wai hiding (ReqId)
|
2020-01-24 08:28:38 +03:00
|
|
|
import Urbit.Vere.Pier.Types
|
2019-05-16 03:00:10 +03:00
|
|
|
|
2020-05-08 02:35:49 +03:00
|
|
|
import Data.PEM (pemParseBS, pemWriteBS)
|
|
|
|
import Network.Socket (SockAddr(..))
|
|
|
|
import RIO.Prelude (decodeUtf8Lenient)
|
|
|
|
import System.Directory (doesFileExist, removeFile)
|
|
|
|
import System.Random (randomIO)
|
|
|
|
import Urbit.Vere.Http (convertHeaders, unconvertHeaders)
|
2019-05-31 05:53:00 +03:00
|
|
|
|
|
|
|
import qualified Network.HTTP.Types as H
|
2019-12-20 00:20:31 +03:00
|
|
|
import qualified Network.Socket as Net
|
2019-05-31 05:53:00 +03:00
|
|
|
import qualified Network.Wai as W
|
|
|
|
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-08 01:24:02 +03:00
|
|
|
-- Internal Types --------------------------------------------------------------
|
|
|
|
|
2020-03-03 02:24:28 +03:00
|
|
|
type HasShipEnv e = (HasLogFunc e, HasNetworkConfig e, HasPierConfig e)
|
|
|
|
|
2019-12-19 22:30:09 +03:00
|
|
|
type ReqId = UD
|
2019-08-08 01:24:02 +03:00
|
|
|
|
|
|
|
data Ports = Ports
|
|
|
|
{ pHttps :: Maybe Port
|
|
|
|
, pHttp :: Port
|
|
|
|
, pLoop :: Port
|
|
|
|
}
|
|
|
|
deriving (Eq, Ord, Show)
|
|
|
|
|
2020-05-08 02:35:49 +03:00
|
|
|
newtype Drv = Drv { _unDrv :: MVar (Maybe Serv) }
|
2019-08-08 01:24:02 +03:00
|
|
|
|
|
|
|
data Serv = Serv
|
|
|
|
{ sServId :: ServId
|
|
|
|
, sConfig :: HttpServerConf
|
|
|
|
, sLoopTid :: Async ()
|
|
|
|
, sHttpTid :: Async ()
|
|
|
|
, sHttpsTid :: Maybe (Async ())
|
2019-12-22 05:24:54 +03:00
|
|
|
, sLoopSock :: Net.Socket
|
|
|
|
, sHttpSock :: Net.Socket
|
|
|
|
, sHttpsSock :: Net.Socket
|
2019-08-08 01:24:02 +03:00
|
|
|
, sPorts :: Ports
|
|
|
|
, sPortsFile :: FilePath
|
|
|
|
, sLiveReqs :: TVar LiveReqs
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
|
2020-01-23 07:16:09 +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.
|
|
|
|
-}
|
2019-08-29 03:26:59 +03:00
|
|
|
restartService :: ∀e s. HasLogFunc e
|
|
|
|
=> MVar (Maybe s)
|
|
|
|
-> RIO e s
|
|
|
|
-> (s -> RIO e ())
|
|
|
|
-> RIO e (Either SomeException s)
|
2019-08-02 08:07:20 +03:00
|
|
|
restartService vServ sstart kkill = do
|
2019-08-29 03:26:59 +03:00
|
|
|
logDebug "restartService"
|
2019-08-02 08:07:20 +03:00
|
|
|
modifyMVar vServ $ \case
|
|
|
|
Nothing -> doStart
|
|
|
|
Just sv -> doRestart sv
|
|
|
|
where
|
2019-08-29 03:26:59 +03:00
|
|
|
doRestart :: s -> RIO e (Maybe s, Either SomeException s)
|
2019-08-08 01:24:02 +03:00
|
|
|
doRestart serv = do
|
2019-08-29 03:26:59 +03:00
|
|
|
logDebug "doStart"
|
2019-08-02 08:07:20 +03:00
|
|
|
try (kkill serv) >>= \case
|
|
|
|
Left exn -> pure (Nothing, Left exn)
|
|
|
|
Right () -> doStart
|
2019-05-16 03:04:21 +03:00
|
|
|
|
2019-08-29 03:26:59 +03:00
|
|
|
doStart :: RIO e (Maybe s, Either SomeException s)
|
2019-08-08 01:24:02 +03:00
|
|
|
doStart = do
|
2019-08-29 03:26:59 +03:00
|
|
|
logDebug "doStart"
|
2019-08-02 08:07:20 +03:00
|
|
|
try sstart <&> \case
|
2019-08-08 01:24:02 +03:00
|
|
|
Right s -> (Just s, Right s)
|
|
|
|
Left exn -> (Nothing, Left exn)
|
2019-08-02 08:07:20 +03:00
|
|
|
|
2019-08-29 03:26:59 +03:00
|
|
|
stopService :: HasLogFunc e
|
|
|
|
=> MVar (Maybe s)
|
|
|
|
-> (s -> RIO e ())
|
|
|
|
-> RIO e (Either SomeException ())
|
2019-08-02 08:07:20 +03:00
|
|
|
stopService vServ kkill = do
|
2019-08-29 03:26:59 +03:00
|
|
|
logDebug "stopService"
|
2019-08-02 08:07:20 +03:00
|
|
|
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-08 01:24:02 +03:00
|
|
|
-- Ports File ------------------------------------------------------------------
|
|
|
|
|
2019-08-29 03:26:59 +03:00
|
|
|
removePortsFile :: FilePath -> RIO e ()
|
2019-08-08 01:24:02 +03:00
|
|
|
removePortsFile pax =
|
2019-08-29 03:26:59 +03:00
|
|
|
io (doesFileExist pax) >>= \case
|
|
|
|
True -> io $ removeFile pax
|
2019-08-08 01:24:02 +03:00
|
|
|
False -> pure ()
|
|
|
|
|
|
|
|
portsFileText :: Ports -> Text
|
|
|
|
portsFileText Ports{..} =
|
|
|
|
unlines $ catMaybes
|
|
|
|
[ pHttps <&> \p -> (tshow p <> " secure public")
|
|
|
|
, Just (tshow (unPort pHttp) <> " insecure public")
|
|
|
|
, Just (tshow (unPort pLoop) <> " insecure loopback")
|
|
|
|
]
|
|
|
|
|
2019-08-29 03:26:59 +03:00
|
|
|
writePortsFile :: FilePath -> Ports -> RIO e ()
|
2019-08-08 01:24:02 +03:00
|
|
|
writePortsFile f = writeFile f . encodeUtf8 . portsFileText
|
|
|
|
|
|
|
|
|
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-08 01:24:02 +03:00
|
|
|
liveEv :: ServId -> Ports -> Ev
|
|
|
|
liveEv sId Ports{..} =
|
|
|
|
servEv $ HttpServerEvLive (sId, ()) pHttp pHttps
|
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-08-03 22:26:45 +03:00
|
|
|
-- Top-Level Driver Interface --------------------------------------------------
|
|
|
|
|
2020-03-03 02:24:28 +03:00
|
|
|
data CantOpenPort = CantOpenPort W.Port
|
|
|
|
deriving (Eq, Ord, Show, Exception)
|
|
|
|
|
|
|
|
data WhichPort
|
|
|
|
= WPSpecific W.Port
|
|
|
|
| WPChoices [W.Port]
|
|
|
|
|
|
|
|
data SockOpts = SockOpts
|
|
|
|
{ soLocalhost :: Bool
|
|
|
|
, soWhich :: WhichPort
|
|
|
|
}
|
|
|
|
|
|
|
|
data PortsToTry = PortsToTry
|
|
|
|
{ pttSec :: SockOpts
|
|
|
|
, pttIns :: SockOpts
|
|
|
|
, pttLop :: SockOpts
|
|
|
|
}
|
|
|
|
|
2020-01-23 07:16:09 +03:00
|
|
|
{-|
|
2019-12-20 23:58:16 +03:00
|
|
|
Opens a socket on some port, accepting connections from `127.0.0.1`
|
|
|
|
if fake and `0.0.0.0` if real.
|
2019-12-20 00:20:31 +03:00
|
|
|
|
2019-12-20 23:58:16 +03:00
|
|
|
It will attempt to open a socket on each of the supplied ports in
|
|
|
|
order. If they all fail, it will ask the operating system to give
|
|
|
|
us an open socket on *any* open port. If that fails, it will throw
|
|
|
|
an exception.
|
2019-12-20 00:20:31 +03:00
|
|
|
-}
|
2020-03-03 02:24:28 +03:00
|
|
|
openPort :: forall e . HasLogFunc e => SockOpts -> RIO e (W.Port, Net.Socket)
|
|
|
|
openPort SockOpts {..} = case soWhich of
|
|
|
|
WPSpecific x -> insist (fromIntegral x)
|
|
|
|
WPChoices xs -> loop (fromIntegral <$> xs)
|
|
|
|
|
|
|
|
where
|
|
|
|
loop :: [W.Port] -> RIO e (W.Port, Net.Socket)
|
|
|
|
loop = \case
|
|
|
|
[] -> do
|
|
|
|
logTrace "Fallback: asking the OS to give us some free port."
|
|
|
|
ps <- io W.openFreePort
|
|
|
|
logTrace (display ("Opened port " <> tshow (fst ps)))
|
|
|
|
pure ps
|
|
|
|
x : xs -> do
|
|
|
|
logTrace (display ("Trying to open port " <> tshow x))
|
|
|
|
io (tryOpen x) >>= \case
|
|
|
|
Left (err :: IOError) -> do
|
|
|
|
logWarn (display ("Failed to open port " <> tshow x))
|
|
|
|
logWarn (display (tshow err))
|
|
|
|
loop xs
|
|
|
|
Right ps -> do
|
|
|
|
logTrace (display ("Opened port " <> tshow (fst ps)))
|
|
|
|
pure ps
|
|
|
|
|
|
|
|
insist :: W.Port -> RIO e (W.Port, Net.Socket)
|
|
|
|
insist p = do
|
|
|
|
logTrace (display ("Opening configured port " <> tshow p))
|
|
|
|
io (tryOpen p) >>= \case
|
|
|
|
Left (err :: IOError) -> do
|
|
|
|
logWarn (display ("Failed to open port " <> tshow p))
|
|
|
|
logWarn (display (tshow err))
|
|
|
|
throwIO (CantOpenPort p)
|
|
|
|
Right ps -> do
|
|
|
|
logTrace (display ("Opened port " <> tshow (fst ps)))
|
|
|
|
pure ps
|
|
|
|
|
|
|
|
bindTo = if soLocalhost then "127.0.0.1" else "0.0.0.0"
|
|
|
|
|
|
|
|
getBindAddr :: W.Port -> IO SockAddr
|
|
|
|
getBindAddr por =
|
|
|
|
Net.getAddrInfo Nothing (Just bindTo) (Just (show por)) >>= \case
|
|
|
|
[] -> error "this should never happen."
|
|
|
|
x : _ -> pure (Net.addrAddress x)
|
|
|
|
|
|
|
|
bindListenPort :: W.Port -> Net.Socket -> IO Net.PortNumber
|
|
|
|
bindListenPort por sok = do
|
|
|
|
Net.bind sok =<< getBindAddr por
|
|
|
|
Net.listen sok 1
|
|
|
|
Net.socketPort sok
|
|
|
|
|
|
|
|
-- `inet_addr`, `bind`, and `listen` all throw `IOError` if they fail.
|
|
|
|
tryOpen :: W.Port -> IO (Either IOError (W.Port, Net.Socket))
|
|
|
|
tryOpen por = do
|
|
|
|
sok <- Net.socket Net.AF_INET Net.Stream Net.defaultProtocol
|
|
|
|
try (bindListenPort por sok) >>= \case
|
|
|
|
Left exn -> Net.close sok $> Left exn
|
|
|
|
Right por -> pure (Right (fromIntegral por, sok))
|
|
|
|
|
|
|
|
httpServerPorts :: HasShipEnv e => Bool -> RIO e PortsToTry
|
|
|
|
httpServerPorts fak = do
|
|
|
|
ins <- view (networkConfigL . ncHttpPort . to (fmap fromIntegral))
|
|
|
|
sec <- view (networkConfigL . ncHttpsPort . to (fmap fromIntegral))
|
|
|
|
lop <- view (networkConfigL . ncLocalPort . to (fmap fromIntegral))
|
|
|
|
localMode <- view (networkConfigL . ncNetMode . to (== NMLocalhost))
|
|
|
|
|
|
|
|
let local = localMode || fak
|
|
|
|
|
|
|
|
let pttSec = case (sec, fak) of
|
|
|
|
(Just p , _ ) -> SockOpts local (WPSpecific p)
|
|
|
|
(Nothing, False) -> SockOpts local (WPChoices (443 : [8443 .. 8448]))
|
|
|
|
(Nothing, True ) -> SockOpts local (WPChoices ([8443 .. 8448]))
|
|
|
|
|
|
|
|
let pttIns = case (ins, fak) of
|
|
|
|
(Just p , _ ) -> SockOpts local (WPSpecific p)
|
|
|
|
(Nothing, False) -> SockOpts local (WPChoices (80 : [8080 .. 8085]))
|
|
|
|
(Nothing, True ) -> SockOpts local (WPChoices [8080 .. 8085])
|
|
|
|
|
|
|
|
let pttLop = case (lop, fak) of
|
|
|
|
(Just p , _) -> SockOpts local (WPSpecific p)
|
|
|
|
(Nothing, _) -> SockOpts local (WPChoices [12321 .. 12326])
|
|
|
|
|
|
|
|
pure (PortsToTry { .. })
|
|
|
|
|
2020-05-08 02:35:49 +03:00
|
|
|
eyreApp
|
|
|
|
:: HasLogFunc e
|
|
|
|
=> e
|
|
|
|
-> ServId
|
|
|
|
-> TVar LiveReqs
|
|
|
|
-> (Ev -> STM ())
|
|
|
|
-> WhichServer
|
|
|
|
-> W.Application
|
|
|
|
eyreApp env sId vLive plan which =
|
|
|
|
app env vLive onReq onCancel
|
|
|
|
where
|
|
|
|
bodFile "" = Nothing
|
|
|
|
bodFile bs = Just $ File $ Octs bs
|
|
|
|
|
|
|
|
onReq :: Word64 -> ReqInfo -> STM ()
|
|
|
|
onReq reqId ReqInfo{..} = do
|
|
|
|
let evBod = bodFile riBod
|
|
|
|
evHdr = convertHeaders riHdr
|
|
|
|
evUrl = Cord (decodeUtf8Lenient riUrl)
|
|
|
|
evReq = HttpRequest riMet evUrl evHdr evBod
|
|
|
|
reqUd = fromIntegral reqId
|
|
|
|
event = reqEv sId reqUd which riAdr evReq
|
|
|
|
|
|
|
|
plan event
|
|
|
|
|
|
|
|
onCancel :: Word64 -> STM ()
|
|
|
|
onCancel reqId = plan (cancelEv sId (fromIntegral reqId))
|
|
|
|
|
2020-04-30 22:13:14 +03:00
|
|
|
parseCerts :: ByteString -> Maybe (ByteString, [ByteString])
|
|
|
|
parseCerts bs = do
|
|
|
|
pems <- pemParseBS bs & either (const Nothing) Just
|
|
|
|
case pems of
|
|
|
|
[] -> Nothing
|
|
|
|
p:ps -> pure (pemWriteBS p, pemWriteBS <$> ps)
|
|
|
|
|
2020-05-08 02:35:49 +03:00
|
|
|
fByt :: File -> ByteString
|
|
|
|
fByt = unOcts . unFile
|
|
|
|
|
|
|
|
reorgHttpEvent :: HttpEvent -> [RespAct]
|
|
|
|
reorgHttpEvent = \case
|
|
|
|
Start h b True -> [RAFull (hSta h) (hHdr h) (fByt $ fromMaybe "" b)]
|
|
|
|
Start h b False -> [RAHead (hSta h) (hHdr h) (fByt $ fromMaybe "" b)]
|
|
|
|
Cancel () -> [RADone]
|
|
|
|
Continue b done -> toList (RABloc . fByt <$> b)
|
|
|
|
<> if done then [RADone] else []
|
|
|
|
where
|
|
|
|
hHdr :: ResponseHeader -> [H.Header]
|
|
|
|
hHdr = unconvertHeaders . headers
|
|
|
|
|
|
|
|
hSta :: ResponseHeader -> H.Status
|
|
|
|
hSta = toEnum . fromIntegral . statusCode
|
|
|
|
|
|
|
|
|
|
|
|
respond :: HasLogFunc e
|
|
|
|
=> Drv -> Word64 -> HttpEvent -> RIO e ()
|
|
|
|
respond (Drv v) reqId ev = do
|
|
|
|
readMVar v >>= \case
|
|
|
|
Nothing -> logError "Got a response to a request that does not exist."
|
|
|
|
Just sv -> do logDebug $ displayShow ev
|
|
|
|
for_ (reorgHttpEvent ev) $
|
|
|
|
atomically . routeRespAct (sLiveReqs sv) reqId
|
|
|
|
|
|
|
|
wainBytes :: Wain -> ByteString
|
|
|
|
wainBytes = encodeUtf8 . unWain
|
|
|
|
|
2020-03-03 02:24:28 +03:00
|
|
|
startServ :: (HasPierConfig e, HasLogFunc e, HasNetworkConfig e)
|
2019-12-20 00:20:31 +03:00
|
|
|
=> Bool -> HttpServerConf -> (Ev -> STM ())
|
2019-08-29 03:26:59 +03:00
|
|
|
-> RIO e Serv
|
2019-12-20 00:20:31 +03:00
|
|
|
startServ isFake conf plan = do
|
2019-08-29 03:26:59 +03:00
|
|
|
logDebug "startServ"
|
2019-08-08 01:24:02 +03:00
|
|
|
|
2020-04-30 22:13:14 +03:00
|
|
|
let tls = do (PEM key, PEM certs) <- hscSecure conf
|
|
|
|
(cert, chain) <- parseCerts (wainBytes certs)
|
|
|
|
pure $ W.tlsSettingsChainMemory cert chain $ wainBytes key
|
2019-08-02 08:07:20 +03:00
|
|
|
|
2019-08-29 03:26:59 +03:00
|
|
|
sId <- io $ 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
|
|
|
|
2020-03-03 02:24:28 +03:00
|
|
|
ptt <- httpServerPorts isFake
|
2019-12-20 00:20:31 +03:00
|
|
|
|
2020-03-03 02:24:28 +03:00
|
|
|
(httpPortInt, httpSock) <- openPort (pttIns ptt)
|
|
|
|
(httpsPortInt, httpsSock) <- openPort (pttSec ptt)
|
|
|
|
(loopPortInt, loopSock) <- openPort (pttLop ptt)
|
2019-08-22 04:18:10 +03:00
|
|
|
|
|
|
|
let httpPort = Port (fromIntegral httpPortInt)
|
|
|
|
httpsPort = Port (fromIntegral httpsPortInt)
|
|
|
|
loopPort = Port (fromIntegral loopPortInt)
|
2019-08-04 00:38:30 +03:00
|
|
|
|
2019-08-22 04:18:10 +03:00
|
|
|
let loopOpts = W.defaultSettings & W.setPort (fromIntegral loopPort)
|
2019-08-04 00:38:30 +03:00
|
|
|
& W.setHost "127.0.0.1"
|
|
|
|
& W.setTimeout (5 * 60)
|
2019-12-20 01:20:04 +03:00
|
|
|
httpOpts = W.defaultSettings & W.setHost "*"
|
|
|
|
& W.setPort (fromIntegral httpPort)
|
|
|
|
httpsOpts = W.defaultSettings & W.setHost "*"
|
|
|
|
& W.setPort (fromIntegral httpsPort)
|
2019-05-25 02:03:46 +03:00
|
|
|
|
2019-08-29 03:26:59 +03:00
|
|
|
env <- ask
|
2019-08-08 01:24:02 +03:00
|
|
|
|
2019-08-29 03:26:59 +03:00
|
|
|
logDebug "Starting loopback server"
|
|
|
|
loopTid <- async $ io
|
|
|
|
$ W.runSettingsSocket loopOpts loopSock
|
2020-05-08 02:35:49 +03:00
|
|
|
$ eyreApp env sId liv plan Loopback
|
2019-05-25 02:03:46 +03:00
|
|
|
|
2019-08-29 03:26:59 +03:00
|
|
|
logDebug "Starting HTTP server"
|
|
|
|
httpTid <- async $ io
|
|
|
|
$ W.runSettingsSocket httpOpts httpSock
|
2020-05-08 02:35:49 +03:00
|
|
|
$ eyreApp env sId liv plan Insecure
|
2019-08-29 03:26:59 +03:00
|
|
|
|
|
|
|
logDebug "Starting HTTPS server"
|
2019-08-08 01:24:02 +03:00
|
|
|
httpsTid <- for tls $ \tlsOpts ->
|
2019-08-29 03:26:59 +03:00
|
|
|
async $ io
|
|
|
|
$ W.runTLSSocket tlsOpts httpsOpts httpsSock
|
2020-05-08 02:35:49 +03:00
|
|
|
$ eyreApp env sId liv plan Secure
|
2019-08-08 01:24:02 +03:00
|
|
|
|
2019-12-17 17:31:50 +03:00
|
|
|
pierPath <- view pierPathL
|
2019-08-08 01:24:02 +03:00
|
|
|
let por = Ports (tls <&> const httpsPort) httpPort loopPort
|
|
|
|
fil = pierPath <> "/.http.ports"
|
|
|
|
|
2019-08-29 03:26:59 +03:00
|
|
|
logDebug $ displayShow (sId, por, fil)
|
2019-07-22 21:10:27 +03:00
|
|
|
|
2019-08-29 03:26:59 +03:00
|
|
|
logDebug "Finished started HTTP Servers"
|
2019-08-02 09:56:42 +03:00
|
|
|
|
2019-12-22 05:24:54 +03:00
|
|
|
pure $ Serv sId conf
|
|
|
|
loopTid httpTid httpsTid
|
|
|
|
httpSock httpsSock loopSock
|
|
|
|
por fil liv
|
2019-08-03 03:09:53 +03:00
|
|
|
|
2019-08-29 03:26:59 +03:00
|
|
|
killServ :: HasLogFunc e => Serv -> RIO e ()
|
2019-08-08 01:24:02 +03:00
|
|
|
killServ Serv{..} = do
|
2019-08-04 00:38:30 +03:00
|
|
|
cancel sLoopTid
|
2019-08-03 22:26:45 +03:00
|
|
|
cancel sHttpTid
|
2019-08-08 01:24:02 +03:00
|
|
|
traverse_ cancel sHttpsTid
|
2019-12-22 05:24:54 +03:00
|
|
|
io $ Net.close sHttpSock
|
|
|
|
io $ Net.close sHttpsSock
|
|
|
|
io $ Net.close sLoopSock
|
2019-08-08 01:24:02 +03:00
|
|
|
removePortsFile sPortsFile
|
|
|
|
(void . waitCatch) sLoopTid
|
|
|
|
(void . waitCatch) sHttpTid
|
|
|
|
traverse_ (void . waitCatch) sHttpsTid
|
2019-08-02 09:56:42 +03:00
|
|
|
|
2019-08-29 03:26:59 +03:00
|
|
|
kill :: HasLogFunc e => Drv -> RIO e ()
|
2019-08-03 22:26:45 +03:00
|
|
|
kill (Drv v) = stopService v killServ >>= fromEither
|
2019-08-03 03:09:53 +03:00
|
|
|
|
2020-05-08 21:29:18 +03:00
|
|
|
eyre :: ∀e. HasShipEnv e
|
2019-12-20 00:20:31 +03:00
|
|
|
=> KingId -> QueueEv -> Bool
|
2019-08-29 03:26:59 +03:00
|
|
|
-> ([Ev], RAcquire e (EffCb e HttpServerEf))
|
2020-05-08 21:29:18 +03:00
|
|
|
eyre king plan isFake =
|
2019-08-03 22:26:45 +03:00
|
|
|
(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-29 03:26:59 +03:00
|
|
|
runHttpServer :: RAcquire e (EffCb e HttpServerEf)
|
|
|
|
runHttpServer = handleEf <$> mkRAcquire (Drv <$> newMVar Nothing) kill
|
2019-08-02 09:56:42 +03:00
|
|
|
|
2019-08-29 03:26:59 +03:00
|
|
|
restart :: Drv -> HttpServerConf -> RIO e Serv
|
2019-08-03 22:26:45 +03:00
|
|
|
restart (Drv var) conf = do
|
2019-08-29 03:26:59 +03:00
|
|
|
logDebug "Restarting http server"
|
2019-12-20 00:20:31 +03:00
|
|
|
res <- fromEither =<<
|
|
|
|
restartService var (startServ isFake conf plan) killServ
|
2019-08-29 03:26:59 +03:00
|
|
|
logDebug "Done restating http server"
|
2019-08-08 01:24:02 +03:00
|
|
|
pure res
|
2019-08-02 09:56:42 +03:00
|
|
|
|
2019-08-29 03:26:59 +03:00
|
|
|
handleEf :: Drv -> HttpServerEf -> RIO e ()
|
2019-08-03 22:26:45 +03:00
|
|
|
handleEf drv = \case
|
2019-08-08 01:24:02 +03:00
|
|
|
HSESetConfig (i, ()) conf -> do
|
|
|
|
-- print (i, king)
|
|
|
|
-- when (i == fromIntegral king) $ do
|
2019-08-29 03:26:59 +03:00
|
|
|
logDebug "restarting"
|
2019-08-08 01:24:02 +03:00
|
|
|
Serv{..} <- restart drv conf
|
2019-08-29 03:26:59 +03:00
|
|
|
logDebug "Enqueue %live"
|
2019-08-08 01:24:02 +03:00
|
|
|
atomically $ plan (liveEv sServId sPorts)
|
2019-08-29 03:26:59 +03:00
|
|
|
logDebug "Write ports file"
|
2019-08-08 01:24:02 +03:00
|
|
|
writePortsFile sPortsFile sPorts
|
|
|
|
HSEResponse (i, req, _seq, ()) ev -> do
|
|
|
|
-- print (i, king)
|
|
|
|
-- when (i == fromIntegral king) $ do
|
2019-08-29 03:26:59 +03:00
|
|
|
logDebug "respond"
|
2019-08-03 22:26:45 +03:00
|
|
|
respond drv (fromIntegral req) ev
|
2020-05-05 19:30:37 +03:00
|
|
|
|
|
|
|
|
|
|
|
-- Multi-Tenet HTTP ------------------------------------------------------------
|
|
|
|
|
|
|
|
{-
|
|
|
|
# Very First Phase: Shared HTTP, no SSL.
|
|
|
|
|
|
|
|
- Global configuration flag for shared HTTP port.
|
|
|
|
|
|
|
|
- Shared server starts before ships.
|
|
|
|
|
|
|
|
- Shared server is informed when ships go up and come down.
|
|
|
|
|
|
|
|
- Shared server delivers requests to existing HTTP driver.
|
|
|
|
|
|
|
|
- Existing HTTP driver can send responses to shared HTTP server.
|
|
|
|
-}
|
|
|
|
|
|
|
|
type ShareRequ = (ServId, ReqId, WhichServer, Address, HttpRequest)
|
|
|
|
type ShareResp = (ServId, UD, UD, HttpEvent)
|
|
|
|
|
|
|
|
data ShipAPI = ShipAPI
|
|
|
|
{ sapiReq :: ShareRequ -> STM ()
|
|
|
|
, sapiRes :: STM ShareResp
|
|
|
|
}
|
|
|
|
|
|
|
|
data MultiServ = MultiServ
|
|
|
|
{ msPort :: Maybe Word16
|
|
|
|
, msShip :: TVar (Map Ship ShipAPI)
|
|
|
|
, msBoot :: TMVar (Ship, ShipAPI)
|
|
|
|
, msDead :: TMVar Ship
|
|
|
|
, msKill :: STM ()
|
|
|
|
}
|
|
|
|
|
|
|
|
data Hap = Deþ Ship
|
|
|
|
| Lif (Ship, ShipAPI)
|
|
|
|
| Res ShareResp
|
|
|
|
| Kil ()
|
|
|
|
|
2020-05-05 20:29:19 +03:00
|
|
|
multiServ :: HasLogFunc e => MultiServ -> RIO e ()
|
2020-05-05 19:30:37 +03:00
|
|
|
multiServ ms = do
|
|
|
|
case msPort ms of
|
|
|
|
Nothing -> doNothing ms
|
|
|
|
Just po -> doSomething ms po
|
|
|
|
|
|
|
|
{-
|
|
|
|
If the port is set, we do things for real. We run an HTTP server,
|
|
|
|
sends requests to the appropriate ship, respond to requests when
|
|
|
|
responses are given, and shuts down when the king shuts down.
|
|
|
|
-}
|
2020-05-05 20:29:19 +03:00
|
|
|
doSomething :: HasLogFunc e => MultiServ -> Word16 -> RIO e ()
|
2020-05-05 19:30:37 +03:00
|
|
|
doSomething MultiServ{..} httpPort = do
|
2020-05-05 20:29:19 +03:00
|
|
|
logDebug "Starting HTTP server"
|
|
|
|
|
|
|
|
let httpOpts = W.defaultSettings & W.setHost "*"
|
|
|
|
& W.setPort (fromIntegral httpPort)
|
|
|
|
|
|
|
|
sId <- io $ ServId . UV . fromIntegral <$> (randomIO :: IO Word32)
|
|
|
|
|
|
|
|
vShips :: TVar (Map Ship ShipAPI) <- newTVarIO mempty
|
|
|
|
liv <- newTVarIO emptyLiveReqs
|
|
|
|
|
|
|
|
env <- ask
|
|
|
|
|
|
|
|
plan <- error "TODO"
|
|
|
|
|
|
|
|
httpTid <- async $ io
|
|
|
|
$ W.runSettings httpOpts
|
2020-05-08 02:35:49 +03:00
|
|
|
$ eyreApp env sId liv plan Insecure
|
2020-05-05 20:29:19 +03:00
|
|
|
|
|
|
|
let onHapn :: STM Hap
|
|
|
|
onHapn = asum [ Lif <$> takeTMVar msBoot
|
|
|
|
, Deþ <$> takeTMVar msDead
|
|
|
|
, Res <$> (readTVar vShips >>= asum . fmap sapiRes . toList)
|
|
|
|
, Kil <$> msKill
|
|
|
|
]
|
|
|
|
|
|
|
|
let loop = join $ atomically $ onHapn >>= \case
|
|
|
|
Deþ s -> modifyTVar' vShips (deleteMap s) >> pure loop
|
|
|
|
Lif (s,api) -> modifyTVar' vShips (insertMap s api) >> pure loop
|
|
|
|
Res _ -> error "TODO"
|
|
|
|
Kil _ -> pure (cancel httpTid)
|
|
|
|
|
|
|
|
loop
|
2020-05-05 19:30:37 +03:00
|
|
|
|
|
|
|
{-
|
|
|
|
If the port is not set, we still run a thread for the shared server. It
|
|
|
|
doesn't run an HTTP server, it ignores all responses, and it shuts
|
|
|
|
down when the king shuts down.
|
|
|
|
-}
|
2020-05-05 20:29:19 +03:00
|
|
|
doNothing :: MultiServ -> RIO e ()
|
2020-05-05 19:30:37 +03:00
|
|
|
doNothing MultiServ{..} = do
|
|
|
|
vShips :: TVar (Map Ship ShipAPI) <- newTVarIO mempty
|
|
|
|
|
|
|
|
let onHapn :: STM Hap
|
|
|
|
onHapn = asum [ Lif <$> takeTMVar msBoot
|
|
|
|
, Deþ <$> takeTMVar msDead
|
|
|
|
, Res <$> (readTVar vShips >>= asum . fmap sapiRes . toList)
|
|
|
|
, Kil <$> msKill
|
|
|
|
]
|
|
|
|
|
|
|
|
let loop = join $ atomically $ onHapn >>= \case
|
|
|
|
Deþ s -> modifyTVar' vShips (deleteMap s) >> pure loop
|
|
|
|
Lif (s,api) -> modifyTVar' vShips (insertMap s api) >> pure loop
|
|
|
|
Res _ -> pure loop
|
|
|
|
Kil _ -> pure (pure ())
|
|
|
|
|
|
|
|
loop
|