Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Protect multiple RSA requesters from each other #866

Merged
merged 1 commit into from
Mar 10, 2017

Conversation

gigabo
Copy link
Contributor

@gigabo gigabo commented Feb 1, 2017

If two ReactServerAgent requests are made to a given endpoint only one upstream http request is actually issued, and the result is provided to both requesters. Previously this result was passed by reference, so mutations by one requester interfered with the data for others.

This patch provides a fresh deep copy to each requester.

This has the unfortunate side effect of introducing a deep copy in the browser where we previously thought we could get away without one. It's a minor perf hit, but it's important for data integrity.

@gigabo gigabo added the bug An issue with the system label Feb 1, 2017
Copy link
Member

@roblg roblg left a comment

Choose a reason for hiding this comment

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

This seems significant enough (and subtle enough) to warrant adding some tests too, I think? No better way to prevent future regressions than enforcing it in code!

// cache when we wake up in the browser as we initially provide on the
// server.
//
this.dfd.resolve(JSON.stringify(res));
Copy link
Member

Choose a reason for hiding this comment

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

I apparently can't leave comments willy-nilly in this file, but should we do the same thing for error responses (line ~209)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, it's annoying that you can click to see those lines but can't comment on them. 👿

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice catch, didn't realize those were handled separately. 👍

@doug-wade
Copy link
Collaborator

Rather than pass a different mutable copy to each requester, why not pass a single immutable reference to each requester?

@gigabo
Copy link
Contributor Author

gigabo commented Feb 2, 2017

@doug-wade How would that work?

@doug-wade
Copy link
Collaborator

doug-wade commented Feb 2, 2017

If we were willing to add a library for immutability, it'd be really simple

const Immutable = import("immutable");
this.dfd.resolve(Immutable.map(res));

If instead we wanted to roll our own, we could do a recursive freeze:

this.dfd.resolve(deepFreeze(res));

function deepFreeze (o) {
  Object.freeze(o);

  Object.keys(o).forEach((prop) => {
    if (typeof o[prop] === "object") {
      deepFreeze(o[prop]);
    }
  });
  
  return o;
}

@gigabo
Copy link
Contributor Author

gigabo commented Feb 2, 2017

@doug-wade I see. That would be a pretty big breaking change.

Worth discussing, but I'd still like to get a fix for the bug out in the meanwhile. 🐛

@@ -223,7 +217,7 @@ class CacheEntry {
// server-side, we increment the number of requesters
// we expect to retrieve the data on the frontend
this.requesters += 1;
return this.dfd.promise;
return this.dfd.promise.then(val => JSON.parse(val));
Copy link
Collaborator

Choose a reason for hiding this comment

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

don't we need to parse the JSON in both the if and else blocks?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, probably. 😁

Nice catch @drewpc!

@drewpc
Copy link
Collaborator

drewpc commented Feb 13, 2017

Seems like there's more work and it's failing the tests. Should I close the PR for now?

@gigabo
Copy link
Contributor Author

gigabo commented Feb 13, 2017

It's not dead yet! It could pull through!

If two ReactServerAgent requests are made to a given endpoint only one
upstream http request is actually issued, and the result is provided to both
requesters.  Previously this result was passed by reference, so mutations
by one requester interfered with the data for others.

This patch provides a fresh deep copy to each requester.

This has the unfortunate side effect of introducing a deep copy in the browser
where we previously thought we could get away without one.  It's a minor perf
hit, but it's important for data integrity.
@gigabo
Copy link
Contributor Author

gigabo commented Mar 8, 2017

don't we need to parse the JSON in both the if and else blocks?

Good call. Done.

should we do the same thing for error responses

I don't think so, actually. Error responses are less likely to be mutated, and their handling is out of band.

adding some tests

The coverage here was already pretty good (thanks @roblg), so I just modified existing tests to verify that values are deeply equal, but not references.

So... how about it? 😁

@gigabo gigabo merged commit 35bb392 into redfin:master Mar 10, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug An issue with the system
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants