Skip to content

fix(kvm): close browser websocket immediately on AMT disconnect#820

Merged
nmgaston merged 4 commits intodevice-management-toolkit:mainfrom
nmgaston:fixKVMNotShowingDisconnected
Mar 16, 2026
Merged

fix(kvm): close browser websocket immediately on AMT disconnect#820
nmgaston merged 4 commits intodevice-management-toolkit:mainfrom
nmgaston:fixKVMNotShowingDisconnected

Conversation

@nmgaston
Copy link
Copy Markdown
Contributor

@nmgaston nmgaston commented Mar 3, 2026

NOTE: Use with sample-web-ui PR: device-management-toolkit/sample-web-ui#3159

Problem

When an AMT device disconnects (e.g. the device is powered off or the network drops), the KVM/SOL/IDER session would appear frozen in the browser UI. The browser websocket was only closed after ListenToBrowser unblocked from its ReadMessage call, which could take up to 30 seconds (the inactivity timeout) or indefinitely if the browser wasn't sending data.

Fix

In ListenToDevice, the deferred cleanup now immediately sends a websocket CloseMessage to the browser and closes the connection before cancelling the context. This means the browser receives the close frame as soon as the AMT device side drops, allowing the UI to update without waiting for ListenToBrowser to unblock.

Changes

  • internal/usecase/devices/interceptor.go — updated the ListenToDevice defer to send websocket.CloseMessage and call Close() on the browser connection before cancel()
  • internal/usecase/devices/interceptor_private_test.go — added TestListenToDeviceClosesWebSocketOnAMTDisconnect to verify WriteMessage(CloseMessage) and Close() are called on the browser websocket when the AMT side disconnects

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves KVM/SOL/IDER session teardown behavior by proactively closing the browser WebSocket as soon as the AMT/device side disconnects, preventing the UI from appearing “frozen” while ListenToBrowser remains blocked.

Changes:

  • Updated ListenToDevice cleanup to send a WebSocket close frame to the browser and close the connection immediately on exit.
  • Added a private unit test to verify CloseMessage + Close() are invoked when the AMT side drops.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
internal/usecase/devices/interceptor.go Sends CloseMessage + closes browser WebSocket in ListenToDevice defer; minor comment tweak near length casting.
internal/usecase/devices/interceptor_private_test.go Adds a spy-based unit test asserting the browser WebSocket is closed when AMT disconnects.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/usecase/devices/interceptor.go Outdated
Comment thread internal/usecase/devices/interceptor.go Outdated
Comment thread internal/usecase/devices/interceptor_private_test.go
@graikhel-intel
Copy link
Copy Markdown
Contributor

graikhel-intel commented Mar 11, 2026

@nmgaston thanks for doing this! Madhavi and I were able to test this today and it’s looking good.

One minor improvement could be around logging. It might help to add a message like: KVM websocket session for <UUID> was closed by AMT.

This would make it easier to debug customer issues where it’s unclear whether the user/browser closed the KVM session or if AMT closed the connection

@nmgaston nmgaston force-pushed the fixKVMNotShowingDisconnected branch 2 times, most recently from c9e6bc3 to 9418c74 Compare March 14, 2026 03:45
… on AMT disconnect

- Capture the browser WebSocket connection before the defer so the
  close targets the same connection used for writes, even if
  deviceConnection.Conn is updated elsewhere; guard against nil.
- Downgrade 'KVM session closed by AMT' log from Info to Debug.
- Add t.Cleanup to stop healthTicker and call cancel in the
  ListenToDevice disconnect test.
@nmgaston nmgaston merged commit 3cead82 into device-management-toolkit:main Mar 16, 2026
17 checks passed
@nmgaston nmgaston deleted the fixKVMNotShowingDisconnected branch March 16, 2026 20:04
@RosieAMT
Copy link
Copy Markdown

🎉 This PR is included in version 1.22.2 🎉

The release is available on:

Your semantic-release bot 📦🚀

DevipriyaS17 added a commit that referenced this pull request Apr 13, 2026
* build:  bump from golang:1.25.6-alpine to golang:1.25.7-alpine (#798)

* build(deps): bump github.com/device-management-toolkit/go-wsman-messages/v2 (#795)

Bumps [github.com/device-management-toolkit/go-wsman-messages/v2](https://github.com/device-management-toolkit/go-wsman-messages) from 2.36.1 to 2.36.2.
- [Release notes](https://github.com/device-management-toolkit/go-wsman-messages/releases)
- [Commits](device-management-toolkit/go-wsman-messages@v2.36.1...v2.36.2)

---
updated-dependencies:
- dependency-name: github.com/device-management-toolkit/go-wsman-messages/v2
  dependency-version: 2.36.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Madhavi Losetty <madhavi.losetty@intel.com>

* build(deps): bump modernc.org/sqlite from 1.44.3 to 1.45.0 (#787)

Bumps [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) from 1.44.3 to 1.45.0.
- [Changelog](https://gitlab.com/cznic/sqlite/blob/master/CHANGELOG.md)
- [Commits](https://gitlab.com/cznic/sqlite/compare/v1.44.3...v1.45.0)

---
updated-dependencies:
- dependency-name: modernc.org/sqlite
  dependency-version: 1.45.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Madhavi Losetty <madhavi.losetty@intel.com>

* build(docs): failing to generate api spec should not be fatal (#800)

and should use configured log level

* feat(power): add EnforceSecureBoot error message for CCM mode (#779)

Prevent EnforceSecureBoot from being disabled when the device is in Client Control Mode (CCM).
The bootOptions now checks the provisioning mode and returns a 400 error if EnforceSecureBoot is set to false in CCM.

* fix: handle encryption and decryption errors instead of silently ignoring them (#778)

* fix: handle password encryption errors in dtoToEntity functions

* fix: handle errors in decrypt and fix UT

* docs: add security guidelines (#808)

* feat: add KVM performance timing metrics and monitoring (#761)

* feat: add KVM performance timing metrics and monitoring

- Add histograms to track KVM connection setup timing:
  - Device lookup time from database
  - TCP connection establishment time
  - WebSocket upgrade duration
  - Total connection time
  - Consent code wait time
  - API request durations
- Add metrics for KVM data flow performance:
  - Device-to-browser and browser-to-device write/send durations
  - Payload size distributions
  - Receive/read block times
- Create recording functions for all metrics
- Add detailed timing logs with KVM_TIMING prefix
- Update tests to handle new timing metrics

* refactor: replace manual REST endpoint instrumentation with go-gin-prometheus

Replace manual timing instrumentation in individual REST endpoints with
go-gin-prometheus middleware for automatic metrics coverage across all
HTTP endpoints.

Changes:
- Add go-gin-prometheus dependency to provide standard HTTP metrics
- Integrate Prometheus middleware in router for automatic instrumentation
- Remove manual timing code from features, KVM displays, and power endpoints
- Remove unused kvmAPIRequestSeconds metric and RecordAPIRequest function
- Clean up unused time and devices package imports

Benefits:
- Automatic metrics for ALL REST endpoints (not just 3)
- Standard HTTP metrics (duration, status codes, request/response sizes)
- Less repetitive code and improved maintainability
- Keeps all custom KVM connection and data flow metrics

Addresses reviewer feedback in PR #761

---------

Co-authored-by: Mike <michael.johanson@intel.com>

* fix: prevent segfault in CIRA cleanup and EOF errors in hardware collection (#776) (#788)

1. Initialize APF session timer to prevent nil pointer dereference
   during CIRA connection cleanup.
2. Replace direct CIM.Chip.Get() calls with Enumerate+Pull pattern to handle
   empty/malformed chip data gracefully and prevent connection termination.

* build(deps): bump modernc.org/sqlite from 1.45.0 to 1.46.1 (#809)

Bumps [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) from 1.45.0 to 1.46.1.
- [Changelog](https://gitlab.com/cznic/sqlite/blob/master/CHANGELOG.md)
- [Commits](https://gitlab.com/cznic/sqlite/compare/v1.45.0...v1.46.1)

---
updated-dependencies:
- dependency-name: modernc.org/sqlite
  dependency-version: 1.46.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: DevipriyaS17 <devipriya.s@intel.com>

* build(deps): bump github.com/zsais/go-gin-prometheus from 1.0.2 to 1.0.3 (#811)

Bumps [github.com/zsais/go-gin-prometheus](https://github.com/zsais/go-gin-prometheus) from 1.0.2 to 1.0.3.
- [Release notes](https://github.com/zsais/go-gin-prometheus/releases)
- [Commits](zsais/go-gin-prometheus@v1.0.2...v1.0.3)

---
updated-dependencies:
- dependency-name: github.com/zsais/go-gin-prometheus
  dependency-version: 1.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix: Update the validator criteria to allow wifi config is empty when DHCP is false (#799)

Co-authored-by: Madhavi Losetty <madhavi.losetty@intel.com>

* build(docker): ensure user is non-root

* refactor: use one logger throughout application

* fix: suppress browser launch in noui builds (#812)

Co-authored-by: Sudhir Pola <sudhir.pola@intel.com>
Co-authored-by: Mike <michael.johanson@intel.com>

* fix: ensure auth failure returns without next() call (#803)

also ensure JWT signatures matches expected

* ci: fix release build introduced in #812

* build(deps): bump github.com/gin-gonic/gin from 1.11.0 to 1.12.0 (#819)

Bumps [github.com/gin-gonic/gin](https://github.com/gin-gonic/gin) from 1.11.0 to 1.12.0.
- [Release notes](https://github.com/gin-gonic/gin/releases)
- [Changelog](https://github.com/gin-gonic/gin/blob/master/CHANGELOG.md)
- [Commits](gin-gonic/gin@v1.11.0...v1.12.0)

---
updated-dependencies:
- dependency-name: github.com/gin-gonic/gin
  dependency-version: 1.12.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump golang from 1.25.7-alpine to 1.26.1-alpine (#825)

Bumps golang from 1.25.7-alpine to 1.26.1-alpine.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1-alpine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* feat: enables support for redirection over CIRA

addresses #743

* refactor: replace mutex usage with safer connection
management methods to handle concurrent CIRA connections

* test: add coverage for new cira redirection functionality

* fix: resolve Memory Summary showing no data in Hardware Information (… (#821)

* fix: resolve Memory Summary showing no data in Hardware Information (#816)

Enhanced parseCIMResponse to handle typed slices using reflection.
Added test for parseCIMResponse function achieving 100% coverage.

Signed-off-by: C, Amarnath <amarnath.c@intel.com>
Signed-off-by: S, Devipriya <devipriya.s@intel.com>

* fix: replace reflection with source conversion in memory summary

Convert PhysicalMemory slice to interface slice at source.

* fix: rename interfaceSlice to convertPhysicalMemorySlice for clarity

Address PR review feedback to use more specific function naming
that reflects its single-purpose for PhysicalMemory type conversion.

---------

Signed-off-by: C, Amarnath <amarnath.c@intel.com>
Signed-off-by: S, Devipriya <devipriya.s@intel.com>

* fix(kvm): close browser websocket immediately on AMT disconnect (#820)

* ci: update GitHub Actions to Node.js 24 compatible versions (#845)

Node.js 20 actions are deprecated and will be forced to Node.js 24                                                                                                                                                                                                                starting June 2nd, 2026. Updated all pinned action SHAs:
- actions/checkout: v4.2.2 → v5.0.0
- actions/cache: v4.2.0 → v5.0.3                                                                                                                                                                                                                                                  - step-security/harden-runner: v2.10.2 → v2.16.0
- github/codeql-action: v3.27.9 → v4.33.0

* build(deps): bump github.com/device-management-toolkit/go-wsman-messages/v2 (#848)

Bumps [github.com/device-management-toolkit/go-wsman-messages/v2](https://github.com/device-management-toolkit/go-wsman-messages) from 2.37.0 to 2.38.0.
- [Release notes](https://github.com/device-management-toolkit/go-wsman-messages/releases)
- [Commits](device-management-toolkit/go-wsman-messages@v2.37.0...v2.38.0)

---
updated-dependencies:
- dependency-name: github.com/device-management-toolkit/go-wsman-messages/v2
  dependency-version: 2.38.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Madhavi Losetty <madhavi.losetty@intel.com>

* refactor: address lint issues in main

* build(deps): bump modernc.org/sqlite from 1.46.1 to 1.47.0

Bumps [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) from 1.46.1 to 1.47.0.
- [Changelog](https://gitlab.com/cznic/sqlite/blob/master/CHANGELOG.md)
- [Commits](https://gitlab.com/cznic/sqlite/compare/v1.46.1...v1.47.0)

---
updated-dependencies:
- dependency-name: modernc.org/sqlite
  dependency-version: 1.47.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix: add connection status tracking with last connected/disconnected (#849)

* fix: add connection status tracking with last connected/disconnected (#828)

* build(deps): bump github.com/jackc/pgx/v5 from 5.8.0 to 5.9.1 (#851)

Bumps [github.com/jackc/pgx/v5](https://github.com/jackc/pgx) from 5.8.0 to 5.9.1.
- [Changelog](https://github.com/jackc/pgx/blob/master/CHANGELOG.md)
- [Commits](jackc/pgx@v5.8.0...v5.9.1)

---
updated-dependencies:
- dependency-name: github.com/jackc/pgx/v5
  dependency-version: 5.9.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix: derive sharedStaticIP from DHCPEnabled in profile export (#853)

Previously, sharedStaticIP was hardcoded to false in the profile export
  configuration. It is now correctly derived as the inverse of DHCPEnabled,
  matching the UI behavior where selecting STATIC mode implies shared
  static IP is true.

* build(deps): bump github.com/hashicorp/vault/api from 1.22.0 to 1.23.0 (#856)

Bumps [github.com/hashicorp/vault/api](https://github.com/hashicorp/vault) from 1.22.0 to 1.23.0.
- [Release notes](https://github.com/hashicorp/vault/releases)
- [Changelog](https://github.com/hashicorp/vault/blob/main/CHANGELOG-v1.10-v1.15.md)
- [Commits](hashicorp/vault@api/v1.22.0...api/v1.23.0)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/vault/api
  dependency-version: 1.23.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump github.com/rs/zerolog from 1.34.0 to 1.35.0 (#866)

Bumps [github.com/rs/zerolog](https://github.com/rs/zerolog) from 1.34.0 to 1.35.0.
- [Commits](rs/zerolog@v1.34.0...v1.35.0)

---
updated-dependencies:
- dependency-name: github.com/rs/zerolog
  dependency-version: 1.35.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump github.com/gin-contrib/cors from 1.7.6 to 1.7.7 (#864)

Bumps [github.com/gin-contrib/cors](https://github.com/gin-contrib/cors) from 1.7.6 to 1.7.7.
- [Release notes](https://github.com/gin-contrib/cors/releases)
- [Commits](gin-contrib/cors@v1.7.6...v1.7.7)

---
updated-dependencies:
- dependency-name: github.com/gin-contrib/cors
  dependency-version: 1.7.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump modernc.org/sqlite from 1.47.0 to 1.48.0

Bumps [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) from 1.47.0 to 1.48.0.
- [Changelog](https://gitlab.com/cznic/sqlite/blob/master/CHANGELOG.md)
- [Commits](https://gitlab.com/cznic/sqlite/compare/v1.47.0...v1.48.0)

---
updated-dependencies:
- dependency-name: modernc.org/sqlite
  dependency-version: 1.48.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump github.com/gin-contrib/pprof from 1.5.3 to 1.5.4

Bumps [github.com/gin-contrib/pprof](https://github.com/gin-contrib/pprof) from 1.5.3 to 1.5.4.
- [Release notes](https://github.com/gin-contrib/pprof/releases)
- [Commits](gin-contrib/pprof@v1.5.3...v1.5.4)

---
updated-dependencies:
- dependency-name: github.com/gin-contrib/pprof
  dependency-version: 1.5.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* refactor: adds dtos for hwinfo

* fix: add name validation to CIRA config to prevent 500 on invalid configName (#867)

Enforce

alphanumhyphenunderscore validation (^[a-zA-Z0-9_-]+$) on the
in the name would pass through to downstream operations and cause a
500 Internal Server Error. With this change, invalid names are rejected
at the validation layer with a proper 4xx error.

Resolves device-management-toolkit/rps#2599

* build(deps): bump github.com/go-playground/validator/v10 (#869)

Bumps [github.com/go-playground/validator/v10](https://github.com/go-playground/validator) from 10.30.1 to 10.30.2.
- [Release notes](https://github.com/go-playground/validator/releases)
- [Commits](go-playground/validator@v10.30.1...v10.30.2)

---
updated-dependencies:
- dependency-name: github.com/go-playground/validator/v10
  dependency-version: 10.30.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump github.com/go-jose/go-jose/v4 from 4.1.3 to 4.1.4 (#872)

Bumps [github.com/go-jose/go-jose/v4](https://github.com/go-jose/go-jose) from 4.1.3 to 4.1.4.
- [Release notes](https://github.com/go-jose/go-jose/releases)
- [Commits](go-jose/go-jose@v4.1.3...v4.1.4)

---
updated-dependencies:
- dependency-name: github.com/go-jose/go-jose/v4
  dependency-version: 4.1.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump modernc.org/sqlite from 1.48.0 to 1.48.1 (#873)

Bumps [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) from 1.48.0 to 1.48.1.
- [Changelog](https://gitlab.com/cznic/sqlite/blob/master/CHANGELOG.md)
- [Commits](https://gitlab.com/cznic/sqlite/compare/v1.48.0...v1.48.1)

---
updated-dependencies:
- dependency-name: modernc.org/sqlite
  dependency-version: 1.48.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix: ensure CIM_Chip is returned in correct format

* test: add go fuzz tests for key internal functions

* fix: check len of state before accessing power state

* build(deps): bump github.com/coreos/go-oidc/v3 from 3.17.0 to 3.18.0 (#879)

Bumps [github.com/coreos/go-oidc/v3](https://github.com/coreos/go-oidc) from 3.17.0 to 3.18.0.
- [Release notes](https://github.com/coreos/go-oidc/releases)
- [Commits](coreos/go-oidc@v3.17.0...v3.18.0)

---
updated-dependencies:
- dependency-name: github.com/coreos/go-oidc/v3
  dependency-version: 3.18.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump golang from 1.26.1-alpine to 1.26.2-alpine (#878)

Bumps golang from 1.26.1-alpine to 1.26.2-alpine.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.2-alpine
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(ci): relax commitlint scope for sync PRs

* chore(ci): use go test exit codes instead of grep

* fix(redfish): prevent UUID path race in parallel tests

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: C, Amarnath <amarnath.c@intel.com>
Signed-off-by: S, Devipriya <devipriya.s@intel.com>
Co-authored-by: Madhavi Losetty <madhavi.losetty@intel.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Mike <michael.johanson@intel.com>
Co-authored-by: Ganesh Raikhelkar <ganesh.raikhelkar@intel.com>
Co-authored-by: Natalie Gaston <natalie.gaston@intel.com>
Co-authored-by: Amarnath C <amarnath.c@intel.com>
Co-authored-by: choonkeat1986 <100182284+choonkeat1986@users.noreply.github.com>
Co-authored-by: Sudhir Pola <sudhir.pola@intel.com>
Co-authored-by: Loh, Shao Boon <shao.boon.loh@intel.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Console UI does not reflect KVM disconnect after AMT closes idle session (~3 minutes inactivity)

5 participants