ちょっと悩んだのでメモ。
あるWebアプリがどんなパラメータの受け渡しをしてるのか覗いてやろうと思って WEBrick::HTTPProxyServer でプロキシサーバを作りました。その時レスポンスのボディ (WEBrick::HTTPResponse オブジェクト) がなんか Proc オブジェクトになってたので中身が覗けません…。で、こういうふうにやれば覗けましたというメモです。
だめな例
s = WEBrick::HTTPProxyServer.new(
Port: 8080,
ProxyContentHandler: ->(_req, res) {
puts res.body # ここが問題。これじゃ覗けない
})
Signal.trap('INT') do
s.shutdown
end
s.start
だめな例では #<Proc:0x00007f8a06977628 (省略)/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/webrick-1.7.0/lib/webrick/httpproxy.rb:344 (lambda)>
とか言う文字列が出力されます。
res.body の中身を見るとなんかパラメータを一個取り (xとします) bodyを保持しているバッファ (body_tmp: 実体は Array) からから読み出して x に write で書いていたので StringIO オブジェクトを渡してやればいいかなと思ってこうしました。
良い例
s = WEBrick::HTTPProxyServer.new(
Port: 8080,
ProxyContentHandler: ->(_req, res) {
sio = StringIO.new(String.new, 'r+')
res.body.(sio)
sio.rewind
res.body = sio.read
puts res.body
})
Signal.trap('INT') do
s.shutdown
end
s.start
これで body が見えました。res.body に読んだ結果を改めて代入しているのは、こうしないと UA 側に body が渡らないからです (body_tmp.shift で破壊的に読み出してるので残らない)。
あともう一つ、StringIO オブジェクトを作るときに '' などリテラルではなく String.new を渡している理由を説明します。今どき
# frozen_string_literal: true
を普通にやってると思うんですが、これだとリテラルで作った String が freeze されてるので書けないんですよ。ということで 'r+' モードで作ろうとしても permission denied になっちゃいます (よくできてる)。ということで freeze されてない String オブジェクトを得るのに手っ取り早い方法が String.new なのでした。