Merge branch 'feature/VNC-275_server_idle_timeout' into 'master'

Resolve VNC-275 "Feature/ server idle timeout"

Closes VNC-275

See merge request kasm-technologies/internal/KasmVNC!219
This commit is contained in:
Matthew McClaskey 2025-11-03 17:45:40 +00:00
commit 28bbc92467
14 changed files with 67 additions and 2 deletions

2
.gitmodules vendored
View file

@ -1,7 +1,7 @@
[submodule "kasmweb"]
path = kasmweb
url = https://github.com/kasmtech/noVNC.git
branch = master
branch = feature/VNC-275_server_idle_timeout
[submodule "kasmvnc-functional-tests"]
path = kasmvnc-functional-tests
url = git@gitlab.com:kasm-technologies/internal/kasmvnc-functional-tests.git

View file

@ -59,6 +59,7 @@ ConnParams::ConnParams()
supportsWEBP(false), supportsQOI(false),
supportsSetDesktopSize(false), supportsFence(false),
supportsContinuousUpdates(false), supportsExtendedClipboard(false),
supportsDisconnectNotify(false),
supportsUdp(false),
compressLevel(2), qualityLevel(-1), fineQualityLevel(-1),
subsampling(subsampleUndefined), name_(0), cursorPos_(0, 0), verStrPos(0),
@ -147,6 +148,7 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
supportsQEMUKeyEvent = false;
supportsWEBP = false;
supportsQOI = false;
supportsDisconnectNotify = false;
compressLevel = -1;
qualityLevel = -1;
fineQualityLevel = -1;
@ -215,6 +217,10 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
supportsQOI = true;
clientparlog("qoi", true);
break;
case pseudoEncodingKasmDisconnectNotify:
supportsDisconnectNotify = true;
clientparlog("disconnectNotify", true);
break;
case pseudoEncodingFence:
supportsFence = true;
clientparlog("fence", true);

View file

@ -117,6 +117,7 @@ namespace rfb {
bool supportsFence;
bool supportsContinuousUpdates;
bool supportsExtendedClipboard;
bool supportsDisconnectNotify;
bool supportsUdp;

View file

@ -104,3 +104,7 @@ void SMsgHandler::setDesktopSize(int fb_width, int fb_height,
cp.height = fb_height;
cp.screenLayout = layout;
}
void SMsgHandler::keepAlive()
{
}

View file

@ -61,6 +61,7 @@ namespace rfb {
virtual void sendStats(const bool toClient = true) = 0;
virtual void handleFrameStats(rdr::U32 all, rdr::U32 render) = 0;
virtual void keepAlive();
virtual bool canChangeKasmSettings() const = 0;

View file

@ -106,6 +106,9 @@ void SMsgReader::readMsg()
case msgTypeUnixRelay:
readUnixRelay();
break;
case msgTypeKeepAlive:
readKeepAlive();
break;
default:
fprintf(stderr, "unknown message type %d\n", msgType);
throw Exception("unknown message type");
@ -319,6 +322,11 @@ void SMsgReader::readFrameStats()
handler->handleFrameStats(all, render);
}
void SMsgReader::readKeepAlive()
{
handler->keepAlive();
}
void SMsgReader::readQEMUMessage()
{
int subType = is->readU8();

View file

@ -59,6 +59,7 @@ namespace rfb {
void readRequestStats();
void readFrameStats();
void readBinaryClipboard();
void readKeepAlive();
void readQEMUMessage();
void readQEMUKeyEvent();

View file

@ -791,3 +791,20 @@ void SMsgWriter::writeUserLeftSession(const std::string& username)
os->writeString(username.c_str());
endMsg();
}
void SMsgWriter::writeDisconnectNotify(bool graceful, const char *reason)
{
if (!cp->supportsDisconnectNotify)
return;
const char *msg = reason ? reason : "";
size_t len = strlen(msg);
startMsg(msgTypeServerDisconnect);
os->writeU8(graceful ? 1 : 0);
os->pad(3);
os->writeU32(len);
if (len > 0)
os->writeBytes(msg, len);
endMsg();
}

View file

@ -135,6 +135,7 @@ namespace rfb {
void writeUserJoinedSession(const std::string& username);
void writeUserLeftSession(const std::string& username);
void writeDisconnectNotify(bool graceful, const char *reason);
protected:
void startMsg(int type);

View file

@ -49,6 +49,10 @@ static LogWriter vlog("VNCSConnST");
static Cursor emptyCursor(0, 0, Point(0, 0), NULL);
namespace {
const rdr::U32 CLIENT_KEEPALIVE_KEYSYM = 1;
}
extern rfb::BoolParameter disablebasicauth;
extern "C" char unixrelaynames[MAX_UNIX_RELAYS][MAX_UNIX_RELAY_NAME_LEN];
@ -557,6 +561,13 @@ int VNCSConnectionST::checkIdleTimeout()
return secsToMillis(idleTimeout);
}
if (timeLeft <= 0) {
if (cp.supportsDisconnectNotify) {
try {
writer()->writeDisconnectNotify(true, "Idle timeout");
} catch (rdr::Exception& e) {
vlog.debug("Failed to send disconnect notice: %s", e.str());
}
}
close("Idle timeout");
return 0;
}
@ -861,6 +872,10 @@ public:
void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) {
rdr::U32 lookup;
if (keycode == 0 && keysym == CLIENT_KEEPALIVE_KEYSYM) {
return;
}
lastEventTime = time(0);
server->lastUserInputTime = lastEventTime;
if (!(accessRights & AccessKeyEvents)) return;
@ -1699,6 +1714,11 @@ void VNCSConnectionST::handleFrameStats(rdr::U32 all, rdr::U32 render)
frameTracking = false;
}
void VNCSConnectionST::keepAlive()
{
// Keepalive traffic should not influence idle disconnect timers.
}
// setCursor() is called whenever the cursor has changed shape or pixel format.
// If the client supports local cursor then it will arrange for the cursor to
// be sent to the client.

View file

@ -172,6 +172,7 @@ namespace rfb {
virtual void sendStats(const bool toClient = true);
virtual void handleFrameStats(rdr::U32 all, rdr::U32 render);
virtual void keepAlive();
bool is_owner() const {
bool read, write, owner;

View file

@ -86,6 +86,7 @@ namespace rfb {
const int pseudoEncodingVideoOutTimeLevel1 = -1986;
const int pseudoEncodingVideoOutTimeLevel100 = -1887;
const int pseudoEncodingQOI = -1886;
const int pseudoEncodingKasmDisconnectNotify = -1885;
// VMware-specific
const int pseudoEncodingVMwareCursor = 0x574d5664;

View file

@ -35,6 +35,8 @@ namespace rfb {
const int msgTypeUpgradeToUdp = 181;
const int msgTypeSubscribeUnixRelay = 182;
const int msgTypeUnixRelay = 183;
const int msgTypeKeepAlive = 184;
const int msgTypeServerDisconnect = 185;
const int msgTypeServerFence = 248;
const int msgTypeUserAddedToSession = 253;
@ -61,6 +63,8 @@ namespace rfb {
//const int msgTypeUpgradeToUdp = 181;
//const int msgTypeSubscribeUnixRelay = 182;
//const int msgTypeUnixRelay = 183;
//const int msgTypeKeepAlive = 184;
//const int msgTypeServerDisconnect = 185;
const int msgTypeClientFence = 248;

@ -1 +1 @@
Subproject commit da6b8f3cfbed05891380f33aa4ca541c99d1f4c6
Subproject commit 0dab9997d98ff5046682f15e1632af5ecf72f180