From 652e845d8317da0371c0efbfccd450118497946b Mon Sep 17 00:00:00 2001 From: Siyuan Miao Date: Wed, 9 Apr 2025 20:25:26 +0200 Subject: [PATCH 01/12] fix(ota): certificate signed by unknown authority --- ota.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ota.go b/ota.go index 9c583b6..b28abbb 100644 --- a/ota.go +++ b/ota.go @@ -126,12 +126,10 @@ func downloadFile(ctx context.Context, path string, url string, downloadProgress return fmt.Errorf("error creating request: %w", err) } + // TODO: set a separate timeout for the download but keep the TLS handshake short + // use Transport here will cause CA certificate validation failure so we temporarily removed it client := http.Client{ - // allow a longer timeout for the download but keep the TLS handshake short Timeout: 10 * time.Minute, - Transport: &http.Transport{ - TLSHandshakeTimeout: 1 * time.Minute, - }, } resp, err := client.Do(req) From 1656420b3b6407dba08326afd43e7b571ef75969 Mon Sep 17 00:00:00 2001 From: Adam Shiervani Date: Wed, 9 Apr 2025 22:23:05 +0200 Subject: [PATCH 02/12] re-add old signaling for when upgrading --- web.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/web.go b/web.go index c3f6d8d..3ab7780 100644 --- a/web.go +++ b/web.go @@ -99,6 +99,23 @@ func setupRouter() *gin.Engine { protected := r.Group("/") protected.Use(protectedMiddleware()) { + + /* + * Legacy WebRTC session endpoint + * + * This endpoint is maintained for backward compatibility when users upgrade from a version + * using the legacy HTTP-based signaling method to the new WebSocket-based signaling method. + * + * During the upgrade process, when the "Rebooting device after update..." message appears, + * the browser still runs the previous JavaScript code which polls this endpoint to establish + * a new WebRTC session. Once the session is established, the page will automatically reload + * with the updated code. + * + * Without this endpoint, the stale JavaScript would fail to establish a connection, + * causing users to see the "Rebooting device after update..." message indefinitely + * until they manually refresh the page, leading to a confusing user experience. + */ + protected.POST("/webrtc/session", handleWebRTCSession) protected.GET("/webrtc/signaling/client", handleLocalWebRTCSignal) protected.POST("/cloud/register", handleCloudRegister) protected.GET("/cloud/state", handleCloudState) @@ -126,6 +143,37 @@ func setupRouter() *gin.Engine { // TODO: support multiple sessions? var currentSession *Session +func handleWebRTCSession(c *gin.Context) { + var req WebRTCSessionRequest + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + session, err := newSession(SessionConfig{}) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err}) + return + } + + sd, err := session.ExchangeOffer(req.Sd) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err}) + return + } + if currentSession != nil { + writeJSONRPCEvent("otherSessionConnected", nil, currentSession) + peerConn := currentSession.peerConnection + go func() { + time.Sleep(1 * time.Second) + _ = peerConn.Close() + }() + } + currentSession = session + c.JSON(http.StatusOK, gin.H{"sd": sd}) +} + func handleLocalWebRTCSignal(c *gin.Context) { cloudLogger.Infof("new websocket connection established") // Create WebSocket options with InsecureSkipVerify to bypass origin check From 0b6be9b644a8630112c4659eff9b4848c70ecbac Mon Sep 17 00:00:00 2001 From: Adam Shiervani Date: Wed, 9 Apr 2025 22:25:47 +0200 Subject: [PATCH 03/12] refactor: remove unnecessary whitespace in setupRouter function --- web.go | 1 - 1 file changed, 1 deletion(-) diff --git a/web.go b/web.go index 3ab7780..51bcd98 100644 --- a/web.go +++ b/web.go @@ -99,7 +99,6 @@ func setupRouter() *gin.Engine { protected := r.Group("/") protected.Use(protectedMiddleware()) { - /* * Legacy WebRTC session endpoint * From ee29cb11bdb6811bde012f2a4ab9cee11dc8fe60 Mon Sep 17 00:00:00 2001 From: Adam Shiervani Date: Wed, 9 Apr 2025 23:26:02 +0200 Subject: [PATCH 04/12] Don't block new PC if connection is stable. No need to (#340) --- ui/src/routes/devices.$id.tsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/ui/src/routes/devices.$id.tsx b/ui/src/routes/devices.$id.tsx index fef1764..1b66bf5 100644 --- a/ui/src/routes/devices.$id.tsx +++ b/ui/src/routes/devices.$id.tsx @@ -243,7 +243,7 @@ export default function KvmIdRoute() { { heartbeat: true, retryOnError: true, - reconnectAttempts: 5, + reconnectAttempts: 15, reconnectInterval: 1000, onReconnectStop: () => { console.log("Reconnect stopped"); @@ -398,11 +398,6 @@ export default function KvmIdRoute() { setConnectionFailed(false); setLoadingMessage("Connecting to device..."); - if (peerConnection?.signalingState === "stable") { - console.log("[setupPeerConnection] Peer connection already established"); - return; - } - let pc: RTCPeerConnection; try { console.log("[setupPeerConnection] Creating peer connection"); @@ -499,7 +494,6 @@ export default function KvmIdRoute() { cleanupAndStopReconnecting, iceConfig?.iceServers, legacyHTTPSignaling, - peerConnection?.signalingState, sendWebRTCSignal, setDiskChannel, setMediaMediaStream, From d824a1bc8665fa6cb2cf703a174c3cdbf5411a20 Mon Sep 17 00:00:00 2001 From: Adam Shiervani Date: Thu, 10 Apr 2025 00:05:41 +0200 Subject: [PATCH 05/12] fix(dev_device): update JETKVM_PROXY_URL to use WebSocket protocol (#342) --- ui/dev_device.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/dev_device.sh b/ui/dev_device.sh index 092b8c8..6b3a649 100755 --- a/ui/dev_device.sh +++ b/ui/dev_device.sh @@ -16,4 +16,4 @@ echo "└─────────────────────── # Set the environment variable and run Vite echo "Starting development server with JetKVM device at: $ip_address" sleep 1 -JETKVM_PROXY_URL="http://$ip_address" npx vite dev --mode=device +JETKVM_PROXY_URL="ws://$ip_address" npx vite dev --mode=device From 02bf869b9916fcc41fa89d2f500538e178251351 Mon Sep 17 00:00:00 2001 From: Adam Shiervani Date: Thu, 10 Apr 2025 00:05:51 +0200 Subject: [PATCH 06/12] fix(ui): increase z-index for Modal component to improve layering (#341) --- ui/src/components/Modal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/components/Modal.tsx b/ui/src/components/Modal.tsx index 039b493..e395445 100644 --- a/ui/src/components/Modal.tsx +++ b/ui/src/components/Modal.tsx @@ -15,12 +15,12 @@ const Modal = React.memo(function Modal({ onClose: () => void; }) { return ( - + -
+
{/* TODO: This doesn't work well with other-sessions */}
Date: Wed, 9 Apr 2025 23:30:33 +0100 Subject: [PATCH 07/12] fix: Shell linting (#328) Cleanup various shell linting issues * Use `/usr/bin/env` consistently for better platform compatibility. * SC2317 (info): Command appears to be unreachable. * SC2002 (style): Useless cat. Signed-off-by: SuperQ --- dev_deploy.sh | 9 +++++---- publish_source.sh | 4 ++-- ui/dev_device.sh | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/dev_deploy.sh b/dev_deploy.sh index 7fbf29e..3f2ebb6 100755 --- a/dev_deploy.sh +++ b/dev_deploy.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash +# # Exit immediately if a command exits with a non-zero status set -e @@ -16,7 +18,6 @@ show_help() { echo "Example:" echo " $0 -r 192.168.0.17" echo " $0 -r 192.168.0.17 -u admin" - exit 0 } # Default values @@ -70,10 +71,10 @@ cd bin ssh "${REMOTE_USER}@${REMOTE_HOST}" "killall jetkvm_app_debug || true" # Copy the binary to the remote host -cat jetkvm_app | ssh "${REMOTE_USER}@${REMOTE_HOST}" "cat > $REMOTE_PATH/jetkvm_app_debug" +ssh "${REMOTE_USER}@${REMOTE_HOST}" "cat > ${REMOTE_PATH}/jetkvm_app_debug" < jetkvm_app # Deploy and run the application on the remote host -ssh "${REMOTE_USER}@${REMOTE_HOST}" ash < Date: Thu, 10 Apr 2025 11:55:28 +0200 Subject: [PATCH 08/12] fix(ui): update WebRTCVideo component to properly animate on peer connection state (#343) --- ui/src/components/WebRTCVideo.tsx | 6 +++++- ui/src/routes/devices.$id.tsx | 12 +++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/ui/src/components/WebRTCVideo.tsx b/ui/src/components/WebRTCVideo.tsx index 99c0191..970867a 100644 --- a/ui/src/components/WebRTCVideo.tsx +++ b/ui/src/components/WebRTCVideo.tsx @@ -28,6 +28,7 @@ export default function WebRTCVideo() { const videoElm = useRef(null); const mediaStream = useRTCStore(state => state.mediaStream); const [isPlaying, setIsPlaying] = useState(false); + const peerConnectionState = useRTCStore(state => state.peerConnectionState); // Store hooks const settings = useSettingsStore(); @@ -601,7 +602,10 @@ export default function WebRTCVideo() { "cursor-none": settings.mouseMode === "absolute" && settings.isCursorHidden, - "opacity-0": isVideoLoading || hdmiError, + "opacity-0": + isVideoLoading || + hdmiError || + peerConnectionState !== "connected", "animate-slideUpFade border border-slate-800/30 opacity-0 shadow dark:border-slate-300/20": isPlaying, }, diff --git a/ui/src/routes/devices.$id.tsx b/ui/src/routes/devices.$id.tsx index 1b66bf5..70132f4 100644 --- a/ui/src/routes/devices.$id.tsx +++ b/ui/src/routes/devices.$id.tsx @@ -795,14 +795,16 @@ export default function KvmIdRoute() { kvmName={deviceName || "JetKVM Device"} /> -
-
-
+
+ +
+
{!!ConnectionStatusElement && ConnectionStatusElement}
- - {peerConnectionState === "connected" && }
From 929e3a66d74203bb7e5a12f310c8a5e4b7d76ce9 Mon Sep 17 00:00:00 2001 From: Adam Shiervani Date: Thu, 10 Apr 2025 12:39:36 +0200 Subject: [PATCH 09/12] fix(ui): adjust layout and z-index for improved UI consistency in KvmIdRoute (#345) --- ui/src/routes/devices.$id.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ui/src/routes/devices.$id.tsx b/ui/src/routes/devices.$id.tsx index 70132f4..82bb542 100644 --- a/ui/src/routes/devices.$id.tsx +++ b/ui/src/routes/devices.$id.tsx @@ -785,6 +785,7 @@ export default function KvmIdRoute() {
+
-
+
e.stopPropagation()} onKeyDown={e => { e.stopPropagation(); From 4137b82661438f4584682837d47a35c3d980c661 Mon Sep 17 00:00:00 2001 From: Aveline <352441+ym@users.noreply.github.com> Date: Thu, 10 Apr 2025 15:10:22 +0200 Subject: [PATCH 10/12] feat(websocket): handle ping messages sent from react and add logging (#346) --- cloud.go | 4 ++++ go.mod | 2 +- go.sum | 2 ++ web.go | 53 +++++++++++++++++++++++++++++++++++++++-------------- 4 files changed, 46 insertions(+), 15 deletions(-) diff --git a/cloud.go b/cloud.go index 7ad8b75..89666a1 100644 --- a/cloud.go +++ b/cloud.go @@ -275,6 +275,10 @@ func runWebsocketClient() error { defer cancelDial() c, _, err := websocket.Dial(dialCtx, wsURL.String(), &websocket.DialOptions{ HTTPHeader: header, + OnPingReceived: func(ctx context.Context, payload []byte) bool { + websocketLogger.Infof("ping frame received: %v, source: %s, sourceType: cloud", payload, wsURL.Host) + return true + }, }) if err != nil { return err diff --git a/go.mod b/go.mod index 93fedab..fae1cbd 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.21.1 require ( github.com/Masterminds/semver/v3 v3.3.0 github.com/beevik/ntp v1.3.1 - github.com/coder/websocket v1.8.12 + github.com/coder/websocket v1.8.13 github.com/coreos/go-oidc/v3 v3.11.0 github.com/creack/pty v1.1.23 github.com/gin-gonic/gin v1.9.1 diff --git a/go.sum b/go.sum index b5769d8..1563130 100644 --- a/go.sum +++ b/go.sum @@ -18,6 +18,8 @@ github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= +github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE= +github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI= github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0= diff --git a/web.go b/web.go index 51bcd98..0258dc6 100644 --- a/web.go +++ b/web.go @@ -1,6 +1,7 @@ package kvm import ( + "bytes" "context" "embed" "encoding/json" @@ -173,11 +174,24 @@ func handleWebRTCSession(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"sd": sd}) } +var ( + pingMessage = []byte("ping") + pongMessage = []byte("pong") +) + func handleLocalWebRTCSignal(c *gin.Context) { cloudLogger.Infof("new websocket connection established") + + // get the source from the request + source := c.ClientIP() + // Create WebSocket options with InsecureSkipVerify to bypass origin check wsOptions := &websocket.AcceptOptions{ InsecureSkipVerify: true, // Allow connections from any origin + OnPingReceived: func(ctx context.Context, payload []byte) bool { + websocketLogger.Infof("ping frame received: %v, source: %s, sourceType: local", payload, source) + return true + }, } wsCon, err := websocket.Accept(c.Writer, c.Request, wsOptions) @@ -186,9 +200,6 @@ func handleLocalWebRTCSignal(c *gin.Context) { return } - // get the source from the request - source := c.ClientIP() - // Now use conn for websocket operations defer wsCon.Close(websocket.StatusNormalClosure, "") @@ -211,7 +222,6 @@ func handleWebRTCSignalWsMessages(wsCon *websocket.Conn, isCloudConnection bool, // Add connection tracking to detect reconnections connectionID := uuid.New().String() - cloudLogger.Infof("new websocket connection established with ID: %s", connectionID) // connection type var sourceType string @@ -223,18 +233,20 @@ func handleWebRTCSignalWsMessages(wsCon *websocket.Conn, isCloudConnection bool, // probably we can use a better logging framework here logInfof := func(format string, args ...interface{}) { - args = append(args, source, sourceType) - websocketLogger.Infof(format+", source: %s, sourceType: %s", args...) + args = append(args, source, sourceType, connectionID) + websocketLogger.Infof(format+", source: %s, sourceType: %s, id: %s", args...) } logWarnf := func(format string, args ...interface{}) { - args = append(args, source, sourceType) - websocketLogger.Warnf(format+", source: %s, sourceType: %s", args...) + args = append(args, source, sourceType, connectionID) + websocketLogger.Warnf(format+", source: %s, sourceType: %s, id: %s", args...) } logTracef := func(format string, args ...interface{}) { - args = append(args, source, sourceType) - websocketLogger.Tracef(format+", source: %s, sourceType: %s", args...) + args = append(args, source, sourceType, connectionID) + websocketLogger.Tracef(format+", source: %s, sourceType: %s, id: %s", args...) } + logInfof("new websocket connection established") + go func() { for { time.Sleep(WebsocketPingInterval) @@ -245,7 +257,7 @@ func handleWebRTCSignalWsMessages(wsCon *websocket.Conn, isCloudConnection bool, metricConnectionPingDuration.WithLabelValues(sourceType, source).Observe(v) })) - logInfof("pinging websocket") + logTracef("sending ping frame") err := wsCon.Ping(runCtx) if err != nil { @@ -255,10 +267,12 @@ func handleWebRTCSignalWsMessages(wsCon *websocket.Conn, isCloudConnection bool, } // dont use `defer` here because we want to observe the duration of the ping - timer.ObserveDuration() + duration := timer.ObserveDuration() metricConnectionTotalPingCount.WithLabelValues(sourceType, source).Inc() metricConnectionLastPingTimestamp.WithLabelValues(sourceType, source).SetToCurrentTime() + + logTracef("received pong frame, duration: %v", duration) } }() @@ -296,6 +310,16 @@ func handleWebRTCSignalWsMessages(wsCon *websocket.Conn, isCloudConnection bool, Data json.RawMessage `json:"data"` } + if bytes.Equal(msg, pingMessage) { + logInfof("ping message received: %s", string(msg)) + err = wsCon.Write(context.Background(), websocket.MessageText, pongMessage) + if err != nil { + logWarnf("unable to write pong message: %v", err) + return err + } + continue + } + err = json.Unmarshal(msg, &message) if err != nil { logWarnf("unable to parse ws message: %v", err) @@ -311,8 +335,9 @@ func handleWebRTCSignalWsMessages(wsCon *websocket.Conn, isCloudConnection bool, continue } - logInfof("new session request: %v", req.OidcGoogle) - logTracef("session request info: %v", req) + if req.OidcGoogle != "" { + logInfof("new session request with OIDC Google: %v", req.OidcGoogle) + } metricConnectionSessionRequestCount.WithLabelValues(sourceType, source).Inc() metricConnectionLastSessionRequestTimestamp.WithLabelValues(sourceType, source).SetToCurrentTime() From 76a1825b0226fcdfa1e4d44b5f1b7f08a28391c7 Mon Sep 17 00:00:00 2001 From: Aveline <352441+ym@users.noreply.github.com> Date: Thu, 10 Apr 2025 15:53:26 +0200 Subject: [PATCH 11/12] chore(websocket): logging and metrics improvement (#347) * chore(websocket): only show warning if websocket is closed abnormally * chore(websocket): add counter for ping requests received --- cloud.go | 39 +++++++++++++++++++++++++++++++-------- web.go | 20 +++++++++++++++++++- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/cloud.go b/cloud.go index 89666a1..070db8d 100644 --- a/cloud.go +++ b/cloud.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/json" + "errors" "fmt" "net/http" "net/url" @@ -59,6 +60,13 @@ var ( }, []string{"type", "source"}, ) + metricConnectionLastPingReceivedTimestamp = promauto.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "jetkvm_connection_last_ping_received_timestamp", + Help: "The timestamp when the last ping request was received", + }, + []string{"type", "source"}, + ) metricConnectionLastPingDuration = promauto.NewGaugeVec( prometheus.GaugeOpts{ Name: "jetkvm_connection_last_ping_duration", @@ -76,16 +84,23 @@ var ( }, []string{"type", "source"}, ) - metricConnectionTotalPingCount = promauto.NewCounterVec( + metricConnectionTotalPingSentCount = promauto.NewCounterVec( prometheus.CounterOpts{ - Name: "jetkvm_connection_total_ping_count", + Name: "jetkvm_connection_total_ping_sent", Help: "The total number of pings sent to the connection", }, []string{"type", "source"}, ) + metricConnectionTotalPingReceivedCount = promauto.NewCounterVec( + prometheus.CounterOpts{ + Name: "jetkvm_connection_total_ping_received", + Help: "The total number of pings received from the connection", + }, + []string{"type", "source"}, + ) metricConnectionSessionRequestCount = promauto.NewCounterVec( prometheus.CounterOpts{ - Name: "jetkvm_connection_session_total_request_count", + Name: "jetkvm_connection_session_total_requests", Help: "The total number of session requests received", }, []string{"type", "source"}, @@ -131,6 +146,8 @@ func wsResetMetrics(established bool, sourceType string, source string) { metricConnectionLastPingTimestamp.WithLabelValues(sourceType, source).Set(-1) metricConnectionLastPingDuration.WithLabelValues(sourceType, source).Set(-1) + metricConnectionLastPingReceivedTimestamp.WithLabelValues(sourceType, source).Set(-1) + metricConnectionLastSessionRequestTimestamp.WithLabelValues(sourceType, source).Set(-1) metricConnectionLastSessionRequestDuration.WithLabelValues(sourceType, source).Set(-1) @@ -277,20 +294,29 @@ func runWebsocketClient() error { HTTPHeader: header, OnPingReceived: func(ctx context.Context, payload []byte) bool { websocketLogger.Infof("ping frame received: %v, source: %s, sourceType: cloud", payload, wsURL.Host) + + metricConnectionTotalPingReceivedCount.WithLabelValues("cloud", wsURL.Host).Inc() + metricConnectionLastPingReceivedTimestamp.WithLabelValues("cloud", wsURL.Host).SetToCurrentTime() + return true }, }) + // if the context is canceled, we don't want to return an error if err != nil { + if errors.Is(err, context.Canceled) { + cloudLogger.Infof("websocket connection canceled") + return nil + } return err } defer c.CloseNow() //nolint:errcheck cloudLogger.Infof("websocket connected to %s", wsURL) // set the metrics when we successfully connect to the cloud. - wsResetMetrics(true, "cloud", "") + wsResetMetrics(true, "cloud", wsURL.Host) // we don't have a source for the cloud connection - return handleWebRTCSignalWsMessages(c, true, "") + return handleWebRTCSignalWsMessages(c, true, wsURL.Host) } func authenticateSession(ctx context.Context, c *websocket.Conn, req WebRTCSessionRequest) error { @@ -379,9 +405,6 @@ func handleSessionRequest(ctx context.Context, c *websocket.Conn, req WebRTCSess func RunWebsocketClient() { for { - // reset the metrics when we start the websocket client. - wsResetMetrics(false, "cloud", "") - // If the cloud token is not set, we don't need to run the websocket client. if config.CloudToken == "" { time.Sleep(5 * time.Second) diff --git a/web.go b/web.go index 0258dc6..6c35073 100644 --- a/web.go +++ b/web.go @@ -5,6 +5,7 @@ import ( "context" "embed" "encoding/json" + "errors" "fmt" "io/fs" "net/http" @@ -190,6 +191,10 @@ func handleLocalWebRTCSignal(c *gin.Context) { InsecureSkipVerify: true, // Allow connections from any origin OnPingReceived: func(ctx context.Context, payload []byte) bool { websocketLogger.Infof("ping frame received: %v, source: %s, sourceType: local", payload, source) + + metricConnectionTotalPingReceivedCount.WithLabelValues("local", source).Inc() + metricConnectionLastPingReceivedTimestamp.WithLabelValues("local", source).SetToCurrentTime() + return true }, } @@ -251,6 +256,15 @@ func handleWebRTCSignalWsMessages(wsCon *websocket.Conn, isCloudConnection bool, for { time.Sleep(WebsocketPingInterval) + if ctxErr := runCtx.Err(); ctxErr != nil { + if !errors.Is(ctxErr, context.Canceled) { + logWarnf("websocket connection closed: %v", ctxErr) + } else { + logTracef("websocket connection closed as the context was canceled: %v") + } + return + } + // set the timer for the ping duration timer := prometheus.NewTimer(prometheus.ObserverFunc(func(v float64) { metricConnectionLastPingDuration.WithLabelValues(sourceType, source).Set(v) @@ -269,7 +283,7 @@ func handleWebRTCSignalWsMessages(wsCon *websocket.Conn, isCloudConnection bool, // dont use `defer` here because we want to observe the duration of the ping duration := timer.ObserveDuration() - metricConnectionTotalPingCount.WithLabelValues(sourceType, source).Inc() + metricConnectionTotalPingSentCount.WithLabelValues(sourceType, source).Inc() metricConnectionLastPingTimestamp.WithLabelValues(sourceType, source).SetToCurrentTime() logTracef("received pong frame, duration: %v", duration) @@ -317,6 +331,10 @@ func handleWebRTCSignalWsMessages(wsCon *websocket.Conn, isCloudConnection bool, logWarnf("unable to write pong message: %v", err) return err } + + metricConnectionTotalPingReceivedCount.WithLabelValues(sourceType, source).Inc() + metricConnectionLastPingReceivedTimestamp.WithLabelValues(sourceType, source).SetToCurrentTime() + continue } From 3fbcb7e5c42e2c26ebbbdc5536ef4d9cf2ddf66a Mon Sep 17 00:00:00 2001 From: Adam Shiervani Date: Thu, 10 Apr 2025 16:09:37 +0200 Subject: [PATCH 12/12] chore(dev_deploy): update logging for websocket in deployment script (#348) --- dev_deploy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev_deploy.sh b/dev_deploy.sh index 3f2ebb6..02bbb24 100755 --- a/dev_deploy.sh +++ b/dev_deploy.sh @@ -91,7 +91,7 @@ cd "${REMOTE_PATH}" chmod +x jetkvm_app_debug # Run the application in the background -PION_LOG_TRACE=jetkvm,cloud ./jetkvm_app_debug +PION_LOG_TRACE=jetkvm,cloud,websocket ./jetkvm_app_debug EOF echo "Deployment complete."