Skip to content
This repository has been archived by the owner on Feb 8, 2024. It is now read-only.

adding codec and encoding tests #381

Merged
merged 6 commits into from Aug 27, 2021
Merged

Conversation

ibeckermayer
Copy link
Contributor

Implements client side codec for the custom desktop access protocol (RFD 37). Clipboard was ignored for now as that part of the protocol needs revision.

UTF-8 encoder (stringToUtf8ByteArray) was ripped from the google closure-library

@alex-kovoy
Copy link
Contributor

@ibeckermayer could you use some feature branch for this POC? (we will review it when it's ready for production)

@alex-kovoy
Copy link
Contributor

btw, check out built-in browser APIs for utf-8 encoding (TextDecoder/TextEncoder) and see if you can remove unnecessary code.

@alex-kovoy alex-kovoy self-requested a review August 17, 2021 17:21
@ibeckermayer
Copy link
Contributor Author

@ibeckermayer could you use some feature branch for this POC? (we will review it when it's ready for production)

Sure

btw, check out built-in browser APIs for utf-8 encoding (TextDecoder/TextEncoder) and see if you can remove unnecessary code.

That's a much better solution... you would think this would pop up on the front page of a google search for "javascript encode string to utf8"

@alex-kovoy
Copy link
Contributor

also, check out our testing (unfinished) guide :) https://gravitational.slab.com/posts/testing-guideline-proposal-wip-yuc9uvwa

@ibeckermayer ibeckermayer changed the base branch from master to desktop-access August 17, 2021 19:42
@ibeckermayer
Copy link
Contributor Author

Unfortunately jsdom hasn't implemented TextEncoder either (jsdom/jsdom#2524), so I'm skipping those username/pwd tests. I think there is a way that I can effectively mock it and replace it with the stringToUtf8ByteArray.ts from the previous commits, however I'm not sure its worth it.

In the meantime, I double checked that all works as expected by converting those skipped tests into something that can be run by copy-pasting into the browser console. Any thoughts or previous experience regarding handling jsdom deficiencies?

var MessageType = {
  CLIENT_SCREEN_SPEC: 1,
  PNG_FRAME: 2,
  MOUSE_MOVE: 3,
  MOUSE_BUTTON: 4,
  KEYBOARD_INPUT: 5,
  CLIPBOARD_DATA: 6,
  USERNAME_PASSWORD_REQUIRED: 7,
  USERNAME_PASSWORD_RESPONSE: 8,
};

var Codec = /** @class */ (function () {
  function Codec() {}
  // encUsernamePassword encodes the username and password response
  Codec.prototype.encUsernamePassword = function (username, password) {
    // Encode username/pass to utf8
    var encoder = new TextEncoder();
    var usernameUtf8array = encoder.encode(username);
    var passwordUtf8array = encoder.encode(password);
    // initialize buffer and corresponding view
    // numbers correspond to message spec
    // | message type (8) | user_length uint32 | username []byte | pass_length uint32 | password []byte
    var bufLen =
      1 + 4 + usernameUtf8array.length + 4 + passwordUtf8array.length;
    var buffer = new ArrayBuffer(bufLen);
    var view = new DataView(buffer);
    var offset = 0;
    // set data
    view.setUint8(offset++, MessageType.USERNAME_PASSWORD_RESPONSE);
    view.setUint32(offset, usernameUtf8array.length);
    offset += 4; // 4 bytes to offset 32-bit uint
    usernameUtf8array.forEach(function (byte) {
      view.setUint8(offset++, byte);
    });
    view.setUint32(offset, passwordUtf8array.length);
    offset += 4;
    passwordUtf8array.forEach(function (byte) {
      view.setUint8(offset++, byte);
    });
    return buffer;
  };
  return Codec;
})();

var codec = new Codec();

// Create test vals + known UTF8 encodings
const username = "Hello";
const usernameUTF8 = [0x0048, 0x0065, 0x006c, 0x006c, 0x006f];
const password = "world!*@123";
const passwordUTF8 = [
  0x0077, 0x006f, 0x0072, 0x006c, 0x0064, 0x0021, 0x002a, 0x0040, 0x0031,
  0x0032, 0x0033,
];

// Encode test vals
const message1 = codec.encUsernamePassword(username, password);
const view1 = new DataView(message1);

// Walk through output
let offset1 = 0;
if (view1.getUint8(offset1++) != MessageType.USERNAME_PASSWORD_RESPONSE) {
  throw new Error("Test Failed!");
}
if (view1.getUint32(offset1) != usernameUTF8.length) {
  throw new Error("Test Failed!");
}
offset1 += 4;
usernameUTF8.forEach((byte) => {
  if (view1.getUint8(offset1++) != byte) {
    throw new Error("Test Failed!");
  }
});
if (view1.getUint32(offset1) != passwordUTF8.length) {
  throw new Error("Test Failed!");
}
offset1 += 4;
passwordUTF8.forEach((byte) => {
  if (view1.getUint8(offset1++) != byte) {
    throw new Error("Test Failed!");
  }
});

const first3RangesString = "\u0000\u007F\u0080\u07FF\u0800\uFFFF";
const first3RangesUTF8 = [
  0x00, 0x7f, 0xc2, 0x80, 0xdf, 0xbf, 0xe0, 0xa0, 0x80, 0xef, 0xbf, 0xbf,
];
const message2 = codec.encUsernamePassword(
  first3RangesString,
  first3RangesString
);
const view2 = new DataView(message2);
let offset2 = 0;
if (view2.getUint8(offset2++) != MessageType.USERNAME_PASSWORD_RESPONSE) {
  throw new Error("Test Failed!");
}
if (view2.getUint32(offset2) != first3RangesUTF8.length) {
  throw new Error("Test Failed!");
}
offset2 += 4;
first3RangesUTF8.forEach((byte) => {
  if (view2.getUint8(offset2++) != byte) {
    throw new Error("Test Failed!");
  }
});
if (view2.getUint32(offset2) != first3RangesUTF8.length) {
  throw new Error("Test Failed!");
}
offset2 += 4;
first3RangesUTF8.forEach((byte) => {
  if (view2.getUint8(offset2++) != byte) {
    throw new Error("Test Failed!");
  }
});

@ibeckermayer
Copy link
Contributor Author

ibeckermayer commented Aug 17, 2021

As an aside, do you understand why the JS ecosystem needs a project like JSDOM at all?

Naively -- v8 is open source, why can't Jest just use it to emulate the chrome browser precisely?

@alex-kovoy
Copy link
Contributor

@ibeckermayer you can mock these APIs in your tests with something like this https://github.com/gravitational/webapps/blob/master/packages/teleport/src/lib/term/protobuf.js#L178

Or you can mock it with nodejs one

 const { TextEncoder } = require('util');
 window.TextEncoder = TextEncoder;

As for JSDOM, nodejs does not have any renderer (html) and it's pure V8 engine. So to test UI layer you need something like JSDOM to mock DOM APIs.

@ibeckermayer
Copy link
Contributor Author

As for JSDOM, nodejs does not have any renderer (html) and it's pure V8 engine. So to test UI layer you need something like JSDOM to mock DOM APIs.

Oh I see, I was confused about the different parts involved. Still, why don't some wizards pull out the part of Chrome that implements the DOM API, rather than reimplement everything with JSDOM? Or why is that the wrong question?

Copy link
Contributor

@alex-kovoy alex-kovoy left a comment

Choose a reason for hiding this comment

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

can we change the directory name from tdp to rdp ? :-)

packages/teleport/src/lib/tdp/codec.test.ts Outdated Show resolved Hide resolved
packages/teleport/src/lib/tdp/codec.test.ts Outdated Show resolved Hide resolved
packages/teleport/src/lib/tdp/codec.test.ts Outdated Show resolved Hide resolved
@alex-kovoy
Copy link
Contributor

can we change the directory name from tdp to rdp ? :-)

I saw your reply about teleport desktop protocol - yes, it makes sense then

Copy link
Contributor

@alex-kovoy alex-kovoy left a comment

Choose a reason for hiding this comment

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

LGTM after comments are addressed

@ibeckermayer ibeckermayer merged commit d4b53c1 into desktop-access Aug 27, 2021
@alex-kovoy alex-kovoy deleted the isaiah/da-codec branch October 26, 2021 17:16
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants