Files
OpenFlare/openflare_relay/internal/relay/runner.go
T
ryan 4566fc1f53 [优化] 增强 WebSocket 处理逻辑,添加上下文取消支持和关闭机制
[优化] 重构 WebSocket 处理逻辑,添加消息处理接口和心跳机制
2026-06-02 19:37:03 +08:00

91 lines
2.0 KiB
Go

package relay
import (
"context"
"encoding/json"
"log/slog"
"time"
"openflare-relay/internal/config"
"openflare-relay/internal/frps"
"openflare-relay/internal/heartbeat"
"openflare-relay/internal/httpclient"
"openflare-relay/internal/state"
"openflare-relay/internal/wsclient"
"openflare/service"
)
type Runner struct {
Config *config.Config
StateStore *state.Store
HeartbeatService *heartbeat.Service
FrpsManager *frps.Manager
WebSocketService *wsclient.Client
HttpClient *httpclient.Client
}
func (r *Runner) Run(ctx context.Context) error {
// Start heartbeat loop in background
go r.HeartbeatService.Run(ctx)
// WebSocket reconnection loop
for {
select {
case <-ctx.Done():
r.FrpsManager.Stop()
return ctx.Err()
default:
}
conn, err := r.WebSocketService.Connect(ctx)
if err != nil {
slog.Error("relay ws connect failed, will retry", "error", err)
r.sleepContext(ctx, 5*time.Second)
continue
}
r.handleConnection(ctx, conn)
_ = conn.Close()
slog.Info("relay ws connection closed, reconnecting...")
r.sleepContext(ctx, 2*time.Second)
}
}
type relayWSHandler struct {
runner *Runner
}
func (h *relayWSHandler) OnConnect(ctx context.Context) error {
return nil
}
func (h *relayWSHandler) HandleMessage(ctx context.Context, msg wsclient.WSMessage) error {
switch msg.Type {
case "relay_config":
var cfg service.RelayConfig
if err := json.Unmarshal(msg.Payload, &cfg); err != nil {
slog.Error("failed to unmarshal relay_config", "error", err)
return nil
}
h.runner.FrpsManager.UpdateConfig(&cfg)
default:
slog.Debug("ignored unknown ws message type", "type", msg.Type)
}
return nil
}
func (h *relayWSHandler) OnClose(err error) {
slog.Error("relay ws receive failed", "error", err)
}
func (r *Runner) handleConnection(ctx context.Context, conn *wsclient.Connection) {
_ = conn.RunReceiveLoop(ctx, &relayWSHandler{runner: r})
}
func (r *Runner) sleepContext(ctx context.Context, d time.Duration) {
select {
case <-ctx.Done():
case <-time.After(d):
}
}