When running in an environment that freezes string literals by default, `hybi.rb` raises exceptions as it modifies the `'C2'` string.
Adding the magic comment supports this use case without modifying behavior for anyone else. I added it to one spec too, so that running specs this way (`RUBYOPT="--enable=frozen-string-literal" rspec`) keeps them all passing.
We're seeing this occasionally in our error logs, where the `env`
passed into to the driver will return a frozen string when read.
The stack trace always includes ActionCable in to the mix, so
something like this:
"/app/vendor/bundle/ruby/2.6.0/gems/websocket-driver-0.7.0/lib/websocket/driver/draft76.rb" line 11 in force_encoding
"/app/vendor/bundle/ruby/2.6.0/gems/websocket-driver-0.7.0/lib/websocket/driver/draft76.rb" line 11 in initialize
"/app/vendor/bundle/ruby/2.6.0/gems/websocket-driver-0.7.0/lib/websocket/driver.rb" line 162 in new
"/app/vendor/bundle/ruby/2.6.0/gems/websocket-driver-0.7.0/lib/websocket/driver.rb" line 162 in rack
"/app/vendor/bundle/ruby/2.6.0/gems/actioncable-5.2.3/lib/action_cable/connection/client_socket.rb" line 47 in initialize
"/app/vendor/bundle/ruby/2.6.0/gems/actioncable-5.2.3/lib/action_cable/connection/web_socket.rb" line 10 in new
"/app/vendor/bundle/ruby/2.6.0/gems/actioncable-5.2.3/lib/action_cable/connection/web_socket.rb" line 10 in initialize
"/app/vendor/bundle/ruby/2.6.0/gems/actioncable-5.2.3/lib/action_cable/connection/base.rb" line 59 in new
"/app/vendor/bundle/ruby/2.6.0/gems/actioncable-5.2.3/lib/action_cable/connection/base.rb" line 59 in initialize
"/app/vendor/bundle/ruby/2.6.0/gems/actioncable-5.2.3/lib/action_cable/server/base.rb" line 30 in new
"/app/vendor/bundle/ruby/2.6.0/gems/actioncable-5.2.3/lib/action_cable/server/base.rb" line 30 in call
I still don't quite know why someone is sometimes returning a frozen
string (it could also have to with any of the current rack middleware
we're using), but I do know that we can handle it here.
So this adds a spec and some behavior to fix that.
When originally implemented, we still supported Ruby 1.8, which
necessitated checking for encoding methods and using a regex to validate
UTF-8. These checks are now gone.
We tagged many strings as binary when not strictly necessary, either
because we were just going to iterate their bytes or because we were
going to hand them off to the caller which should just write them
directly to a socket. Strings used as buffers to accumulate streaming
input are still tagged as binary to avoid encoding
collision/conversion.
The places where we do need to tag as UTF-8 (i.e. just before emitting
to the application) remain, but copy the string if necessary. This
allows us to work with frozen strings.
Finally, strings passed in via the Driver#text method should be
*transcoded* to UTF-8 if necessary, not merely tagged. The Ruby
String#encode method produces a new string so this should also be safe
with frozen strings.