Rack::MockRequestとRack::Requestを使ってRack::Requestのbodyのrack.inputをいじっていて気づいたこと

こんにちは!kossyです!




今回はRack::MockRequestのenv_forメソッドとRack::Requestクラスを使って、Rack::Requestのbodyのrack.inputをいじっていて気づいたことがあったので、
備忘録としてブログに残してみたいと思います。




環境

Ruby 2.6.8
Rails 6.0.4
MacOS BigSur




Rack::MockRequestを使ってenvを生成する

rack gemにはデフォルトで Rack::MockRequest というクラスが定義されています。

www.rubydoc.info

github.com

Rack::MockRequestのenv_forメソッドを使えばenvオブジェクトを生成することができるので、まずはこちらを試してみます。

# rails c

$ env = Rack::MockRequest.env_for("/", {})
=> {"rack.version"=>[1, 3],
 "rack.input"=>#<StringIO:0x000055e41ae646b0>,
 "rack.errors"=>#<StringIO:0x000055e41ae64750>,
 "rack.multithread"=>true,
 "rack.multiprocess"=>true,
 "rack.run_once"=>false,
 "REQUEST_METHOD"=>"GET",
 "SERVER_NAME"=>"example.org",
 "SERVER_PORT"=>"80",
 "QUERY_STRING"=>"",
 "PATH_INFO"=>"/",
 "rack.url_scheme"=>"http",
 "HTTPS"=>"off",
 "SCRIPT_NAME"=>"",
 "CONTENT_LENGTH"=>"0"}

envのrack.inputに日本語の文字列を渡してみます。

$ env["rack.input"].string
=> ""

$ env.merge!(::Rack::RACK_INPUT => StringIO.new("その辺にいるWebエンジニアの備忘録"))

$ env["rack.input"].string
=> "その辺にいるWebエンジニアの備忘録"

env["rack.input"]に日本語の文字列を入れることができました。


Rack::Requestのbodyの日本語文字列をreadで読み出してみる

github.com

次に、Rack::Requestインスタンスを生成して、先ほど渡した日本語文字列をreadメソッドで読み出してみます。

$ request = Rack::Request.new(env)

# envで文字列を参照できる
$ request.env['rack.input'].string
=> "その辺にいるWebエンジニアの備忘録"

# bodyはStringIOクラスのインスタンスが格納されている
$ request.body
=> #<StringIO:0x000055e41e96a8b8>

# 4096はバイト数
$ data = request.body.read(4096)
=> "\xE3\x81\x9D\xE3\x81\xAE\xE8\xBE\xBA\xE3\x81\xAB\xE3\x81\x84\xE3\x82\x8BWeb\xE3\x82\xA8\xE3\x83\xB3\xE3\x82\xB8\xE3\x83\x8B\xE3\x82\xA2\xE3\x81\xAE\xE5\x82\x99\xE5\xBF\x98\xE9\x8C\xB2"

非ASCII文字はエスケープされていますね。この文字列をJSON.generateしようとすると以下のエラーが発生します。

$ JSON.generate(data)
=> Encoding::UndefinedConversionError: "\xE3" from ASCII-8BIT to UTF-8

同様の問題がsentry-rubyというGemで発生していたようです。

github.com

修正されたPRでは、非ASCII文字をUTF-8エンコーディングしていました。

$ encoded_data = data.force_encoding(Encoding::UTF_8)
=> "その辺にいるWebエンジニアの備忘録"

rack.inputに非ASCII文字を渡してreadでバイト指定して読み出す時は気をつけるようにしましょう。。。


まとめ

Rack便利!と思う反面きちんと挙動を理解しないと思わぬところでハマってしまうので、

完全にブラックボックスな状態で使うのはやはり怖いと思いました。

Gemのコード読んでおいてよかった、、、