Streaming servlet responses proposal

22 views
Skip to first unread message

Mike Maul

unread,
Aug 6, 2008, 11:35:39 AM8/6/08
to liftweb
In certain cases it would be nice to be able to generate the servlet
response directly. Some of these cases are wrapping esternal API's
that require access to the servlet response printwriter and on the fly
image,pdf, non html/xml content-type generation.

While one could do this by implementing a servlet and a filter in the
container out side of lift, it would be easier, more consistent, and
cleaner to be able to keep as much of the application inside the lift
frame work as possible.

What I propose is adding an additional dispatch table in LiftRules
along with dispatch code in
LiftServlet.doService to dispatch the "streaming" servlet request.
Wiring the dispatch would take place in Boot.scala similar to the
existing lift responses. Code to implement the request would be placed
in a a directory called stream which would implement a new class
called lift stream LiftStream.

Another plus is having access to more "traditional" servlet like
responses would make it easier to port existing applications to lift.

I would be more than happy to implement this or anyother scheme that
provides similar functionality.

David Pollak

unread,
Aug 6, 2008, 11:46:22 AM8/6/08
to lif...@googlegroups.com
Mike,

Excellent suggestion.  I happen to be re-plumbing the Servlet-facing section of lift.  I'm going to add a Response that has an InputStream as the source of the bytes.

Thanks,

David
--
lift, the simply functional web framework http://liftweb.net
Collaborative Task Management http://much4.us
Follow me: http://twitter.com/dpp
Git some: http://github.com/dpp

David Pollak

unread,
Aug 6, 2008, 11:57:08 AM8/6/08
to lif...@googlegroups.com
I'll roll more of the changes out when I make the commit, but you'll be able to return a StreamingResponse from any method:

final case class StreamingResponse(data: InputStream, onEnd: () => Unit, size: Long,
                                   headers: List[(String, String)], cookies: List[Cookie],
                                   code: Int) extends ConvertableResponse with BasicResponse

You specify an InputStream, a size, and an onEnd function (which will probably close the stream).  The contents of the InputStream will be sent to the browser.

Does this work?

Thanks,

David

Marius

unread,
Aug 6, 2008, 12:10:39 PM8/6/08
to liftweb
Hi,

Just a side note ...
IMHO static content should be served separately.For instance a reverse
proxy can serve images etc. and dynamic content would be forwarded to
the lift application server. Anyways this is a common practice for
production environments as it reduces load for the application itself.

Br's,
Marius

Mike Maul

unread,
Aug 6, 2008, 12:44:22 PM8/6/08
to liftweb
It might work, but it would either require a BufferedInputStream, or a
PipedOutputStream<->PipedInputStream.
If a BufferedInputstream is used this would mean everything that is
going to get spit out is going to held in memory which would not be so
great if your generating a large ammount of content.
With PipedStreams the content generator would have to be running as a
seperate thread or as an actor.

On Aug 6, 11:57 am, David Pollak <d...@athena.com> wrote:
> I'll roll more of the changes out when I make the commit, but you'll be
> able to return a StreamingResponse from any method:
>
>     final case class StreamingResponse(data: InputStream, onEnd: () =>
>     Unit, size: Long,
>                                        headers: List[(String, String)],
>     cookies: List[Cookie],
>                                        code: Int) extends
>     ConvertableResponse with BasicResponse
>
> You specify an InputStream, a size, and an onEnd function (which will
> probably close the stream).  The contents of the InputStream will be
> sent to the browser.
>
> Does this work?
>
> Thanks,
>
> David
>
>
>
> David Pollak wrote:
> > Mike,
>
> > Excellent suggestion.  I happen to be re-plumbing the Servlet-facing
> > section of lift.  I'm going to add a Response that has an InputStream
> > as the source of the bytes.
>
> > Thanks,
>
> > David
>
> > On Wed, Aug 6, 2008 at 8:35 AM, Mike Maul <mike.m...@gmail.com
> > <mailto:mike.m...@gmail.com>> wrote:
>
> >     In certain cases it would be nice to be able to generate the servlet
> >     response directly. Some of these cases are wrapping esternal API's
> >     that require access to the servlet response printwriter and on the fly
> >     image,pdf, non html/xml content-type generation.
>
> >     While one could do this by implementing a servlet and a filter in the
> >     container out side of lift, it would be easier, more consistent, and
> >     cleaner to be able to keep as much of the application inside the lift
> >     frame work as possible.
>
> >     What I propose is adding an additional dispatch table in LiftRules
> >     along with dispatch code in
> >     LiftServlet.doService to dispatch the "streaming" servlet request.
> >     Wiring the dispatch would take place in Boot.scala similar to the
> >     existing lift responses. Code to implement the request would be placed
> >     in a a directory called stream which would implement a new class
> >     called lift stream LiftStream.
>
> >     Another plus is having access to more "traditional" servlet like
> >     responses would make it easier to port existing applications to lift.
>
> >     I would be more than happy to implement this or anyother scheme that
> >     provides similar functionality.
>
> > --
> > lift, the simply functional web frameworkhttp://liftweb.net
> > Collaborative Task Managementhttp://much4.us

David Pollak

unread,
Aug 6, 2008, 1:07:08 PM8/6/08
to lif...@googlegroups.com
On Wed, Aug 6, 2008 at 9:44 AM, Mike Maul <mike...@gmail.com> wrote:

It might work, but it would either require a BufferedInputStream, or a
PipedOutputStream<->PipedInputStream.
If a BufferedInputstream is used this would mean everything that is
going to get spit out is going to held in memory which would not be so
great if your generating a large ammount of content.
With PipedStreams the content generator would have to be running as a
seperate thread or as an actor.

Both of those subclass from InputStream.  I'm not going to enforce one or the other at the framework level.  A developer can choose the best for the given application.

Internally, the reads are batched at 8192 bytes each.
 



--
lift, the simply functional web framework http://liftweb.net
Collaborative Task Management http://much4.us

Mike Maul

unread,
Aug 6, 2008, 1:59:05 PM8/6/08
to liftweb
Not advocating a specific flavor of stream. Just going over how it
would work either you'd have to generate content prior to return of
response, or thread the generator and pass back the response stream.

On Aug 6, 1:07 pm, "David Pollak" <feeder.of.the.be...@gmail.com>
wrote:

David Pollak

unread,
Aug 6, 2008, 2:20:36 PM8/6/08
to lif...@googlegroups.com
On Wed, Aug 6, 2008 at 10:59 AM, Mike Maul <mike...@gmail.com> wrote:

Not advocating a specific flavor of stream. Just going over how it
would work either you'd have to generate content prior to return of
response, or thread the generator and pass back the response stream.

In lift, here's the code that accesses the InputStream:
        try {
          var len = 0
          val ba = new Array[Byte](8192)
          val os = response.getOutputStream
          len = stream.read(ba)
          while (len >= 0) {
            if (len > 0) os.write(ba, 0, len)
            len = stream.read(ba)
          }

        } finally {
          endFunc()
        }

It'll work great for files... it'll read them 8192 bytes at a time.  If you're using something else (like a PipeInputStream), it'll work fine as long as the generator is not on the same thread.

If I was getting all fancy-like, I could change InputStream into an existential type: {def read(dest: Array[Byte]): Int} so it'd work with InputStream as well as anything else that had a method with that signature... in fact... I think that's what I'm going to do.




--
lift, the simply functional web framework http://liftweb.net
Collaborative Task Management http://much4.us
Reply all
Reply to author
Forward
0 new messages