Aaronius / penpal

A promise-based library for securely communicating with iframes via postMessage.
MIT License
389 stars 56 forks source link

Multiple children on same origin #3

Closed loganvolkers closed 7 years ago

loganvolkers commented 7 years ago

When there are multiple Children on the same domain, the children will produce cross-talk. This is because the parent's postMessage channel is shared between the children.

The solution to this is for every child to get a childId to identify it uniquely. I implemented this approach for Postmate, but I'd like to switch to Penpal for the additional feature it provides.

Here's a test case that I wrote up to illustrate the problem.

I'll start working on a pull request. Any contribution guidelines?

  it('should call an asynchronous function on the child, but only the right child!', (done) => {
    const c1 = Penpal.connectToChild({
      url: `http://${HOST}:9000/child.html`
    });
    const c2 = Penpal.connectToChild({
      url: `http://${HOST}:9000/child.html`
    });

    c1.promise.then((child) => {
      child.multiplyAsync(2, 5).then((value) => {
        expect(value).toEqual(10);
        connection.destroy();
        done();
      });
    });
    c2.promise.then((child) => {
      child.multiplyAsync(3, 5).then((value) => {
        expect(value).toEqual(15);
        connection.destroy();
        done();
      });
    });

  });
Aaronius commented 7 years ago

This would be a fantastic contribution. Thank you for offering. Contributing guidelines:

Try to follow the conventions already in the code. Don't be offended if I tweak it later. File size is a feature. Consider it as such. Add tests where you can (looks like you already have the idea).

Thanks!

loganvolkers commented 7 years ago

Well shoot. My fix of adding a childId didn't fix my test. Turns out the problem was in my test code.

Here's the revised test code:

  it('should call an asynchronous function on the child, but only the right child!', (done) => {
    const c1 = Penpal.connectToChild({
      url: `http://${HOST}:9000/child.html`
    });
    const c2 = Penpal.connectToChild({
      url: `http://${HOST}:9000/child.html`
    });

    c1.promise.then((child) => {
      child.multiplyAsync(2, 5).then((value) => {
        expect(value).toEqual(10);
        c1.destroy();
        done();
      });
    });
    c2.promise.then((child) => {
      child.multiplyAsync(3, 5).then((value) => {
        expect(value).toEqual(15);
        c2.destroy();
        done();
      });
    });

  });

Also today I learned that using event.source and data.id already provides adequate filtering for this use case.

          if (event.source === remote &&
              event.origin === remoteOrigin &&
              event.data.penpal === REPLY &&
              event.data.id === id) {

Thanks for the prompt response! Looking forward to using Penpal for more.

Aaronius commented 6 years ago

This test has been added to the test suite: https://github.com/Aaronius/penpal/commit/3e61c23252720e8226ea48d6ca3f82f41f474044

I tweaked it a bit to make sure done will only get called after both method calls resolve.