なぜRackのResponse BodyはStringではなくArrayなのか
run lambda { |env| [ 200, {"Content-Type" => "text/plain"}, ["Hello World!"] ] }
この3つ目のbody。
PSGIでもそうだけど、なんでStringでも良さそうなのにArrayなのか。
ふと気になってググったら全く同じ内容がstachoverflowにあった。
ruby - Why is rack response body an array not a string? - Stack Overflow
元になってるWSGIから来てる理由と同じく、streamingに対応するためのようだ。
でっかいファイルを一気にメモリに読み込みたくないとか。
Stringだとどうしても1個だけという制限があるので、
イテレーションできるようにArrayになってるってことかな。
つまり
Rackの場合、3つ目のbodyは#eachを呼べて中身がStringならいいらしい。
ゆえに、
class Body def self.each yield "Hello World" end end run lambda { |env| [ 200, {"Content-Type" => "text/plain"}, Body ] }
こういうのでも動く。
Sinatra Streaming Responses
Sinatra: README
Sinatraでちょっとずつclientに送るサンプルを見る。
get '/' do stream do |out| out << "It's gonna be legen -\n" sleep 0.5 out << " (wait for it) \n" sleep 1 out << "- dary!\n" end end
実装としては3つ目のbodyのところにSinatra::Helpers::Streamオブジェクトがくる。
バージョンは1.4.3
sinatra/base.rb
class Stream def each(&front) @front = front @scheduler.defer do begin @back.call(self) rescue Exception => e @scheduler.schedule { raise e } end close unless @keep_open end end def <<(data) @scheduler.schedule { @front.call(data.to_s) } self end end
@schedulerはenv["async.callback"]に何か入ってたらEMが使われるようになってた。
入ってなかったらダミーが動く。
WEBrickで動かなくてthinで動くのはたぶんその関係。
なるほどなー。