Skip to content

Commit

Permalink
feat: Make reconnect timeout configurable and add some extra logging.
Browse files Browse the repository at this point in the history
  • Loading branch information
mturoci committed Feb 7, 2024
1 parent 4e43e86 commit 5cb3e7e
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 24 deletions.
41 changes: 22 additions & 19 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,26 +54,29 @@ type BootMsg struct {

// Client represent a websocket (UI) client.
type Client struct {
id string // unique id
auth *Auth // auth provider, might be nil
addr string // remote IP:port, used for logging only
session *Session // end-user session
broker *Broker // broker
conn *websocket.Conn // connection
routes []string // watched routes
data chan []byte // send data
editable bool // allow editing? // TODO move to user; tie to role
baseURL string // URL prefix of the Wave server
header *http.Header // forwarded headers from the WS connection
appPath string // path of the app this client is connected to, doesn't change throughout WS lifetime
pingInterval time.Duration
isReconnect bool
cancel context.CancelFunc
id string // unique id
auth *Auth // auth provider, might be nil
addr string // remote IP:port, used for logging only
session *Session // end-user session
broker *Broker // broker
conn *websocket.Conn // connection
routes []string // watched routes
data chan []byte // send data
editable bool // allow editing? // TODO move to user; tie to role
baseURL string // URL prefix of the Wave server
header *http.Header // forwarded headers from the WS connection
appPath string // path of the app this client is connected to, doesn't change throughout WS lifetime
pingInterval time.Duration
isReconnect bool
cancel context.CancelFunc
reconnectTimeout time.Duration
}

func newClient(addr string, auth *Auth, session *Session, broker *Broker, conn *websocket.Conn, editable bool, baseURL string, header *http.Header, pingInterval time.Duration, isReconnect bool) *Client {
// TODO: Refactor some of the params into a Config struct.
func newClient(addr string, auth *Auth, session *Session, broker *Broker, conn *websocket.Conn, editable bool,
baseURL string, header *http.Header, pingInterval time.Duration, isReconnect bool, reconnectTimeout time.Duration) *Client {
id := uuid.New().String()
return &Client{id, auth, addr, session, broker, conn, nil, make(chan []byte, 256), editable, baseURL, header, "", pingInterval, isReconnect, nil}
return &Client{id, auth, addr, session, broker, conn, nil, make(chan []byte, 256), editable, baseURL, header, "", pingInterval, isReconnect, nil, reconnectTimeout}
}

func (c *Client) refreshToken() error {
Expand All @@ -97,8 +100,8 @@ func (c *Client) listen() {
c.cancel = cancel
go func(ctx context.Context) {
select {
// Send disconnect message only if client doesn't reconnect within 2s.
case <-time.After(2 * time.Second):
// Send disconnect message only if client doesn't reconnect within the specified timeframe.
case <-time.After(c.reconnectTimeout):
app := c.broker.getApp(c.appPath)
if app != nil {
app.forward(c.id, c.session, disconnectMsg)
Expand Down
7 changes: 7 additions & 0 deletions cmd/wave/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ func main() {
panic(err)
}

// TODO: Handle this at the config parser level.
if authConf.SessionExpiry, err = time.ParseDuration(conf.SessionExpiry); err != nil {
panic(err)
}
Expand All @@ -189,6 +190,10 @@ func main() {
panic(err)
}

if serverConf.ReconnectTimeout, err = time.ParseDuration(conf.ReconnectTimeout); err != nil {
panic(err)
}

serverConf.WebDir, _ = filepath.Abs(conf.WebDir)
serverConf.DataDir, _ = filepath.Abs(conf.DataDir)
serverConf.Version = Version
Expand Down Expand Up @@ -227,6 +232,8 @@ func main() {
authConf.URLParameters = append(authConf.URLParameters, kv)
}
}

// TODO: Handle this at the config parser level.
if authConf.SessionExpiry, err = time.ParseDuration(conf.SessionExpiry); err != nil {
panic(err)
}
Expand Down
2 changes: 2 additions & 0 deletions conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type ServerConf struct {
ForwardedHeaders map[string]bool
KeepAppLive bool
PingInterval time.Duration
ReconnectTimeout time.Duration
}

type AuthConf struct {
Expand Down Expand Up @@ -112,4 +113,5 @@ type Conf struct {
SkipLogin bool `cfg:"oidc-skip-login" env:"H2O_WAVE_OIDC_SKIP_LOGIN" cfgDefault:"false" cfgHelper:"do not display the login form during OIDC authorization"`
KeepAppLive bool `cfg:"keep-app-live" env:"H2O_WAVE_KEEP_APP_LIVE" cfgDefault:"false" cfgHelper:"do not unregister unresponsive apps"`
Conf string `cfg:"conf" env:"H2O_WAVE_CONF" cfgDefault:".env" cfgHelper:"path to configuration file"`
ReconnectTimeout string `cfg:"reconnect-timeout" env:"H2O_WAVE_RECONNECT_TIMEOUT" cfgDefault:"2s" cfgHelper:"Time to wait for reconnect before dropping the client"`
}
2 changes: 1 addition & 1 deletion server.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func Run(conf ServerConf) {
handle("_auth/refresh", newRefreshHandler(auth, conf.Keychain))
}

handle("_s/", newSocketServer(broker, auth, conf.Editable, conf.BaseURL, conf.ForwardedHeaders, conf.PingInterval)) // XXX terminate sockets when logged out
handle("_s/", newSocketServer(broker, auth, conf.Editable, conf.BaseURL, conf.ForwardedHeaders, conf.PingInterval, conf.ReconnectTimeout))

fileDir := filepath.Join(conf.DataDir, "f")
handle("_f/", newFileServer(fileDir, conf.Keychain, auth, conf.BaseURL+"_f"))
Expand Down
10 changes: 7 additions & 3 deletions socket.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ type SocketServer struct {
baseURL string
forwardedHeaders map[string]bool
pingInterval time.Duration
reconnectTimeout time.Duration
}

func newSocketServer(broker *Broker, auth *Auth, editable bool, baseURL string, forwardedHeaders map[string]bool, pingInterval time.Duration) *SocketServer {
return &SocketServer{broker, auth, editable, baseURL, forwardedHeaders, pingInterval}
func newSocketServer(broker *Broker, auth *Auth, editable bool, baseURL string, forwardedHeaders map[string]bool, pingInterval, reconnectTimeout time.Duration) *SocketServer {
return &SocketServer{broker, auth, editable, baseURL, forwardedHeaders, pingInterval, reconnectTimeout}
}

func (s *SocketServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -78,8 +79,11 @@ func (s *SocketServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if client.cancel != nil {
client.cancel()
}
if s.broker.debug {
echo(Log{"t": "socket_reconnect", "client_id": clientID, "addr": getRemoteAddr(r)})
}
} else {
client = newClient(getRemoteAddr(r), s.auth, session, s.broker, conn, s.editable, s.baseURL, &header, s.pingInterval, false)
client = newClient(getRemoteAddr(r), s.auth, session, s.broker, conn, s.editable, s.baseURL, &header, s.pingInterval, false, s.reconnectTimeout)
}

if msg, err := json.Marshal(OpsD{I: client.id}); err == nil {
Expand Down
3 changes: 2 additions & 1 deletion website/docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Wave allows starting Wave server in 2 ways:
Wave can be configured via configuration (`.env`) file, environment variables or command line arguments with the following priority: `cmd arg > env var > config > default`.

<!-- CREDIT: https://www.tablesgenerator.com/markdown_tables. -->

<!-- https://github.com/h2oai/wave/issues/2256 -->
| ENV var or config (wave run or waved) | CLI args (waved) | Description |
|----------------------------------------|---------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| H2O_WAVE_ACCESS_KEY_ID | -access-key-id string | default API access key ID (default "access_key_id") |
Expand Down Expand Up @@ -58,6 +58,7 @@ Wave can be configured via configuration (`.env`) file, environment variables or
| H2O_WAVE_WEB_DIR | -web-dir string | directory to serve web assets from (default "./www") |
| H2O_WAVE_CONF | -conf string | path to a configuration file (default ".env") |
| H2O_WAVE_PING_INTERVAL | -ping-interval string | how often should ping messages be sent (e.g. 60s or 1m or 0.1h) to keep the websocket connection alive (default "50s") |
| H2O_WAVE_RECONNECT_TIMEOUT | -reconnect-timeout string | Time to wait for reconnect before dropping the client (default "2s") |
[^1]: `1`, `t`, `true` to enable; `0`, `f`, `false` to disable (case insensitive).
[^2]: Use OS-specific path list separator to specify multiple arguments - `:` for Linux/OSX and `;` for Windows. For example, `H2O_WAVE_PUBLIC_DIR=/images/@./files/images:/downloads/@./files/downloads`.

Expand Down

0 comments on commit 5cb3e7e

Please sign in to comment.