-
-
Notifications
You must be signed in to change notification settings - Fork 633
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
\u2028 and \u2029 break rendering #375
Conversation
@@ -299,7 +299,7 @@ def server_rendered_react_component_html(props, react_component_name, dom_id, | |||
(function() { | |||
var railsContext = #{rails_context(server_side: true).to_json}; | |||
#{initialize_redux_stores} | |||
var props = #{props_string(props)}; | |||
var props = #{props_string(props).gsub("\u2028", '').gsub("\u2029", '')}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Maybe we should do this in one gsub?
- What are the 2028 and 2029 chars?
@mariusandra If possible, please submit your project to https://github.com/shakacode/react_on_rails/blob/master/PROJECTS.md |
@alexfedoseev This looks reasonable. @mariusandra Can you please sync up to master and make sure CI is passing. |
Hey, sorry for my silence up to now. I wanted to still test if with this change the rendered DOM on the server and on the client side will be identical or not. ... but I spent all my free time in the last 2 weeks preparing a long presentation for my local Ruby User Group. I'll sync and test this within 24h. Sorry for the wait :). And I'll submit my project to the README as well! |
Hey, First, instead of the double .gsub(), it's much faster to use .delete in this case, as demonstrated by this gist: https://gist.github.com/dominikh/208915 So instead of
to use
Then, my suspicions were correct. With the change proposed by the original PR, I got this error using the "hello world" sample app, with a name of ... so just removing these naughty characters in the block that gets sent to the server side rendering part is not enough. They need to be stripped out everywhere. I tried a few different approaches, but in the end all that had to be done was to strip them from the
The code block above does contain the actual \u2028 and \u2029 characters in The final patch was to update Since this method was called 4 times, I also added a memoization.
Now, since this is not my codebase, I'm not sure if |
This does break the tests. One option is to do |
@mariusandra Why are these characters getting into the data? Can you strip this before passing to react-on-rails? |
@justin808 I'm not sure where the characters come from. It was possibly some text the user copied from MS Word into a textarea... or a situation like that. In any case they were found in user entered text and that caused a 500 error instead of showing the page. I could strip these characters before passing props to react-on-rails, but then I would have to implement this filtering at every interaction my app has with react-on-rails. It would be easier if this functionality was already in react-on-rails, possibly also saving other users hours of time tracking this bug down. ... What the problem boils down to is best summarised with this article: http://timelessrepo.com/json-isnt-a-javascript-subset JSON is not a subset of JavaScript. Almost, but not quite. The difference is in these two annoying characters, \u2028 and \u2029. They're OK in JSON, but not OK in a JS string. In react-on-rails, the JSON made from props gets passed to execjs as a JS object:
and if it contains those characters, execjs crashes and a 500 error ensures. However it seems it's OK to pass those characters in the "data-props" attribute for client side rendering. There they are parsed as part of a JSON object (where they are allowed), not as part of JS (where they are not). Reading the link I gave above, they propose a solution:
My original fix was to just remove these characters from the server rendered string. The proper fix™ is to replace them on the server side with their escape sequence:
It's the same fix they use in RACK: |
One comment. Reviewed 1 of 2 files at r3. app/helpers/react_on_rails_helper.rb, line 302 [r1] (raw file): Also, are we sure that 2 gsubs are the fastest way? # Pads the response with the appropriate callback format according to the
# JSON-P spec/requirements.
#
# The Rack response spec indicates that it should be enumerable. The
# method of combining all of the data into a single string makes sense
# since JSON is returned as a full string.
#
def pad(callback, response, body = "")
response.each do |s|
# U+2028 and U+2029 are allowed inside strings in JSON (as all literal
# Unicode characters) but JavaScript defines them as newline
# seperators. Because no literal newlines are allowed in a string, this
# causes a ParseError in the browser. We work around this issue by
# replacing them with the escaped version. This should be safe because
# according to the JSON spec, these characters are *only* valid inside
# a string and should therefore not be present any other places.
body << s.to_s.gsub("\u2028", '\u2028').gsub("\u2029", '\u2029')
end Comments from Reviewable |
@mariusandra Please see my last comment and rebase on top of master after squashing your commits. |
@mariusandra Let's get this one rebased along with a changelog entry, and I'll get this into our upcoming 6.0 release. |
Hey, it'll be done before the end of the day. Sorry for the wait :) |
Hi, I have added a comment in the source file and a changelog entry. About the speed of 2 gsubs, I'm not sure, but I believe so. Since we're replacing one character with 6, we can't rely on the fast "he\u2028llo \u2029world".gsub("\u2028", '\u2028').gsub("\u2029", '\u2029') When benchmarking, it's a bit slower for this simple string: n = 1000000
Benchmark.bm do |x|
x.report('double gsub') { n.times do ; "he\u2028llo \u2029world".gsub("\u2028", '\u2028').gsub("\u2029", '\u2029'); end }
x.report('regexp gsub') { n.times do ; "he\u2028llo \u2029world".gsub(/[\u2028\u2029]/) {|s| '\u' + s.ord.to_s(16) }; end }
end Results:
A lot slower with a long string with many occurrences: n = 10000 # smaller n
Benchmark.bm do |x|
x.report('double gsub') { n.times do ; ("he\u2028llo \u2029world" * 100).gsub("\u2028", '\u2028').gsub("\u2029", '\u2029'); end }
x.report('regexp gsub') { n.times do ; ("he\u2028llo \u2029world" * 100).gsub(/[\u2028\u2029]/) {|s| '\u' + s.ord.to_s(16) }; end }
end results:
And a lot slower in this case: n = 100000
Benchmark.bm do |x|
x.report('double gsub') { n.times do ; (("hello" * 100) + "\u2028 \u2029" + ("world" * 100)).gsub("\u2028", '\u2028').gsub("\u2029", '\u2029'); end }
x.report('regexp gsub') { n.times do ; (("hello" * 100) + "\u2028 \u2029" + ("world" * 100)).gsub(/[\u2028\u2029]/) {|s| '\u' + s.ord.to_s(16) }; end }
end results:
so I would stick with it, unless someone knows of a better and faster way. Plus, knowing that it's good enough for RACK should be comforting. |
Thanks for all the hard work on this one, especially by doing the profiling.
|
Hey,
I'm operating a website (https://www.apprentus.com) where we user react_on_rails with prerendering to server content to our users. Currently it's just the homepage, with other pages under development.
I started getting errors that our homepage wasn't loading for some users. Digging deeper I found it's because of the character "\u2028" that was being sent in the props under the "latest classes" section, that features user entered content.
Digging further I found this link:
https://bugs.chromium.org/p/v8/issues/detail?id=1907
... and learned these two are special characters in JS.
Try it out yourself by running this in your browser
If you change the number to 2027 or 2030 then it works fine.
This PR is a quick fix that solved the problem for me, although there might be a more elegant solution available.
Marius
This change is