jsr369-experts@servlet-spec.java.net

[jsr369-experts] Feedback on Greg's December 2014 push API Thread

From: Edward Burns <edward.burns_at_oracle.com>
Date: Fri, 30 Jan 2015 13:20:19 -0800

>>>>> On Fri, 28 Nov 2014 16:27:11 +1100, Greg Wilkins <gregw_at_intalio.com> said:

GW> I've had a few more thoughts on a HTTP Push API that benefit from a little
GW> more experience and experimentation.

GW> One of the proposals discussed was for an API that looked something like:

GW> response.push(uri)

[...]

GW> The alternative API that I proposed was

GW> servletContext.getRequestDispatcher(uri).push(request)

[...]

GW> So this is a lot of mutating and filtering required to make a good pushed
GW> request and I'm sure there are complexities and interactions yet to be
GW> discovered that need to be handled.

[...]

GW> Thus I'm leaning back to a simpler API where just the uri is passed and the
GW> container is responsible for doing all these kinds of mutations. Perhaps
GW> some kind of optional additional headers could be passed in?

GW> Anyway, in short, I think this mechanism is going to be a lot more complex
GW> and tricky to get right than I originally thought.

This sort of back and forth is normal at this stage of the design
process. I think we need a more formal push proposal to pull this all
together. Perhaps a conference call as well.

>>>>> On Fri, 5 Dec 2014 11:52:01 +0100, Greg Wilkins <gregw_at_intalio.com> said:
GW> I've been working with Push a little bit more and have an alternative
GW> proposal for the API.

>>>>> On Mon, 8 Dec 2014 12:42:07 -0800, Edward Burns <edward.burns_at_oracle.com> said:

EB> Well, I'm sorry to see the simplicity of the existing API have to give
EB> way to something that seems to be starting to get quite complex.

GW> I've started work on this, but thought I'd seek some feedback before I
GW> commit too much effort. The idea is that often there will be many
GW> resources to push and that a lot of the work needed to work out headers,
GW> sessions and cookies is common to all the pushes. So we need a new
GW> PushBuilder instance that does all this work once. We would obtain a
GW> PushBuilder from a new method on the request:

GW> /** Get a PushBuilder associated with this request initialized as
GW> follows:<ul>
GW> * <li>The method is initialized to "GET"</li>

EB> If we obtain the PushBuilder from the current request, then why would
EB> the method not be taken on from the existing request?

GW> Because we cannot push POSTs and PUTs. We can push GETs and HEAD, maybe
GW> even OPTION and these can be pushed in association with a POST (can't think
GW> why a PUT would have push associates).

GW> So just trying to come up with a default that will work with minimal calls
GW> for most situations. Maybe it is initialised to the same method as the
GW> original request, unless that method is not supported by push, in which
GW> case it is initialised to GET.

>>>>> On Tue, 9 Dec 2014 19:13:25 -0500 (EST), Stuart Douglas <sdouglas_at_redhat.com> said:

SD> According to the spec I think it is just GET and HEAD, as the
SD> request must be both safe and cacheable, and OPTIONS is not
SD> cacheable. In practice GET is likely to be the only one that
SD> actually gets pushed.

SD> I think that in this case if the user attempts to set the method to
SD> one that we know is explicitly disallowed by the HTTP2 spec we
SD> should throw an IllegalArgumentException.

GW> The other option is to just support GET and HEAD and control which
GW> one with a setHeadRequest(boolean) method?

GW> That removes the issue of throwing IAE for the arbitrary methods
GW> that might get set on the push builder.

>>>>> On Wed, 10 Dec 2014 08:44:42 +0100, Greg Wilkins <gregw_at_intalio.com> said:

GW> There is a use-case for pushing HEAD requests. If the server is not sure
GW> of the state of the client cache, but is also not in possession of the etag
GW> or last modified date for a resource, then it can push a HEAD response to
GW> inform the cache of the current state of the resource.

>>>>> On Wed, 10 Dec 2014 19:16:27 +1100, Stuart Douglas <stuart.w.douglas_at_gmail.com> said:

GW> I think there is a good chance that over time people will extend the
GW> HTTP2 protocol in ways we can't really predict now, and limiting the
GW> push API to just two methods when the spec allows more seems short
GW> sighted.

GW> We have already been talking about way that we can take advantage of
GW> HTTP2 to replace some of our propietary protocols. For example you
GW> could use server push to push load information from a backend server
GW> to a load balancer, or you could use HTTP2 for EJB invocations, and
GW> use a custom verb to push the result of async invocations.

GW> I think we should throw an IAE basically to let users know they are
GW> doing it wrong and a compliant browser will reject the push, however
GW> we should not add any further restrictions.

There seems to be consensus that we should not bake assumptions about
the allowable methods that can be pushed into the API.

EB> [...]

GW> * <p>Each call to getPushBuilder() will return a new instance
GW> * of a PushBuilder based off this Request. Any mutations to the
GW> * returned PushBuilder are not reflected on future returns.
GW> * @return A new PushBuilder or null if push is not supported
GW> */
GW> public PushBuilder getPushBuilder()

EB> I prefer the isPushSupported() method. I think calling
EB> getPushBuilder() should throw IllegalStateException if push is not
EB> supported.

Using null to convey semantics is bad practice.

GW> Sounds like a converging consensus on that.

I'll take it where I can get it.

GW> void push(String uriInContext,String etag,long lastModified);

MT> s/long/Long/ else lastModified can't be null

MT> How about void push(String uriInContext) as a convenience method for
MT> when etag and lastmodified are both null?

[...]

MT> I like the general idea. A few comments in line based on a quick scan of
MT> the API. I expect I'll have more detailed feedback when I get to the
MT> point where I am implementing this.

>>>>> On Mon, 8 Dec 2014 08:26:49 +0100, Greg Wilkins <gregw_at_intalio.com> said:

GW> Yep, good idea... plus I am making a few other similar trims. The API is
GW> being updated as I play with it, so consider what I posted mostly as
GW> conceptual. I'll post an updated version in the next few days.

>>>>> On Tue, 9 Dec 2014 12:34:20 +0100, Greg Wilkins <gregw_at_intalio.com> said:

GW> I have incorporated some of the feedback received into Jetty's experimental
GW> implementation. You can see the PushBuilder (as a class rather than
GW> interface) here:

GW> http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/tree/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilder.java

GW> and the push methods on request are seen at
GW> http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/tree/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java#n218

GW> and a filter that utilizes the API is
GW> http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/tree/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushSessionCacheFilter.java

GW> I'm going to now gather some experience using this on our live site. I
GW> think further progress on making this the start of a proposed standard API
GW> will probably need to wait until other implementations catch up with
GW> using/implementing push.

Well, this was mid December. Can you share any developments since then?

>>>>> On Wed, 10 Dec 2014 05:42:20 -0500 (EST), Stuart Douglas <sdouglas_at_redhat.com> said:

SD> I updated https://http2.undertow.io to enable push today (with a
SD> nasty hack to work around IE's push support issues), using a similar
SD> kind of handler that learns over time.

That was fast.

SD> Something that did occur to me that there is a real possibility of
SD> different layers generating the same push request, e.g. if you have
SD> something like Greg's filter that learn's over time, and you have
SD> user code that pushes based on what it knows is going to be
SD> requested there is a good chance they will both generate the same
SD> push (this happened to me today, as my directory listing rendering
SD> code was already pushing the css).

SD> I'm not sure how to best deal with this, the obvious way is to just
SD> ignore a push for the same resource after the first has been sent,
SD> although in this case the later response will be from the
SD> application layer which should have more information. Maybe we
SD> should make implementations queue the pushes until headers are sent
SD> and then send the last push for each path?

It seems like we need a system where each desired push is given a hash
so that pushes for the same resource from different layers can be
subject to a "last one wins" or "first one wins" strategy.

>>>>> On Wed, 10 Dec 2014 11:53:24 +0100, Greg Wilkins <gregw_at_intalio.com> said:

GW> Stuart,
GW> I agree that having multiple push strategies operating at once is going to
GW> be a problem and I definitely agree that it is not obvious how to best deal
GW> with it.

GW> Queuing the push promise until the headers are sent may help, but then I
GW> can also imagine frameworks that will be generating content dynamically and
GW> will want to push resources as the html is generated, so the headers will
GW> be long gone. If we say the container is responsible for duplicate
GW> elimination, then such frameworks could be very lazy. I'm going to ponder
GW> on this one some more.

>>>>> On Wed, 10 Dec 2014 22:01:59 +1100, Stuart Douglas <stuart.w.douglas_at_gmail.com> said:

SD> My initial thoughts are to queue until header generation, preserving the
SD> most recent, send all queued at commit time, and after that send
SD> immediately and discard duplicates. There may well be some important use
SD> cases this is missing though, I have not really thought it through.

This is something we need to list as an open question. Let's call it
the "multiple duplicate push requset problem".

Ed

-- 
| edward.burns_at_oracle.com | office: +1 407 458 0017
| 26 days til DevNexus 2015
| 36 days til JavaLand 2015
| 46 days til CONFESS 2015