JSON serialization doesn't support UTF-8
Hi, I was trying to use the JSON serializer and ran into trouble when trying to put an emoji into the session (in real life, this is more likely to happen with flash messages).
Here is the code to reproduce it:
require 'rack/session'
use Rack::Session::Cookie, secrets: ('a' * 64), serialize_json: true
run lambda { |env|
env[Rack::RACK_SESSION][:foo] = '😀'
[200, {}, ['']]
}
The output is as follows:
incompatible character encodings: ASCII-8BIT and UTF-8 (Encoding::CompatibilityError)
/Users/iain/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/rack-session-2.0.0/lib/rack/session/encryptor.rb:177:in `serialize_payload'
/Users/iain/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/rack-session-2.0.0/lib/rack/session/encryptor.rb:105:in `encrypt'
/Users/iain/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/rack-session-2.0.0/lib/rack/session/cookie.rb:296:in `encode_session_data'
/Users/iain/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/rack-session-2.0.0/lib/rack/session/cookie.rb:267:in `write_session'
/Users/iain/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/rack-session-2.0.0/lib/rack/session/abstract/id.rb:394:in `commit_session'
/Users/iain/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/rack-session-2.0.0/lib/rack/session/abstract/id.rb:274:in `context'
/Users/iain/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/rack-session-2.0.0/lib/rack/session/abstract/id.rb:266:in `call'
/Users/iain/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/puma-6.4.2/lib/puma/configuration.rb:272:in `call'
/Users/iain/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/puma-6.4.2/lib/puma/request.rb:100:in `block in handle_request'
/Users/iain/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/puma-6.4.2/lib/puma/thread_pool.rb:378:in `with_force_shutdown'
/Users/iain/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/puma-6.4.2/lib/puma/request.rb:99:in `handle_request'
/Users/iain/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/puma-6.4.2/lib/puma/server.rb:464:in `process_client'
/Users/iain/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/puma-6.4.2/lib/puma/server.rb:245:in `block in run'
It looks like the problem arises from the padding in the encryptor. The padding amount and random bytes are not compatible with the json output, and I don't see an easy way around it either.
Of course the default Marshal encoding works, but I was hoping to move away from that.
Does the JSON data get encoded as the raw value of the header? If so, the only solution would be to base64 (or url encode) it, e.g. emoji -> JSON -> url encoded header. IF that's not what we are already doing, I'd accept a PR for that.
Forcing the encoding of serialized_data in Rack::Session::Encryptor::Serializable#serialize_payload to BINARY fixes it; something we should do, as we want to send it as-is. I'll prepare a PR over the weekend, adding tests to cover this scenario. Thanks for the report @iain!