Subject: [core] Non-traditional responses generalization

From: Christian Amsüss <[email-masked]>
Date: Wed, 13 May 2020 10:51:26 +0200

Hello Carsten,
hello Core,

for a more solid treatment of the suggestions I've presented at
IETF107[1] and in the new-block review[2], I'd like to suggest some text
chunks (below) for consideration in future work on non-traditional
responses. This generally assumes and extends on
I-D.bormann-core-responses-00.

Kind regards
Christian

Terminology
===========

A *non-traditional response* is a response that is not the single response
a server generates when it receives a request but is still responding to
the oriinal request (in terms of CoAP transports, this means that it has
the same token and is send between the reversed endpoint pair of the
request).

In particular, observations and responses to responses to multicast
requests are non-traditional according to the definition; they already
do follow the guidance set out here for new non-traditional responses.

(A second different response sent by a non-deduplicating server to a
retransmission of a request is not non-traditional because it is sent to
a repeated request -- that is probably the last corner case along the
distinction line).

An *obviously non-matching response* is a response that has properties
(typically options) that make it incompatible with the original request,
and thus especially unsuitable as a cached response to that request (but
possibly suitable as a cached response to a similar request). A response
can not be obviously non-matching if it only differs in safe-to-forward
options.

Preconditions to sending non-traditional responses
==================================================

A server may send multiple responses to a request if there is any
property in the request that indicates the client's intention to receive
them. This is typically indicated by a request option, or (in the
multicast case) by a propety of the destination address.

Responses are sent after the primary response, but may be reorderered by
transports that do not preserve sequence. The responses carry the same
token as the original request when there are tokens, or are otherwise
bound to the original request by whichever means the transport provides
(e.g. by using the same reqest_kid/request_piv pair in OSCORE).

A mechanism for eliciting multiple responses must specify the conditions
under which a token gets freed, as the traditional arrival of the
response is insufficient. It may also specify for which requests the
token can be reused immediately for follow-up requests. On unordered
transports, or when it's a client's follow-up request and not a response
that terminates the token, the client needs to wait until no reordered
non-traditional responses can be expected any more.

If a non-traditional response answers the original request, no further
action is required (this is the case of observation: ordering is added
on top of that to ensure that only the freshest response is used). If
the response does not answer the original request, it can either be
obviously non-matching, or it needs to carry some option that indicates
which request would have triggered it, for example the Response-For
option.

Responses without request
=========================

Endpoints may agree out of band on a token (or other request-matching
details). One way to do that is to exchange a "phantom request", which
is a request that client and server will agree to have sent and
received, respectively, without it actually being sent between those
endpoints.

As tokens are managed by the client, that request needs to be
generated by the client, or in close collaboration with the client (for
example by the client allowing a third party to use a subset of its
token values in order to set up non-traditional responses).

Examples of non-traditional responses
=====================================

Observation
-----------

This section describes the Observe option {{?RFC7641}} in terms of this
document, [ so nothing in here should contradict that document ].

When Observe:0 is present in a request, this sets up non-traditional
responses until either of the following conditions is met:

* A follow-up request on the same token carries an Observe:1 option.

  (This is primarily in here because; Observe:1 and No-Response:any
  could be combined; otherwise, the other conditions suffice).

* Any response does not carry an Observe option.
* Any response has a non-successful state.

Follow-up requests are limited to extending the request ETag set.
Responses are obviousy non-matching by their Observe option; each hop
discards the Observe option for the purpose of caching and refreshes its
cache with the most recent one as per the Observe value.

Responses to multicast requests
-------------------------------

As with observe, this just phrases the existing mechanism in the context
of this generalization.

When the destination address of a CoAP request is a multicast address,
that token is valid for any member of that group (which, for the purpose
of the client, is any server at all) on any port.

(Except for that the implications of having received a multicast request
still need to be followed, it might be seen as a template for creating a
phantom request to any endpoint, if that suits the reader's mental
model.)

Responses can only be sent for up to the deployment's leisure time plus
the application's timeout (in proxy situations, this needs to be
communicated explicitly in the Multicast-Signaling option of
{{?I-D.tiloca-core-groupcomm-proxy}}).

Triangular responses
--------------------

The Response-To option can be viewed as a short-hand notation for
"Consider this a No-Response:any request, but take a copy of it, make it
into a CoAP-over-UDP request with that particular address as a source
and any address of yours as a response, and treat that as a phantom
request".

[ It may make sense to add an explicit return token, and include a
No-Response option; that might allow it to be used even across proxies. ]

Leisure-For-Responses
---------------------

This new option (elective, proxy-unsafe, uint valued) allows the server
to send up as many responses as indicated in the option value in
addition to the requested response. They are to be sent immediately
after the original response.

On its own, the option does not indicate which more responses the client
would expect (though further elective proxy-safe no-cache-key options
can be added on top of that to give better guidance), and the server may
choose not to send any at all.

Intermediaries may add or remove the option, use incoming responses to
populate their cache. They may serve additional responses from their
cache, but in most cases the sensible course of action is to forward the
additional responses the origin server sends.

Use cases for Leisure-For-Responses include sending further blocks in a
Block2 transfer (which are obviously non-matching and thus don't need a
Response-For), serving follow-up documents (a response containing a
single link can be followed by a representation of the linked resource,
which needs a Request-For header that indicates the URI) or just provide
the ETag of a freshly created resource (which would have a Reqeust-For
option for a GET with the given path and an ETag, and be a 2.03 Valid
response).

Other current documents
-----------------------

{{?I-D.tiloca-core-observe-multicast-notifications}} is a
straightforward application of the phantom requests (the concept was
developed there); Leisure-For-Responses could help it around the topic
of joining a multicast group securely through a proxy.

{{?I-D.tiloca-core-groupcomm-proxy}} seems to fit well with the concepts
here as well, and might be simplified by it both in terminology and by
replacing Response-Forwarding with Response-For(Proxy-Scheme, Uri-Host).

[1]: https://github.com/core-wg/wg-materials/blob/master/ietf107/slides/draft-bormann-core-responses.pdf
[2]: https://mailarchive.ietf.org/arch/msg/core/HW-mYZQsP9BwiASO7ts1lOpwIRg/

-- 
To use raw power is to make yourself infinitely vulnerable to greater powers.
  -- Bene Gesserit axiom