Subject: Alt-Svc alternative cache invalidation (ext#16)

From: Martin Thomson <[email-masked]>
Date: Mon, 18 Aug 2014 10:23:05 -0700

>From the issue:

In 3 The Alt-Svc HTTP Header Field, there's:

> When an Alt-Svc response header field is received from an origin, its value invalidates and  replaces all cached alternative services for that origin.

However, in several other places, we now say that multiple
alternatives can co-exist (with the client figuring out which to use).

Is this still our intent -- i.e., that the header field has a special
cache invalidation semantic -- or is it just left over from our
previous approach?

--

I think that this is residue from previous changes.  A small tweak can
fix it up.

The change regarding multiple services (i.e., clients choose which of
many it uses) is the right one.  However, that doesn't let a server
explicitly kill an Alt-Svc advertisement.  I think that this is useful
functionality.

I think that we should say that the tuple of (origin, service
protocol, service endpoint) is the key and that new advertisements
that match update the expiration time.

The only possible caveat is authentication of this information.  An
unsecured advertisement cannot override a value that has been set on a
secured channel.  Though thinking that through, I don't think that we
ever have a sometimes-secured/sometimes-not-secured origin in that
way.  There's an interaction with HTTP-TLS that we need to be careful
with.  I'll make a note.

---

From: Erik Nygren <[email-masked]>
Date: Mon, 18 Aug 2014 13:37:55 -0400

> that match update the expiration time.

Would it make more sense for (origin) to be the key with a set of (service
protocol, service endpoint) tuples being the value?  This allows a reset or
correction (or removal?) by publishing a new set of [(service protcol,
service endpoint), ...] values.

On a related note, when sketching out a server-side implementation it
became clear that it is hard to distinguish the separate use-cases
(alternate protocol, port, or endpoint) from the current "Alt-Svc-Used"
value.  This ends up being problematic in cases where a server could
receive Alt-Svc from either direction (from a separate endpoint or just a
protocol+port upgrade on the same endpoint).  One option which would help
here without causing privacy problems would be to make Alt-Svc-Used an
enumeration or bitmask rather than just a "0"/"1" value.

      Erik

---

From: Martin Thomson <[email-masked]>
Date: Mon, 18 Aug 2014 10:42:29 -0700

> service endpoint), ...] values.

The problem is that there are two different ways to update the set:

* The Alt-Svc header field bears multiple values and could be used in
the fashion you describe.

* The ALTSVC frame type bears just a single value.

The latter is the problem here.

---

From: Mark Nottingham <[email-masked]>
Date: Tue, 19 Aug 2014 17:24:53 +1000

> The latter is the problem here.

Yeah. We don't have a concept of replacement at all in the current spec. The header *could* have this semantic, but like Martin points out, the frame would be incapable of doing that (unless we had a 'clear cache for this origin' flag or something simliar).

Cheers,

--
Mark Nottingham   https://www.mnot.net/

---

From: Erik Nygren <[email-masked]>
Date: Tue, 19 Aug 2014 09:10:15 -0400

Ah, of course.  Having different data models between the header and the
frame
seems like it will be unfortunate and lead to more complicated
implementations.
If we're going to allow multiple Alt-Svc header targets, it seems like we'd
want
something similar for the frame, especially if we have full set replacement
semantics which seems potentially easier to reason about.

Could we alter the ALTSVC frame to allow for multiple targets within
the frame?  Or have a flag of "more ALTSVC follows" to allow a series
of the frames in-sequence for the same origin?  Either would bring
the two back in alignment.  The latter seems more annoying from
a state machine perspective.

       Erik

---

From: Julian Reschke <[email-masked]>
Date: Tue, 19 Aug 2014 15:20:02 +0200

On 2014-08-19 15:10, Erik Nygren wrote:
> If we're going to allow multiple Alt-Svc header targets, it seems like
> we'd want

That sounds right to me.

Best regards, Julian

---

From: Martin Thomson <[email-masked]>
Date: Tue, 19 Aug 2014 10:14:13 -0700

>> a state machine perspective.

I'm not certain about this.  More from an architectural standpoint
than anything else.  Conceptually, alternative services is build on
the idea that there are multiple potential sources of information.
We've defined two, but there are also potentially other avenues (DNS,
for instance).

This change would require that each of those sources knows about all
the alternatives.  That's a constraint on the alternative service
advertiser that seems unnecessary.  And it could become burdensome.
Erik's B record is a good example of where alternatives might appear
independently.

I don't think that this is annoying to implement at all.  Simply find
the entry that matches, create one if none exists, and update its time
to match.  It also keeps independent header field processing simpler.
You don't have to worry that another Alt-Svc header field might appear
in the block before you act.

---

From: Erik Nygren <[email-masked]>
Date: Tue, 19 Aug 2014 18:09:16 -0400

> We've defined two, but there are also potentially other avenues (DNS,

I'd actually been thinking that the record set model might actually more
consistent with multiple avenues of data.
For example, DNS records come in an RRset as a unit which could be added
fairly cleanly to the set received
from the origin.  In that world, (origin, data_source) would be the key,
such that each data source would
update its set.  This likely does bring the priority/ordering discussion
back into play regardless of how it is
approached (ie, which get priority beyond client choice).

> in the block before you act.

I'd think this would be painful for clients.  If a server emits a series
of ALTSVC frames with multiple choices, does the client buffer up
or wait some before it starts making connections?  With a set of options
it becomes much more clear when the client can start taking action
without needing to worry about thrashing.

Additionally, how does a server remove/replace an ALTSVC record it set
previously without waiting for a TTL expiration?  There isn't enough data
in Alt-Svc-Used for servers to know which values clients may have in their
cache so without set replacement it's unclear how a server would remove
or replace an entry.

       Erik

---

From: Martin Thomson <[email-masked]>
Date: Tue, 19 Aug 2014 16:09:33 -0700

On 19 August 2014 15:09, Erik Nygren <[email-masked]> wrote:
> With a set of options

I think that I can concede that point.  It will make the ALTSVC frame
uglier, but that's fine.

> previously without waiting for a TTL expiration?

That's only a problem if you forget about one.

---

From: Erik Nygren <[email-masked]>
Date: Fri, 22 Aug 2014 17:53:45 -0400

Taking a careful read-through again, I think the cleanest thing to do is to
change the ALTSVC frame structure to allow multiple alternatives.  Note
that the current text reads for the header:

   When an Alt-Svc response header field is received from an origin, its
value
   invalidates and replaces all cached alternative services for that origin.

but does not define anything similar for the ALTSVC frame.  Aligning the
frame and the
header would allow this to apply to both.

If we're changing the frame structure, I think it would be better to fold
the port and host into a single field.  Different non-TCP protocols may
have different ways of defining ports that may not necessarily be 16 bits.
To align them well, it also seems like it would make sense to have
Ext-Param in the frame as well
(which can be some to-be-defined-in-the-future string with the same format
as ext-param in Alt-Svc header)

For example:

  0                   1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                          Max-Age (32)                         |
 +---------------+---------------+-------------------------------+
 | Num-AltAuth(8)|
 +---------------+---------------+-------------------------------+
 | Proto-Len(8)  |        Protocol-ID (*)                        |
 +---------------+-----------------------------------------------+
 | AltAuth-Len(8)|        Alt-Auth (*)                         ...
 +---------------+-----------------------------------------------+
 | Origin-Len (8)|          Origin? (*)                        ...
 +---------------------------------------------------------------+
 |                        Ext-Param? (*)                       ...
 +---------------------------------------------------------------+

This would allow the same things to be expressed in the header and the
frame.

      Erik

---

From: Martin Thomson <[email-masked]>
Date: Fri, 22 Aug 2014 16:50:29 -0700

> header would allow this to apply to both.

I think that we would want to move the Origin field up to the header
with Max-Age.  Logically, you store alternatives for different origins
separately, so requiring different frames makes sense there.  It also
removes any potential for duplication.

Also 8 bits of length is not sufficient for an HTTP origin if the name
is maximum size.  I'd assume that the same applies to authority.

---

From: Erik Nygren <[email-masked]>
Date: Sat, 23 Aug 2014 21:30:06 -0400

> is maximum size.  I'd assume that the same applies to authority.

Agreed on both counts.  What about this, then:

  0                   1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                          Max-Age (32)                         |
 +---------------+---------------+-------------------------------+
 | Origin-Len (16)               |         Origin? (*)         ...
 +---------------------------------------------------------------+
 |Num-Alt-Auth(8)|
 +---------------+---------------+-------------------------------+
 | Proto-Len(8)  |        Protocol-ID (*)                        |
 +---------------+-----------------------------------------------+
 | Alt-Auth-Len (16)             |        Alt-Auth (*)         ...
 +---------------+-----------------------------------------------+
 |                        Ext-Param? (*)                       ...
 +---------------------------------------------------------------+

where Origin-Len=0 would be used in the case where this was part of a
Stream != 0
and Num-Alt-Auth>=1.  The {Proto-Len, Protocol-ID, Alt-Auth-Len, Alt-Auth}
would be
repeated Num-Alt-Auth times.  Alt-Auth is a string such as "
server.example.com:443"

---

From: Mark Nottingham <[email-masked]>
Date: Mon, 25 Aug 2014 10:30:40 +1000

So, to be clear, you're suggesting that both the Alt-Svc header field and the ALTSVC frame type have the side effect of cache invalidation?

Personally -- I'm not sure that's a good idea. 

For example, imagine a http:// service that a) wants to use Opp-Sec and b) the alternate wants to do some load balancing, etc.

The http:// service sets an Alt-Svc header field with a very long lifetime, so that Opp-Sec is as sticky as possible.

The alternate, OTOH, uses a fairly short lifetime for load balancing.

With cache invalidation, the alternate doing load balancing is going to clear the cache of the Opp-Sec hint, thereby forcing the client to go back to the http:// origin once the (short lifetime) load balancing policy expires.

Without invalidation, it'd fall back to the original Opp-Sec alternative.

Likewise for the SNI segmentation use case. 

Regards,

On 24 Aug 2014, at 11:30 am, Erik Nygren <[email-masked]> wrote:

> On Fri, Aug 22, 2014 at 7:50 PM, Martin Thomson <[email-masked]> wrote:
> where Origin-Len=0 would be used in the case where this was part of a Stream != 0
> and Num-Alt-Auth>=1.  The {Proto-Len, Protocol-ID, Alt-Auth-Len, Alt-Auth} would be
> repeated Num-Alt-Auth times.  Alt-Auth is a string such as "server.example.com:443"
> 
> 
> 
> 
> 
>