Reputation: 95252
Working on a web service that sometimes needs to return large files, and want it to send something to the client quickly so the client doesn't time out waiting for the start of the data. stream
seemed perfect for this, but I ran into a problem.
Dumb example:
get '/path' do
status 200
headers 'Content-Type' => 'text/plain'
stream do |out|
sleep 1
out << "Hello,\n"
sleep 1
out << "World!\n"
end
end
This works fine:
$ curl http://localhost:4567/path
Hello,
World!
But I have a side log that the service writes to, and trying to mix File I/O with the streaming API doesn't work at all:
get '/path' do
status 200
headers 'Content-Type' => 'text/plain'
File.open '/tmp/side-log', 'a' do |lf|
stream do |out|
lf.puts "Woo!"
sleep 1
out << "Hello,\n"
sleep 1
out << "World!\n"
end
end
end
Now I get this:
$ curl http://localhost:4567/path
curl: (18) transfer closed with outstanding read data remaining
Puma doesn't indicate any problems on the server side, but Thin exits entirely:
hello2.rb:13:in `write': closed stream (IOError)
from hello2.rb:13:in `puts'
from hello2.rb:13:in `block (3 levels) in <main>'
from vendor/bundle/gems/sinatra-1.4.6/lib/sinatra/base.rb:437:in `block (2 levels) in stream'
from vendor/bundle/gems/sinatra-1.4.6/lib/sinatra/base.rb:628:in `with_params'
from vendor/bundle/gems/sinatra-1.4.6/lib/sinatra/base.rb:437:in `block in stream'
from vendor/bundle/gems/sinatra-1.4.6/lib/sinatra/base.rb:403:in `call'
from vendor/bundle/gems/sinatra-1.4.6/lib/sinatra/base.rb:403:in `block in each'
from vendor/bundle/gems/eventmachine-1.0.8/lib/eventmachine.rb:1062:in `call'
from vendor/bundle/gems/eventmachine-1.0.8/lib/eventmachine.rb:1062:in `block in spawn_threadpool'
[1]+ Exit 1 ruby hello2.rb
So what should I do if I want to write something out to someplace other than the output stream from inside the stream block?
Upvotes: 2
Views: 631
Reputation: 95252
Not sure if this is the best solution, but using the asynchronous em-files
gem worked for me, even in Puma (which I understand is not EventMachine-based):
require 'em-files'
get '/path' do
status 200
headers 'Content-Type' => 'text/plain'
EM::File.open '/tmp/side-log', 'a' do |lf|
stream do |out|
lf.write "Woo!\n"
sleep 1
out << "Hello,\n"
sleep 1
out << "World!\n"
end
end
end
Upvotes: 2