const extendedClipboardActionNotify = 1 << 27;
const extendedClipboardActionProvide = 1 << 28;
-
export default class RFB extends EventTargetMixin {
- constructor(target, url, options) {
+ constructor(target, urlOrChannel, options) {
if (!target) {
throw new Error("Must specify target");
}
- if (!url) {
- throw new Error("Must specify URL");
+ if (!urlOrChannel) {
+ throw new Error("Must specify URL, WebSocket or RTCDataChannel");
}
super();
this._target = target;
- this._url = url;
+
+ if (typeof urlOrChannel === "string") {
+ this._url = urlOrChannel;
+ } else {
+ this._url = null;
+ this._rawChannel = urlOrChannel;
+ }
// Connection details
options = options || {};
break;
}
this._sock.off('close');
+ // Delete reference to raw channel to allow cleanup.
+ this._rawChannel = null;
});
this._sock.on('error', e => Log.Warn("WebSocket on-error event"));
// Slight delay of the actual connection so that the caller has
- // time to set up callbacks
- setTimeout(this._updateConnectionState.bind(this, 'connecting'));
+ // time to set up callbacks.
+ // This it not possible when a pre-existing socket is passed in and is just opened.
+ // If the caller creates this object in the open() callback of a socket and there's
+ // data pending doing it next tick causes a packet to be lost.
+ // This is particularly noticable for RTCDataChannel's where the other end creates
+ // the channel and the client, this end, gets notified it exists.
+ if (typeof urlOrChannel === 'string') {
+ setTimeout(this._updateConnectionState.bind(this, 'connecting'));
+ } else {
+ this._updateConnectionState('connecting');
+ }
Log.Debug("<< RFB.constructor");
_connect() {
Log.Debug(">> RFB.connect");
- Log.Info("connecting to " + this._url);
-
- try {
- // WebSocket.onopen transitions to the RFB init states
- this._sock.open(this._url, this._wsProtocols);
- } catch (e) {
- if (e.name === 'SyntaxError') {
- this._fail("Invalid host or port (" + e + ")");
- } else {
- this._fail("Error when opening socket (" + e + ")");
+ if (this._url) {
+ try {
+ Log.Info(`connecting to ${this._url}`);
+ this._sock.open(this._url, this._wsProtocols);
+ } catch (e) {
+ if (e.name === 'SyntaxError') {
+ this._fail("Invalid host or port (" + e + ")");
+ } else {
+ this._fail("Error when opening socket (" + e + ")");
+ }
+ }
+ } else {
+ try {
+ Log.Info(`attaching ${this._rawChannel} to Websock`);
+ this._sock.attach(this._rawChannel);
+ } catch (e) {
+ this._fail("Error attaching channel (" + e + ")");
}
}
this._sock.send([
(user.length >> 24) & 0xFF,
(user.length >> 16) & 0xFF,
- (user.legnth >> 8) & 0xFF,
+ (user.length >> 8) & 0xFF,
user.length & 0xFF
]);
this._sock.send([
(pass.length >> 24) & 0xFF,
(pass.length >> 16) & 0xFF,
- (pass.legnth >> 8) & 0xFF,
+ (pass.length >> 8) & 0xFF,
pass.length & 0xFF
]);
this._sock.sendString(user);