rack-devel archive mirror (unofficial) https://groups.google.com/group/rack-devel
 help / color / mirror / Atom feed
* streaming api
@ 2010-02-03  4:47 Dominic Sisneros
  2010-02-03 10:59 ` Eric Wong
                   ` (2 more replies)
  0 siblings, 3 replies; 11+ messages in thread
From: Dominic Sisneros @ 2010-02-03  4:47 UTC (permalink / raw)
  To: rack-devel

[-- Attachment #1: Type: text/plain, Size: 1484 bytes --]

The perl folks jumped on the rack & wsgi trend in a big way with psgi and
are doing a lot of interesting stuff.

One of the things that they have done differently is have an optional
streaming interface.

http://bulknews.typepad.com/blog/2009/10/psgiplack-streaming-is-now-complete.html

So, basically the idea is the same as the original Python WSGI's
start_response but this callback is NOT an optional parameter to the app
because that stands in the way of everybody in the chain including
middleware and that sucks. Instead, an app can optionally return a callback
that accepts another callback to which you can return the response array ref
(code, headers and body) if you want to delay your response.

my $app = sub {
  my $env = shift;
  return sub {
    my $respond = shift;
    # do some event stuff
    $event->callback(sub { $respod->([ $code, $headers, $body ]) });
  };
};

If you also want to delay the content body delivery as well (i.e. streaming)
you can omit the body, in which case you'll get the writer object that has
write(), close() and poll_cb().

my $app = sub {
  my $env = shift;
  return sub {
    my $respond = shift;
    my $w = $respond->([ 200, [ 'Content-Type' => 'text/plain' ]]); #
no $body here
    # do more event stuff
    $event->callback(sub { $w->write($body) });
    $event->done_callback(sub { $w->close });
  };
};


That post also has links to other posts about their streaming rack like
interface

Should rack pursue something like this?

[-- Attachment #2: Type: text/html, Size: 1783 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: streaming api
  2010-02-03  4:47 streaming api Dominic Sisneros
@ 2010-02-03 10:59 ` Eric Wong
  2010-02-03 18:14   ` Randy Fischer
  2010-02-03 11:02 ` James Tucker
  2010-02-03 11:25 ` Tom Robinson
  2 siblings, 1 reply; 11+ messages in thread
From: Eric Wong @ 2010-02-03 10:59 UTC (permalink / raw)
  To: rack-devel

Dominic Sisneros <dsisnero@gmail.com> wrote:
> The perl folks jumped on the rack & wsgi trend in a big way with psgi and
> are doing a lot of interesting stuff.
> 
> One of the things that they have done differently is have an optional
> streaming interface.
> 
> http://bulknews.typepad.com/blog/2009/10/psgiplack-streaming-is-now-complete.html
> 
> So, basically the idea is the same as the original Python WSGI's
> start_response but this callback is NOT an optional parameter to the app
> because that stands in the way of everybody in the chain including
> middleware and that sucks. Instead, an app can optionally return a callback
> that accepts another callback to which you can return the response array ref
> (code, headers and body) if you want to delay your response.
> 
> my $app = sub {
>   my $env = shift;
>   return sub {
>     my $respond = shift;
>     # do some event stuff
>     $event->callback(sub { $respod->([ $code, $headers, $body ]) });
>   };
> };
> 
> If you also want to delay the content body delivery as well (i.e. streaming)
> you can omit the body, in which case you'll get the writer object that has
> write(), close() and poll_cb().
> 
> my $app = sub {
>   my $env = shift;
>   return sub {
>     my $respond = shift;
>     my $w = $respond->([ 200, [ 'Content-Type' => 'text/plain' ]]); #
> no $body here
>     # do more event stuff
>     $event->callback(sub { $w->write($body) });
>     $event->done_callback(sub { $w->close });
>   };
> };
> 
> 
> That post also has links to other posts about their streaming rack like
> interface
> 
> Should rack pursue something like this?

Hi Dominic,

Rack already lets you stream the body.  The body response only has to
respond to #each and the #each call can delay as much as it wants.  Ruby
1.8 has (somewhat) cheap threads, and 1.9 has even cheaper Fibers so
you can sleep synchronously inside them.

Additionally, James Tucker made some non-standard Rack extensions in
Thin (using EventMachine) that let you use EventMachine callbacks
similar to the Perl examples above.  Take a look at the examples
packaged with Thin and their use of env["async.callback"] and also the
async_sinatra gem.  James had plans to start working on a Rack 2.x based
entirely around asynchronous code, but (afaik) hasn't gotten to
publishing anything yet.

The Ebb server also has env["async.callback"] support using Rev instead
of EM.  However, Ebb+Rev has far less traction than Thin+EM.
async_sinatra relies on EventMachine and can't use Rev at the moment.

There's also Rainbows! (and Zbatery) which may use EventMachine and
should[1] emulate async extensions found in Thin.


IMHO, Ruby 1.9 Fibers are a "close enough" concurrency solution for Ruby
and already fit the Rack 1.x specs without modifications.  While Fibers
will always be more expensive than async models, I can live with that
given the application is already implemented in Ruby...


[1] - I'm the author of Rainbows! and Zbatery, but I don't know of
anybody actively using EventMachine with them.  Please let us know if
you find anything broken.  I have only had some small demos running
Revactor and some of the Fiber-based concurrency models.  The only two
production sites I've ever heard of using Rainbows! were running the
ThreadSpawn concurrency model.

-- 
Eric Wong

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: streaming api
  2010-02-03  4:47 streaming api Dominic Sisneros
  2010-02-03 10:59 ` Eric Wong
@ 2010-02-03 11:02 ` James Tucker
  2010-02-03 18:08   ` Jeremy Hinegardner
  2010-02-03 11:25 ` Tom Robinson
  2 siblings, 1 reply; 11+ messages in thread
From: James Tucker @ 2010-02-03 11:02 UTC (permalink / raw)
  To: rack-devel


[-- Attachment #1.1: Type: text/plain, Size: 1709 bytes --]


On 3 Feb 2010, at 04:47, Dominic Sisneros wrote:

> The perl folks jumped on the rack & wsgi trend in a big way with psgi and are doing a lot of interesting stuff.
> 
> One of the things that they have done differently is have an optional streaming interface.
> 
> http://bulknews.typepad.com/blog/2009/10/psgiplack-streaming-is-now-complete.html
> 
> So, basically the idea is the same as the original Python WSGI's start_response but this callback is NOT an optional parameter to the app because that stands in the way of everybody in the chain including middleware and that sucks. Instead, an app can optionally return a callback that accepts another callback to which you can return the response array ref (code, headers and body) if you want to delay your response.
> 
> my $app = sub {
>   my $env = shift;
>   return sub {
>     my $respond = shift;
>     # do some event stuff
>     $event->callback(sub { $respod->([ $code, $headers, $body ]) });
>   };
> };
> If you also want to delay the content body delivery as well (i.e. streaming) you can omit the body, in which case you'll get the writer object that has write(), close() and poll_cb(). 
> 
> my $app = sub {
>   my $env = shift;
>   return sub {
>     my $respond = shift;
>     my $w = $respond->([ 200, [ 'Content-Type' => 'text/plain' ]]); # no $body here
>     # do more event stuff
> 
>     $event->callback(sub { $w->write($body) });
>     $event->done_callback(sub { $w->close });
>   };
> };
> 
> That post also has links to other posts about their streaming rack like interface
> 
> Should rack pursue something like this?


Yes this looks disturbingly familiar to my thin patches from 1.2


[-- Attachment #1.2: Type: text/html, Size: 2349 bytes --]

[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 3679 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: streaming api
  2010-02-03  4:47 streaming api Dominic Sisneros
  2010-02-03 10:59 ` Eric Wong
  2010-02-03 11:02 ` James Tucker
@ 2010-02-03 11:25 ` Tom Robinson
  2 siblings, 0 replies; 11+ messages in thread
From: Tom Robinson @ 2010-02-03 11:25 UTC (permalink / raw)
  To: rack-devel

[-- Attachment #1: Type: text/plain, Size: 1159 bytes --]

On Feb 2, 2010, at 8:47 PM, Dominic Sisneros wrote:

> The perl folks jumped on the rack & wsgi trend in a big way with psgi and are doing a lot of interesting stuff.
> 
> One of the things that they have done differently is have an optional streaming interface.
> 
> http://bulknews.typepad.com/blog/2009/10/psgiplack-streaming-is-now-complete.html
> 
> So, basically the idea is the same as the original Python WSGI's start_response but this callback is NOT an optional parameter to the app because that stands in the way of everybody in the chain including middleware and that sucks. Instead, an app can optionally return a callback that accepts another callback to which you can return the response array ref (code, headers and body) if you want to delay your response.
> 
FWIW, JSGI (Rack/WSGI for JavaScript) is investigating/debating using promises for asynchronous / streaming. Here's a comparison of two versions of "AJSGI" to PSGI's approach (with your PSGI examples ported to JavaScript):

http://gist.github.com/293552

Lots of discussion going on in the CommonJS mailing list too BTW: http://groups.google.com/group/commonjs

-tom

[-- Attachment #2: Type: text/html, Size: 1676 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: streaming api
  2010-02-03 11:02 ` James Tucker
@ 2010-02-03 18:08   ` Jeremy Hinegardner
  0 siblings, 0 replies; 11+ messages in thread
From: Jeremy Hinegardner @ 2010-02-03 18:08 UTC (permalink / raw)
  To: rack-devel

On Wed, Feb 03, 2010 at 11:02:33AM +0000, James Tucker wrote:
> 
> On 3 Feb 2010, at 04:47, Dominic Sisneros wrote:
> 
> > The perl folks jumped on the rack & wsgi trend in a big way with psgi and are doing a lot of interesting stuff.
> > 
> > One of the things that they have done differently is have an optional streaming interface.
> > 
> > http://bulknews.typepad.com/blog/2009/10/psgiplack-streaming-is-now-complete.html
> > 
> > So, basically the idea is the same as the original Python WSGI's start_response but this callback is NOT an optional parameter to the app because that stands in the way of everybody in the chain including middleware and that sucks. Instead, an app can optionally return a callback that accepts another callback to which you can return the response array ref (code, headers and body) if you want to delay your response.
> > 
> > my $app = sub {
> >   my $env = shift;
> >   return sub {
> >     my $respond = shift;
> >     # do some event stuff
> >     $event->callback(sub { $respod->([ $code, $headers, $body ]) });
> >   };
> > };
> > If you also want to delay the content body delivery as well (i.e. streaming) you can omit the body, in which case you'll get the writer object that has write(), close() and poll_cb(). 
> > 
> > my $app = sub {
> >   my $env = shift;
> >   return sub {
> >     my $respond = shift;
> >     my $w = $respond->([ 200, [ 'Content-Type' => 'text/plain' ]]); # no $body here
> >     # do more event stuff
> > 
> >     $event->callback(sub { $w->write($body) });
> >     $event->done_callback(sub { $w->close });
> >   };
> > };
> > 
> > That post also has links to other posts about their streaming rack like interface
> > 
> > Should rack pursue something like this?
> 
> 
> Yes this looks disturbingly familiar to my thin patches from 1.2
> 

You might want to also check out Rack::StreamingProxy

http://github.com/aniero/rack-streaming-proxy

enjoy,

-jeremy


-- 
========================================================================
 Jeremy Hinegardner                              jeremy@hinegardner.org 

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: streaming api
  2010-02-03 10:59 ` Eric Wong
@ 2010-02-03 18:14   ` Randy Fischer
  2010-02-04 10:32     ` James Tucker
  2010-02-04 10:39     ` Eric Wong
  0 siblings, 2 replies; 11+ messages in thread
From: Randy Fischer @ 2010-02-03 18:14 UTC (permalink / raw)
  To: rack-devel

[-- Attachment #1: Type: text/plain, Size: 757 bytes --]

> Rack already lets you stream the body.  The body response only has to
> respond to #each and the #each call can delay as much as it wants.  Ruby
> 1.8 has (somewhat) cheap threads, and 1.9 has even cheaper Fibers so
> you can sleep synchronously inside them.
>
>
I'm more interested in getting streaming content on the request
side of things (go ahead and parse the headers,  then just give
me an IO object on the content).

Reason:  I get largish (> 4G) bodies occasionally in a particular
archival service;  I need to do md5 and sha1 and perhaps other
checksums soon.   I don't want the web server doing a copy
to some temp space, thanks, I'll write it and checksum it and
so on on the fly.

Anyone doing work on this at the rack level?

-Randy Fischer

[-- Attachment #2: Type: text/html, Size: 1027 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: streaming api
  2010-02-03 18:14   ` Randy Fischer
@ 2010-02-04 10:32     ` James Tucker
  2010-02-04 10:39     ` Eric Wong
  1 sibling, 0 replies; 11+ messages in thread
From: James Tucker @ 2010-02-04 10:32 UTC (permalink / raw)
  To: rack-devel


[-- Attachment #1.1: Type: text/plain, Size: 1020 bytes --]


On 3 Feb 2010, at 18:14, Randy Fischer wrote:

> 
> Rack already lets you stream the body.  The body response only has to
> respond to #each and the #each call can delay as much as it wants.  Ruby
> 1.8 has (somewhat) cheap threads, and 1.9 has even cheaper Fibers so
> you can sleep synchronously inside them.
> 
> 
> I'm more interested in getting streaming content on the request 
> side of things (go ahead and parse the headers,  then just give 
> me an IO object on the content).   
> 
> Reason:  I get largish (> 4G) bodies occasionally in a particular 
> archival service;  I need to do md5 and sha1 and perhaps other
> checksums soon.   I don't want the web server doing a copy
> to some temp space, thanks, I'll write it and checksum it and
> so on on the fly.
> 
> Anyone doing work on this at the rack level?

Not really no, as it's down to the webserver at this time, not down to rack, as rack expects a full formed request, most servers deliver just that.

> 
> -Randy Fischer


[-- Attachment #1.2: Type: text/html, Size: 1585 bytes --]

[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 3679 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: streaming api
  2010-02-03 18:14   ` Randy Fischer
  2010-02-04 10:32     ` James Tucker
@ 2010-02-04 10:39     ` Eric Wong
  2010-02-04 10:41       ` Eric Wong
  1 sibling, 1 reply; 11+ messages in thread
From: Eric Wong @ 2010-02-04 10:39 UTC (permalink / raw)
  To: rack-devel

Randy Fischer <randy.fischer@gmail.com> wrote:
> > Rack already lets you stream the body.  The body response only has to
> > respond to #each and the #each call can delay as much as it wants.  Ruby
> > 1.8 has (somewhat) cheap threads, and 1.9 has even cheaper Fibers so
> > you can sleep synchronously inside them.
> >
> >
> I'm more interested in getting streaming content on the request
> side of things (go ahead and parse the headers,  then just give
> me an IO object on the content).
> 
> Reason:  I get largish (> 4G) bodies occasionally in a particular
> archival service;  I need to do md5 and sha1 and perhaps other
> checksums soon.   I don't want the web server doing a copy
> to some temp space, thanks, I'll write it and checksum it and
> so on on the fly.
> 
> Anyone doing work on this at the rack level?

lib/rack/file.rb is an example of how to use the #each method
to stream a regular file off the filesystem:

    def each
      F.open(@path, "rb") { |file|
        while part = file.read(8192)
          yield part
        end
      }
    end

You should be easily able to adapt it to any IO object, not just File.
If you happen to be using Rainbows! with a Fiber-based concurrency
model, you can actually just wrap a Rainbows::Fiber::IO[1] object around
your as a response body:

I actually just started working on this earlier today for another
experiment:

# -*- encoding: binary -*-
# ResponseBody is not tied to Rainbows! and may be used with any
# IO-like class.  This is intended to be used as a body in a Rack
# response.
class ResponseBody < Struct.new(:upstream, :initial)

  def each(&block)
    buf = initial || ""
    yield(initial) unless buf.empty?

    while upstream.readpartial(16384, buf)
      yield buf
    end
  end

  def close
    upstream.close
  end
end

# This is very much tied to Rainbows!
class Upstream < Rainbows::Fiber::IO

  # non-blocking
  def initialize(addr = Socket.sockaddr_in(80, '192.168.0.1'))
    socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
    super(socket, ::Fiber.current)
    begin
      socket.connect_nonblock(addr)
    rescue Errno::EINPROGRESS
      wait_writable # schedules other Fibers while this waits
      retry
    rescue Errno::EISCONN
    end
  end
end

# Usage in the Rack app should be something like this:
lambda { |env|
  # open a socket to the upstream server, this may schedule other fibers
  upstream = Upstream.new(sockaddr)

  # send request, should be small enough to be non-blocking
  upstream.write("GET /foo HTTP/1.0\r\n\r\n")

  # wait for it, this can schedule and run other fibers while waiting
  buf = upstream.readpartial(16384)

  # TODO the parse_initial method used below is not implemented
  status, headers, initial_body = parse_initial(buf)

  [ status, headers, ResponseBody.new(upstream, initial_body) ]
}

[1] - http://git.bogomips.org/cgit/rainbows.git/tree/lib/rainbows/fiber/io.rb?id=v0.90

-- 
Eric Wong

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: streaming api
  2010-02-04 10:39     ` Eric Wong
@ 2010-02-04 10:41       ` Eric Wong
  2010-02-04 15:00         ` Tom Robinson
  0 siblings, 1 reply; 11+ messages in thread
From: Eric Wong @ 2010-02-04 10:41 UTC (permalink / raw)
  To: rack-devel

Eric Wong <normalperson@yhbt.net> wrote:
> Randy Fischer <randy.fischer@gmail.com> wrote:
> > > Rack already lets you stream the body.  The body response only has to
> > > respond to #each and the #each call can delay as much as it wants.  Ruby
> > > 1.8 has (somewhat) cheap threads, and 1.9 has even cheaper Fibers so
> > > you can sleep synchronously inside them.
> > >
> > I'm more interested in getting streaming content on the request
> > side of things (go ahead and parse the headers,  then just give
> > me an IO object on the content).

Nevermind, I didn't notice the "request side" of things, I need sleep :x

-- 
Eric Wong

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: streaming api
  2010-02-04 10:41       ` Eric Wong
@ 2010-02-04 15:00         ` Tom Robinson
  2010-02-04 16:34           ` James Tucker
  0 siblings, 1 reply; 11+ messages in thread
From: Tom Robinson @ 2010-02-04 15:00 UTC (permalink / raw)
  To: rack-devel


On Feb 4, 2010, at 2:41 AM, Eric Wong wrote:

> Eric Wong <normalperson@yhbt.net> wrote:
>> Randy Fischer <randy.fischer@gmail.com> wrote:
>>>> Rack already lets you stream the body.  The body response only has to
>>>> respond to #each and the #each call can delay as much as it wants.  Ruby
>>>> 1.8 has (somewhat) cheap threads, and 1.9 has even cheaper Fibers so
>>>> you can sleep synchronously inside them.
>>>> 
>>> I'm more interested in getting streaming content on the request
>>> side of things (go ahead and parse the headers,  then just give
>>> me an IO object on the content).
> 
> Nevermind, I didn't notice the "request side" of things, I need sleep :x

Also this PSGI interface allows for returning a response *asynchronously*. Rack does not allow for async responses (e.x. long polling) nor async streaming (e.x. streaming comet).

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: streaming api
  2010-02-04 15:00         ` Tom Robinson
@ 2010-02-04 16:34           ` James Tucker
  0 siblings, 0 replies; 11+ messages in thread
From: James Tucker @ 2010-02-04 16:34 UTC (permalink / raw)
  To: rack-devel


On 4 Feb 2010, at 15:00, Tom Robinson wrote:

> 
> On Feb 4, 2010, at 2:41 AM, Eric Wong wrote:
> 
>> Eric Wong <normalperson@yhbt.net> wrote:
>>> Randy Fischer <randy.fischer@gmail.com> wrote:
>>>>> Rack already lets you stream the body.  The body response only has to
>>>>> respond to #each and the #each call can delay as much as it wants.  Ruby
>>>>> 1.8 has (somewhat) cheap threads, and 1.9 has even cheaper Fibers so
>>>>> you can sleep synchronously inside them.
>>>>> 
>>>> I'm more interested in getting streaming content on the request
>>>> side of things (go ahead and parse the headers,  then just give
>>>> me an IO object on the content).
>> 
>> Nevermind, I didn't notice the "request side" of things, I need sleep :x
> 
> Also this PSGI interface allows for returning a response *asynchronously*. Rack does not allow for async responses (e.x. long polling) nor async streaming (e.x. streaming comet).

Actually rack /allows/ for streaming bodies in the api, although specific scheduling of the body rendering is restricted by middleware code and the servers approach to body#each.

That being said, the latter two do work /around/ racks api in Thin and rainbows, although writing middleware for this api is not so trivial.

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2010-02-04 16:34 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-02-03  4:47 streaming api Dominic Sisneros
2010-02-03 10:59 ` Eric Wong
2010-02-03 18:14   ` Randy Fischer
2010-02-04 10:32     ` James Tucker
2010-02-04 10:39     ` Eric Wong
2010-02-04 10:41       ` Eric Wong
2010-02-04 15:00         ` Tom Robinson
2010-02-04 16:34           ` James Tucker
2010-02-03 11:02 ` James Tucker
2010-02-03 18:08   ` Jeremy Hinegardner
2010-02-03 11:25 ` Tom Robinson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).