Subject: PUSH Clarifications

From: James M Snell <[email-masked]>
Date: Mon, 5 Aug 2013 10:21:44 -0700

Saw the comments in the github repo issues regarding PUSH and safe
methods discussed in Hamburg... I gave some comments over there, but
given that it affects technical design and not just editorial, I
wanted to echo those same comments here on list...

I'm not 100% sure where the conversation ended up in Hamburg, but.. in
my opinion.. a PUSH...

1) Ought to ALWAYS be either an implied GET or HEAD, sending a
PUSH_PROMISE with a :method header field that specifies anything other
than GET or HEAD ought to be a stream error. This keeps things as
simple as possible without forcing us to get into dealing with
possibly weird edge cases caused by unknown extension methods.

2) Ought to only be an implied HEAD request if the originating request
is also a HEAD request. Otherwise, the PUSH is always a GET.

3) Ought to only be sent when the response has a 2xx status code. It
does not make much sense at all to send a PUSH when the status code is
[3-5]xx.

4) Ought to have an implicit or explicit Origin equal to the origin of
the pushed implied GET's effective request URI...

For instance, if a client sends..

  :method = GET
  :path = /foo
  :host = example.org:80
  origin = foo.example.com:43

The server can send a PUSH_PROMISE like...

  :method = GET
  :path = /foo/images/1.jpg
  :host = example.org:80
  origin = example.org:80

(in other words, the push does not inherit the origin of the
originating request...)

One thing we ought to consider: some mechanism a user-agent can use to
tell the server it is willing to accept pushed content from other
origins.  For instance, imagine sending the following *request*:

  :method = GET
  :path = /foo
  :host = example.org:80
  origin = foo.example.com:43
  allow-push-origin: abc.example.net:80, example.org:80

Which states: For this stream, I'll accept pushed resources from
abc.example.net:80 or example.org:80 and I'll RST_STREAM any attempt
to push resources from another origin.

(Obviously... where this gets a bit weird, however, is the fact that
PUSH is hop-by-hop, which would make allow-push-origin necessarily
hop-by-hop...)

---

From: James M Snell <[email-masked]>
Date: Mon, 5 Aug 2013 10:30:47 -0700

On Mon, Aug 5, 2013 at 10:21 AM, James M Snell <[email-masked]> wrote:
>[snip]

Sorry, I meant this to say "Ought to have an implicit or explicit
Origin equal to the origin of the *originating requests* effective
request URI... To give a more complete example so it's clear:

I could send...

   :method = GET
   :path = /foo
   :host = example.org:80

   :method = GET
   :path = /foo/images/1.jpg
   :host = abc.example.net:80
   origin = example.org:80

- James

---

From: Julian Reschke <[email-masked]>
Date: Mon, 05 Aug 2013 23:13:12 +0200

> possibly weird edge cases caused by unknown extension methods.

Such as?

> is also a HEAD request. Otherwise, the PUSH is always a GET.

Why?

> does not make much sense at all to send a PUSH when the status code is

I think we discussed and liked an idea of HEAD->304 to update cache meta 
data.

> ...

Best regards, Julian

---

From: Robert Collins <[email-masked]>
Date: Tue, 6 Aug 2013 09:34:21 +1200

...
> I think we discussed and liked an idea of HEAD->304 to update cache meta

Exactly! consider conditional gets: if the UA sends a GET I-M-S or a
GET I-N-M then the server can infer likely cached resources and PUSH a
HEAD->304for those resources the client probably has, and a GET->200
for those that are definitely new.

So I think originating GET leading to PUSH of GET and HEAD makes a lot of sense.

-Rob

---

From: James M Snell <[email-masked]>
Date: Mon, 5 Aug 2013 14:38:07 -0700

>> possibly weird edge cases caused by unknown extension methods.

1) Let's say I create two new http methods FOO and BAR. FOO is not
safe and is not idempotent. BAR, however, is safe and idempotent. How
is does an implementation that is unfamiliar with FOO or BAR determine
whether they are safe to PUSH (or safe to accept from as pushed
resources?).

2) What does it mean when a server sends:

  a. PUSH_PROMISE(:method=DELETE) ?
  b. PUSH_PROMISE(:method=PATCH) ?
  c. PUSH_PROMISE(:method=PUT) ?

If PUSH_PROMISE is viewed primarily as a means of preemptively
delivering content that the server expects the user-agent to request
(which is the key use case presented thus far), then it makes sense
that pushed resources are always only implied GET or HEAD requests.
None of the other methods make any sense.

>> is also a HEAD request. Otherwise, the PUSH is always a GET.

See above.

Further, if I send a HEAD request, then the assumption ought to be: I
only want metadata back, not content, pushed resources ought to also
consist only of metadata and not content...

> I think we discussed and liked an idea of HEAD->304 to update cache meta

That's not what I mean. We have the originating request and response,
and N pushed resources. If the response to the originating request is
4xx/5xx there is exceedingly little justification for sending any
pushed resources. The response codes used for each of the pushed
resources is a separate matter.

That said, thinking about it further... in certain situations, a case
could certainly be made for sending a push resource along with a 3xx
response so I'm not 100% against that.. For instance, I could send a
3xx response and immediately push the resource I'm redirecting you
too...

HEADERS
  :status = 302
  location = /images/jpg

PUSH_PROMISE
  :method = GET
  :path = /images/jpg

...

This would actually be a fairly interesting use of pushed resources so
will have to give more thought to that one.

- James

---

From: James M Snell <[email-masked]>
Date: Mon, 5 Aug 2013 14:48:14 -0700

> for those that are definitely new.

Or the server simply chooses not to push the resources that haven't
been modified. If the user-agent has them, it uses them, if it
doesn't, it requests them again.

Also, remember that the push is a server implied request.. that is,
the server can assume that a GET request will be coming in so it
crafts a set of request headers followed by the response. The
following, then, would be identical to your suggestion:

PUSH_PROMISE
  Promised Stream ID: 2
  :method = GET
  :path = /images/f.jpg
  :if-modified-since: 2013-01-01T12:12:12Z

HEADERS
  Stream ID: 2
  :status = 304
  last-modified: 2013-01-01T12:12:12Z

This would still lead to the cache metadata being updated while
holding to the suggested Implied GET rule.

---

From: Julian Reschke <[email-masked]>
Date: Mon, 05 Aug 2013 23:56:44 +0200

> whether they are safe to PUSH (or safe to accept from as pushed

They check in the method registry. (No, not at runtime)

>    c. PUSH_PROMISE(:method=PUT) ?

Those are not safe.

> None of the other methods make any sense.

How would you know without knowing the method semantics of all future 
methods?

> consist only of metadata and not content...

With PUSH, isn't it always about the server deferring what the client 
might do next? I agree that HEAD does not imply subsequent GET requests, 
but then why don't we let the server decide?

> resources is a separate matter.

Why not send a HEAD->404 to inform the client that a resource is gone?

Best regards, Julian

---

From: James M Snell <[email-masked]>
Date: Mon, 5 Aug 2013 15:15:32 -0700

[snip]
>>    c. PUSH_PROMISE(:method=PUT) ?

Oh right.. then how about PUSH_PROMISE(:method=FOO) ? ... I know,
gotta go check the registry because there is no reliable means of
determining if some method is safe at runtime...

The end result ends up being: the only methods that an implementation
can absolutely guarantee will work with server push are GET and HEAD
given that those are the only ones that we know for certain are
safe... so why not just make it clear up front: a push is always an
implied GET (or HEAD). (I can live with saying that a server can push
:method=HEAD if it wants... the biggest thing for me is that I want it
limited to only GET and HEAD).

> How would you know without knowing the method semantics of all future

(a) If and when such methods emerge and (b) if and when such methods
are implemented we can deal with the possible push ramifications at
that time. For now, GET and HEAD are the only ones that I can see that
make sense given the only use cases that have been put on the table.

> With PUSH, isn't it always about the server deferring what the client might
> do next? I agree that HEAD does not imply subsequent GET requests, but then
> why don't we let the server decide?
>

I'm not going to lie down in the road about it but, if the user-agent
wanted content, it would have sent a GET. I would consider it rather
obnoxious of the server to preemptively send something that I might
have had absolutely no intention to request or use... But, like I said
above, I can live with allowing the server to push HEAD or GET so long
as we limit it to those.

> Why not send a HEAD->404 to inform the client that a resource is gone?

Like I said, the :status sent for the push resources is completely
independent of the :status sent for the originating stream. I'm only
talking about the status of the originating stream here. Sure, in
theory, if I GET foo.html that used to link to foo.jpg and receive a
404 saying foo.html is gone, it *might* be marginally useful to also
receive a 404 saying that foo.jpg is also gone, but the utility of
that is tenuous at best (imho of course). (Again, I'm not going to lie
down in the road about it.. I'm just sharing my current opinion on
it.)

- James

---

From: Mike Bishop <[email-masked]>
Date: Mon, 5 Aug 2013 23:18:09 +0000

For the #2 discussion, you're wandering into extension territory.  If we're dealing with unextended HTTP, HEAD and GET are the only safe methods, so the statements you're comparing are equivalent.  If there's an extension which defines a new HTTP method, there needs to be a means of determining that both sides of the exchange support that extension before attempting to use it.  If both sides have agreed that a new method FOO exists and is safe, then it should logically be pushable.  If only the server knows about it, then obviously the client wouldn't understand seeing it in a PUSH_PROMISE.

However, receiving the PUSH_PROMISE shouldn't be the first time the existence and use of FOO is disclosed; this gets into the question of how clients and servers negotiate the use of extensions which may add new methods, frame types, etc.  Your respective arguments presuppose some answers to that question which hasn't been seriously discussed yet.

-----Original Message-----
From: James M Snell [mailto:[email-masked]] 
Sent: Monday, August 5, 2013 3:16 PM
To: Julian Reschke
Cc: [email-masked]
Subject: Re: PUSH Clarifications

[snip]
>> is does an implementation that is unfamiliar with FOO or BAR 
>> determine whether they are safe to PUSH (or safe to accept from as 
>> pushed resources?).
>
>

Oh right.. then how about PUSH_PROMISE(:method=FOO) ? ... I know, gotta go check the registry because there is no reliable means of determining if some method is safe at runtime...

The end result ends up being: the only methods that an implementation can absolutely guarantee will work with server push are GET and HEAD given that those are the only ones that we know for certain are safe... so why not just make it clear up front: a push is always an implied GET (or HEAD). (I can live with saying that a server can push :method=HEAD if it wants... the biggest thing for me is that I want it limited to only GET and HEAD).

> How would you know without knowing the method semantics of all future 

(a) If and when such methods emerge and (b) if and when such methods are implemented we can deal with the possible push ramifications at that time. For now, GET and HEAD are the only ones that I can see that make sense given the only use cases that have been put on the table.

>
>>>> 2) Ought to only be an implied HEAD request if the originating 
>>>> request is also a HEAD request. Otherwise, the PUSH is always a GET.
>>>
>>>
>>>
>>> Why?
>>>
>>
>> See above.
>>
> might do next? I agree that HEAD does not imply subsequent GET 
> requests, but then why don't we let the server decide?
>

I'm not going to lie down in the road about it but, if the user-agent wanted content, it would have sent a GET. I would consider it rather obnoxious of the server to preemptively send something that I might have had absolutely no intention to request or use... But, like I said above, I can live with allowing the server to push HEAD or GET so long as we limit it to those.

>
>>>> 3) Ought to only be sent when the response has a 2xx status code. 
>>>> It does not make much sense at all to send a PUSH when the status 
>>>> code is [3-5]xx.
>>>
>>>
>>>
>>> I think we discussed and liked an idea of HEAD->304 to update cache 
>>> meta data.
>>>
>>

Like I said, the :status sent for the push resources is completely independent of the :status sent for the originating stream. I'm only talking about the status of the originating stream here. Sure, in theory, if I GET foo.html that used to link to foo.jpg and receive a
404 saying foo.html is gone, it *might* be marginally useful to also receive a 404 saying that foo.jpg is also gone, but the utility of that is tenuous at best (imho of course). (Again, I'm not going to lie down in the road about it.. I'm just sharing my current opinion on
it.)

- James

---

From: Julian Reschke <[email-masked]>
Date: Tue, 06 Aug 2013 07:51:37 +0200

> safe... so why not just make it clear up front: a push is always an

Wrong. 
<http://greenbytes.de/tech/webdav/draft-ietf-httpbis-method-registrations-11.html#updated.registry.contents>

> are implemented we can deal with the possible push ramifications at

So when FOO is defined and useful we need to update the HTTP/2.0 spec 
and change the *framing*? Sounds like a big failure to properly define 
an extension point to me.

> make sense given the only use cases that have been put on the table.

I hear you but I disagree.

Best regards, Julian

---

From: Martin Thomson <[email-masked]>
Date: Tue, 6 Aug 2013 16:02:54 +0200

> I hear you but I disagree.

I think that I agree with Julian here.  The sense I got from the
discussion was that we a) didn't want anything to be implicit (header
compression makes this close to free) and b) we don't want to preclude
uses that are OK in theory.

---

From: James M Snell <[email-masked]>
Date: Tue, 6 Aug 2013 09:07:00 -0700

> <http://greenbytes.de/tech/webdav/draft-ietf-httpbis-method-registrations-11.html#updated.registry.contents>

Well... yes, there are the PROPFIND, SEARCH and REPORT methods which
are both safe and idempotent... however, given that PUSH_PROMISE gives
us no means of sending an implied payload along with the implied
request headers, the utility of pushing those is almost comically
questionable... which brings us back to my original point: GET and
HEAD are the only ones that really make any sense given any of the
presented use cases for server push.

Martin says we don't want to preclude uses that are OK in theory... To
which I'd respond: Ok, give me even a theoretical use case that makes
sense. I'm not interested in "Someone, someday, might find some
theoretical, hypothetical case where pushing something other that GET
or HEAD might be marginally useful, maybe" type of arguments.

- James

> So when FOO is defined and useful we need to update the HTTP/2.0 spec and
> change the *framing*? Sounds like a big failure to properly define an
> extension point to me.
>
>

---

From: Julian Reschke <[email-masked]>
Date: Tue, 06 Aug 2013 22:57:37 +0200

> us no means of sending an implied payload along with the implied

Yes, that's what needs to get fixed.

Best regards, Julian

---

From: James M Snell <[email-masked]>
Date: Tue, 6 Aug 2013 14:03:15 -0700

On Tue, Aug 6, 2013 at 1:57 PM, Julian Reschke <[email-masked]> wrote:
[snip]
> Yes, that's what needs to get fixed.

I hear you but I disagree ;-)

Perhaps there is a non-theoretical, somewhat-plausible use-case that
cannot be addressed using GET/HEAD that I'm missing here? If not, lets
draw a line in the sand with GET/HEAD and move on.

- James

---

From: Julian Reschke <[email-masked]>
Date: Tue, 06 Aug 2013 23:25:11 +0200

> draw a line in the sand with GET/HEAD and move on.

We need to decide whether PUSH is limited to GET (/HEAD), or extensible. 
If it is supposed to work with other methods (and that's what the rough 
consensus in the interim meeting (*)) was, then we should prepare for 
safe methods with payloads no. If we do not, we'll need to rev the base 
protocol which seems to be a very bad idea.

Best regards, Julian

(*) to be confirmed on the mailing list

---

From: James M Snell <[email-masked]>
Date: Tue, 6 Aug 2013 15:27:52 -0700

In order to justify making push extensible, we ought to require someone to
put a semi-reasonable use case on the table.
 On Aug 6, 2013 2:37 PM, "Julian Reschke" <[email-masked]> wrote:

>>>>> <http://greenbytes.de/tech/**webdav/draft-ietf-httpbis-**
>>>>> method-registrations-11.html#**updated.registry.contents<http://greenbytes.de/tech/webdav/draft-ietf-httpbis-method-registrations-11.html#updated.registry.contents>
>>>>> >
>>>>>
>>>>>
> consensus in the interim meeting (*)) was, then we should prepare for safe
> methods with payloads no. If we do not, we'll need to rev the base protocol
> which seems to be a very bad idea.
>
> Best regards, Julian
>

---

From: Julian Reschke <[email-masked]>
Date: Wed, 07 Aug 2013 17:19:22 +0200

On 2013-08-07 00:27, James M Snell wrote:
> In order to justify making push extensible, we ought to require someone
> to put a semi-reasonable use case on the table.
> ...

Pushing a PROPFIND response because a WebDAV folder changed? (As opposed 
to polling for it).

I understand almost nobody likes WebDAV over here, but fact is that 
PROPFIND is properly registered, is safe, and takes a payload.

If we don't do it people will come up with clever or not-so clever ways 
to encode the payload into an header field (":payload" maybe). Is this 
really better?

Best regards, Julian