GUACAMOLE-201: Occasionally verify client responsiveness when intercepting streams.

This commit is contained in:
Michael Jumper
2017-02-08 19:49:01 -08:00
parent 7fd7035a47
commit 6ae9faddc2
2 changed files with 44 additions and 1 deletions

View File

@@ -47,6 +47,14 @@ public class OutputStreamInterceptingFilter
private static final Logger logger =
LoggerFactory.getLogger(OutputStreamInterceptingFilter.class);
/**
* Whether this OutputStreamInterceptingFilter should respond to received
* blobs with "ack" messages on behalf of the client. If false, blobs will
* still be handled by this filter, but empty blobs will be sent to the
* client, forcing the client to respond on its own.
*/
private boolean acknowledgeBlobs = true;
/**
* Creates a new OutputStreamInterceptingFilter which selectively intercepts
* "blob" and "end" instructions. The required "ack" responses will
@@ -129,10 +137,22 @@ public class OutputStreamInterceptingFilter
return null;
}
// Attempt to write data to stream
try {
// Attempt to write data to stream
stream.getStream().write(blob);
// Force client to respond with their own "ack" if we need to
// confirm that they are not falling behind with respect to the
// graphical session
if (!acknowledgeBlobs) {
acknowledgeBlobs = true;
return new GuacamoleInstruction("blob", index, "");
}
// Otherwise, acknowledge the blob on the client's behalf
sendAck(index, "OK", GuacamoleStatus.SUCCESS);
}
catch (IOException e) {
sendAck(index, "FAIL", GuacamoleStatus.SERVER_ERROR);
@@ -164,6 +184,17 @@ public class OutputStreamInterceptingFilter
}
/**
* Handles a single "sync" instruction, updating internal tracking of
* client render state.
*
* @param instruction
* The "sync" instruction being handled.
*/
private void handleSync(GuacamoleInstruction instruction) {
acknowledgeBlobs = false;
}
@Override
public GuacamoleInstruction filter(GuacamoleInstruction instruction)
throws GuacamoleException {
@@ -178,6 +209,13 @@ public class OutputStreamInterceptingFilter
return instruction;
}
// Monitor "sync" instructions to ensure the client does not starve
// from lack of graphical updates
if (instruction.getOpcode().equals("sync")) {
handleSync(instruction);
return instruction;
}
// Pass instruction through untouched
return instruction;

View File

@@ -215,6 +215,11 @@ angular.module('rest').factory('tunnelService', ['$injector',
document.body.removeChild(iframe);
};
// Acknowledge (and ignore) any received blobs
stream.onblob = function acknowledgeData() {
stream.sendAck('OK', Guacamole.Status.Code.SUCCESS);
};
// Automatically remove iframe from DOM a few seconds after the stream
// ends, in the browser does NOT fire the "load" event for downloads
stream.onend = function downloadComplete() {