WS: `send_message` function. Will soon serve for sending keepalive messages.
parent
bcc76c8378
commit
b6b6a6be77
|
@ -7,8 +7,6 @@
|
||||||
-- | `App.Container` stores the state of different components (domain list and zone interface)
|
-- | `App.Container` stores the state of different components (domain list and zone interface)
|
||||||
-- | to avoid useless requests to `dnsmanagerd`.
|
-- | to avoid useless requests to `dnsmanagerd`.
|
||||||
-- |
|
-- |
|
||||||
-- | TODO: store forms in session storage?
|
|
||||||
-- |
|
|
||||||
-- | `App.Container` detects when a page has been reloaded and:
|
-- | `App.Container` detects when a page has been reloaded and:
|
||||||
-- | 1. authenticate the user to `dnsmanagerd` via a stored token in session storage.
|
-- | 1. authenticate the user to `dnsmanagerd` via a stored token in session storage.
|
||||||
-- | The authentication to `dnsmanagerd` automatically provides own domains and accepted domains (such as `netlib.re`).
|
-- | The authentication to `dnsmanagerd` automatically provides own domains and accepted domains (such as `netlib.re`).
|
||||||
|
@ -37,12 +35,10 @@
|
||||||
-- | - mail verification: TODO
|
-- | - mail verification: TODO
|
||||||
-- | - domain list: domain (`label`) is insufficient.
|
-- | - domain list: domain (`label`) is insufficient.
|
||||||
-- |
|
-- |
|
||||||
-- | TODO: when reading a RR `name`, always make it an FQDN by adding `<user-domain>.netlib.re.".
|
-- | TODO: when reading a RR `name`, always make it an FQDN by adding `<user-domain>.netlib.re.`.
|
||||||
-- |
|
-- |
|
||||||
-- | TODO: remove the FQDN when showing RR names.
|
-- | TODO: remove the FQDN when showing RR names.
|
||||||
-- |
|
-- |
|
||||||
-- | TODO: authd administrative page
|
|
||||||
-- |
|
|
||||||
-- | TODO: application-level heartbeat to avoid disconnections.
|
-- | TODO: application-level heartbeat to avoid disconnections.
|
||||||
-- |
|
-- |
|
||||||
-- | Untested features:
|
-- | Untested features:
|
||||||
|
|
|
@ -2,8 +2,10 @@
|
||||||
-- | This includes telling when a connection is established or closed, and notify a message has been received.
|
-- | This includes telling when a connection is established or closed, and notify a message has been received.
|
||||||
module App.WS where
|
module App.WS where
|
||||||
|
|
||||||
import Prelude (Unit, bind, discard, pure, show, void, when, ($), (&&), (<$>), (<>), (>>=), (>=>), (<<<), map)
|
import Prelude (Unit, bind, discard, pure, show, void, when
|
||||||
|
, ($), (&&), (<$>), (<>), (>>=), (>=>), (<<<), map, (=<<), (+))
|
||||||
|
|
||||||
|
import Control.Monad.Rec.Class (forever)
|
||||||
import Control.Monad.Except (runExcept)
|
import Control.Monad.Except (runExcept)
|
||||||
import Data.Array as A
|
import Data.Array as A
|
||||||
import Data.ArrayBuffer.Types (ArrayBuffer)
|
import Data.ArrayBuffer.Types (ArrayBuffer)
|
||||||
|
@ -12,6 +14,8 @@ import Data.Either (Either(..))
|
||||||
import Data.Maybe (Maybe(..), isJust, isNothing)
|
import Data.Maybe (Maybe(..), isJust, isNothing)
|
||||||
import Data.String as String
|
import Data.String as String
|
||||||
import Data.Tuple (Tuple(..))
|
import Data.Tuple (Tuple(..))
|
||||||
|
import Effect.Aff as Aff
|
||||||
|
import Effect.Aff (Milliseconds(..))
|
||||||
import Effect.Aff.Class (class MonadAff)
|
import Effect.Aff.Class (class MonadAff)
|
||||||
import Effect (Effect)
|
import Effect (Effect)
|
||||||
import Foreign as F
|
import Foreign as F
|
||||||
|
@ -30,6 +34,8 @@ import Web.Socket.WebSocket as WS
|
||||||
|
|
||||||
import App.LogMessage
|
import App.LogMessage
|
||||||
|
|
||||||
|
keepalive = 30000.0 :: Number
|
||||||
|
|
||||||
-- Input is the WS url.
|
-- Input is the WS url.
|
||||||
type Input = String
|
type Input = String
|
||||||
|
|
||||||
|
@ -47,6 +53,15 @@ data Query a = ToSend ArrayBuffer a
|
||||||
|
|
||||||
type Slot = H.Slot Query Output
|
type Slot = H.Slot Query Output
|
||||||
|
|
||||||
|
-- | `timer` triggers a `Tick` action every `keepalive` seconds.
|
||||||
|
timer :: forall m a. MonadAff m => a -> m (HS.Emitter a)
|
||||||
|
timer val = do
|
||||||
|
{ emitter, listener } <- H.liftEffect HS.create
|
||||||
|
_ <- H.liftAff $ Aff.forkAff $ forever do
|
||||||
|
Aff.delay $ Milliseconds keepalive
|
||||||
|
H.liftEffect $ HS.notify listener val
|
||||||
|
pure emitter
|
||||||
|
|
||||||
data Action
|
data Action
|
||||||
-- | `Initialize` opens the connection (URL is received as an `input` of this component).
|
-- | `Initialize` opens the connection (URL is received as an `input` of this component).
|
||||||
= Initialize
|
= Initialize
|
||||||
|
@ -64,6 +79,8 @@ data Action
|
||||||
-- | `Finalize` is the action performed once the component is destroyed, ending the connection.
|
-- | `Finalize` is the action performed once the component is destroyed, ending the connection.
|
||||||
| Finalize
|
| Finalize
|
||||||
|
|
||||||
|
| Tick
|
||||||
|
|
||||||
-- | Every received websocket message and notification is handled in `HandleWebSocket`.
|
-- | Every received websocket message and notification is handled in `HandleWebSocket`.
|
||||||
| HandleWebSocket (WebSocketEvent WebSocketMessageType)
|
| HandleWebSocket (WebSocketEvent WebSocketMessageType)
|
||||||
|
|
||||||
|
@ -77,7 +94,7 @@ type WSInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
-- | The state of this component only is composed of the websocket.
|
-- | The state of this component only is composed of the websocket.
|
||||||
type State = { wsInfo :: WSInfo }
|
type State = { wsInfo :: WSInfo, seconds :: Number }
|
||||||
|
|
||||||
component :: forall m. MonadAff m => H.Component Query Input Output m
|
component :: forall m. MonadAff m => H.Component Query Input Output m
|
||||||
component =
|
component =
|
||||||
|
@ -98,13 +115,15 @@ initialState url =
|
||||||
, connection: Nothing
|
, connection: Nothing
|
||||||
, reconnect: false
|
, reconnect: false
|
||||||
}
|
}
|
||||||
|
, seconds: 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
-- | The component shows a string when the connection is established, or a button when the connection has closed.
|
-- | The component shows a string when the connection is established, or a button when the connection has closed.
|
||||||
render :: forall m. State -> H.ComponentHTML Action () m
|
render :: forall m. State -> H.ComponentHTML Action () m
|
||||||
render { wsInfo }
|
render { wsInfo, seconds }
|
||||||
= HH.div_
|
= HH.div_
|
||||||
[ renderReconnectButton (isNothing wsInfo.connection && wsInfo.reconnect)
|
[ renderReconnectButton (isNothing wsInfo.connection && wsInfo.reconnect)
|
||||||
|
, HH.text ("You have been here for " <> show seconds <> " seconds")
|
||||||
]
|
]
|
||||||
where
|
where
|
||||||
|
|
||||||
|
@ -137,9 +156,16 @@ handleAction :: forall m. MonadAff m => Action -> H.HalogenM State Action () Out
|
||||||
handleAction action = do
|
handleAction action = do
|
||||||
{ wsInfo } <- H.get
|
{ wsInfo } <- H.get
|
||||||
case action of
|
case action of
|
||||||
Initialize ->
|
Initialize -> do
|
||||||
|
_ <- H.subscribe =<< timer Tick
|
||||||
handleAction ConnectWebSocket
|
handleAction ConnectWebSocket
|
||||||
|
|
||||||
|
Tick -> do
|
||||||
|
H.modify_ \state -> state { seconds = state.seconds + keepalive }
|
||||||
|
-- TODO: create a message only for applicative keepalive.
|
||||||
|
-- message
|
||||||
|
-- send_message message
|
||||||
|
|
||||||
Finalize -> do
|
Finalize -> do
|
||||||
-- H.raise $ Log $ SystemLog $ "Closing websocket for '" <> wsInfo.url <> "'"
|
-- H.raise $ Log $ SystemLog $ "Closing websocket for '" <> wsInfo.url <> "'"
|
||||||
case wsInfo.connection of
|
case wsInfo.connection of
|
||||||
|
@ -200,36 +226,21 @@ handleAction action = do
|
||||||
|
|
||||||
handleQuery :: forall a m. MonadAff m => Query a -> H.HalogenM State Action () Output m (Maybe a)
|
handleQuery :: forall a m. MonadAff m => Query a -> H.HalogenM State Action () Output m (Maybe a)
|
||||||
handleQuery = case _ of
|
handleQuery = case _ of
|
||||||
ToSend message a -> do
|
ToSend message a -> send_message message
|
||||||
|
|
||||||
|
send_message :: forall m. MonadAff m => ArrayBuffer -> H.HalogenM State Action () Output m Unit
|
||||||
|
send_message message =
|
||||||
{ wsInfo } <- H.get
|
{ wsInfo } <- H.get
|
||||||
case wsInfo.connection of
|
case wsInfo.connection of
|
||||||
Nothing -> do
|
Nothing -> H.raise $ Log $ UnableToSend "Not connected to server."
|
||||||
H.raise $ Log $ UnableToSend "Not connected to server."
|
|
||||||
pure Nothing
|
|
||||||
|
|
||||||
Just webSocket -> do
|
Just webSocket -> do
|
||||||
H.liftEffect (WS.readyState webSocket) >>= case _ of
|
H.liftEffect (WS.readyState webSocket) >>= case _ of
|
||||||
Connecting -> do
|
Connecting -> H.raise $ Log $ UnableToSend "Still connecting to server."
|
||||||
H.raise $ Log $ UnableToSend "Still connecting to server."
|
Closing -> H.raise $ Log $ UnableToSend "Connection to server is closing."
|
||||||
pure Nothing
|
Closed -> do
|
||||||
|
|
||||||
Closing -> do
|
|
||||||
H.raise $ Log $ UnableToSend "Connection to server is closing."
|
|
||||||
pure Nothing
|
|
||||||
|
|
||||||
Closed -> do
|
|
||||||
H.raise $ Log $ UnableToSend "Connection to server has been closed."
|
H.raise $ Log $ UnableToSend "Connection to server has been closed."
|
||||||
maybeCurrentConnection <- H.gets _.wsInfo.connection
|
H.modify_ _ { wsInfo { connection = Nothing, reconnect = true } }
|
||||||
when (isJust maybeCurrentConnection) do
|
Open -> H.liftEffect $ sendArrayBuffer webSocket message
|
||||||
H.modify_ _ { wsInfo { connection = Nothing, reconnect = true } }
|
|
||||||
pure Nothing
|
|
||||||
|
|
||||||
Open -> do
|
|
||||||
H.liftEffect $ do
|
|
||||||
sendArrayBuffer webSocket message
|
|
||||||
pure (Just a)
|
|
||||||
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- WebSocket mess.
|
-- WebSocket mess.
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in New Issue