The example shows a one-shot request, wait, response cycle. But it is possible to have multiple wait, response segments to allow the headers to be sent to the client immediately, or the body to trickle in slowly without blocking the worker process.
Async Rack servers,
when env['async.callback']
is called, send the status and headers
to the client and then begin iterating through each part of the
body with #each
.
After the last body part the server must decide
if the connection to the client should be closed (entire body has
been provided) or if it should remain open (body parts will be
provided later). The details of this decision are implementation-specific.
For now, assume the connection is not closed. To send additional
body parts, env['async.callback']
may not be called a second time
since the status code and headers have already been sent to the
client and can not be changed. The app takes advantage of the
server's iteration through the body with #each:
the server calls
body.each(&block)
,
and the trick is to save &block
for later use.
This turns the iteration inside-out: rather than the sever iterating
through a body, the app takes control to send each part of the body
itself.
class DeferredBody def each(&block) # normally we'd yield each part of the body, but since # it isn't available yet, we save &block for later @server_block = block end def send(data) # calling the saved &block has the same effect as # if we had yielded to it @server_block.call data end end class AsyncApp def call(env) Thread.new do sleep 5 # simulate waiting for some event body = DeferredBody.new response = [200, {'Content-Type' => 'text/plain'}, body] env['async.callback'].call response # at this point, the server may send the status and headers, # but the body was empty body.send 'Hello, ' sleep 5 body.send 'World' end [-1, {}, []] # or throw :async end endNote that the above won't quite work because we haven't signaled to the server that the body will be deferred and streamed in part by part.
Casiano Rodriguez León 2015-01-07