RFC 9177

Constrained Application Protocol (CoAP) Block-Wise Transfer Options Supporting Robust Transmission

๐Ÿ“… published March 2022, from ietf

Title Block ๐Ÿ“œ

  • RFC and title: RFC 9177, "Constrained Application Protocol (CoAP) Block-Wise Transfer Options Supporting Robust Transmission" ๐ŸŽ€
  • Authors: M. Boucadair (Orange) and J. Shallow ๐Ÿ’…
  • Category: Standards Track ๐Ÿ”ฅ
  • ISSN: 2070-1721
  • Date: March 2022 ๐ŸŒธ
  • Obsoletes and updates: the title block doesn't list any RFCs that this one obsoletes or updates โœจ

Abstract โœจ

okay so this doc specifies two alternative CoAP block-wise transfer options, Q-Block1 and Q-Block2 ๐Ÿฆ‹

they're similar to the CoAP Block1 and Block2 options from RFC 7959, but they're not the same thing. Q-Block1 and Q-Block2 are NOT meant to replace Block1 and Block2. their whole goal is to support Non-confirmable (NON) messages for big chunks of data with fewer packet interchanges, which is so efficient ๐Ÿ˜ญ they also help you recover faster if any of the blocks get lost in transmission ๐Ÿ’–

Status of This Memo ๐Ÿ“‹

this is an Internet Standards Track document ๐Ÿ”ฅ

it's a product of the Internet Engineering Task Force (IETF), so it represents the consensus of the IETF community. it got public review and was approved for publication by the Internet Engineering Steering Group (IESG). if you want more info on Internet Standards, check Section 2 of RFC 7841 ๐Ÿซถ

for the current status, any errata, and how to give feedback on this doc, go to https://www.rfc-editor.org/info/rfc9177 ๐ŸŒˆ

Copyright (c) 2022 IETF Trust and the persons identified as the document authors. All rights reserved ๐ŸŽ€

this document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) as they're in effect on the date of publication. please actually read those, bestie, because they describe your rights and restrictions for this doc ๐Ÿ™ˆ also, any Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions. they come without warranty, as described in the Revised BSD License โญ

Table of Contents ๐Ÿ“š

   1.  Introduction
   2.  Terminology
   3.  Alternative CoAP Block-Wise Transfer Options
     3.1.  CoAP Response Code (4.08) Usage
     3.2.  Applicability Scope
   4.  The Q-Block1 and Q-Block2 Options
     4.1.  Properties of the Q-Block1 and Q-Block2 Options
     4.2.  Structure of the Q-Block1 and Q-Block2 Options
     4.3.  Using the Q-Block1 Option
     4.4.  Using the Q-Block2 Option
     4.5.  Using the Observe Option
     4.6.  Using the Size1 and Size2 Options
     4.7.  Using the Q-Block1 and Q-Block2 Options Together
     4.8.  Using the Q-Block2 Option with Multicast
   5.  The Use of the 4.08 (Request Entity Incomplete) Response Code
   6.  The Use of Tokens
   7.  Congestion Control for Unreliable Transports
     7.1.  Confirmable (CON)
     7.2.  Non-confirmable (NON)
   8.  Caching Considerations
   9.  HTTP Mapping Considerations
   10. Examples with Non-confirmable Messages
     10.1.  Q-Block1 Option
       10.1.1.  A Simple Example
       10.1.2.  Handling MAX_PAYLOADS Limits
       10.1.3.  Handling MAX_PAYLOADS with Recovery
       10.1.4.  Handling Recovery if Failure Occurs
     10.2.  Q-Block2 Option
       10.2.1.  A Simple Example
       10.2.2.  Handling MAX_PAYLOADS Limits
       10.2.3.  Handling MAX_PAYLOADS with Recovery
       10.2.4.  Handling Recovery by Setting the M Bit
     10.3.  Q-Block1 and Q-Block2 Options
       10.3.1.  A Simple Example
       10.3.2.  Handling MAX_PAYLOADS Limits
       10.3.3.  Handling Recovery
   11. Security Considerations
   12. IANA Considerations
     12.1.  CoAP Option Numbers Registry
     12.2.  Media Type Registration
     12.3.  CoAP Content-Formats Registry
   13. References
     13.1.  Normative References
     13.2.  Informative References
   Appendix A.  Examples with Confirmable Messages
     A.1.  Q-Block1 Option
     A.2.  Q-Block2 Option
   Appendix B.  Examples with Reliable Transports
     B.1.  Q-Block1 Option
     B.2.  Q-Block2 Option
   Acknowledgments
   Authors' Addresses

1. Introduction โœจ

so CoAP [RFC7252] was inspired by HTTP, but it was designed to run over UDP instead of TCP ๐Ÿฆ‹ its message layer over UDP includes reliable delivery, simple congestion control, and flow control. CoAP has two message types (Section 1.2 of [RFC7252]): Confirmable (CON) and Non-confirmable (NON). the difference is the main character energy: every CON message will elicit an acknowledgment or a reset, but NON messages don't get that ๐Ÿ’…

the CoAP spec recommends that a CoAP message should fit within a single IP packet, so you avoid IP fragmentation ๐ŸŒธ but what about data records that can't fit in one packet? that's where [RFC7959] came in. it introduced block-wise transfers plus the companion CoAP Block1 and Block2 options ๐ŸŽ€ the catch is that this concept is designed to work exclusively with Confirmable messages (Section 1 of [RFC7959]) ๐Ÿ™ˆ also, [RFC8323] further updated block-wise transfer for use over TCP, TLS, and WebSockets โœจ

the CoAP Block1 and Block2 options work well when there are no, or minimal, packet losses ๐Ÿซถ they operate synchronously, which means each individual block has to be requested. a CoAP endpoint can only ask for (or send) the next block once the transfer of the previous block has completed. so the packet transmission rate, and therefore the block transmission rate, is controlled by Round-Trip Times (RTTs) ๐Ÿ˜ญ

now here's the problem. there's a requirement for blocks of data larger than a single IP datagram to be sent under network conditions with asymmetrical transient packet loss, where acknowledgment responses may get dropped ๐Ÿ’€ one example is a Distributed Denial of Service (DDoS) attack, when DDoS mitigation agents that rely on CoAP need to talk to each other (e.g., [RFC9132] [DOTS-TELEMETRY]) ๐ŸŒˆ [RFC7959] recommends using CON responses to handle potential packet loss, but that doesn't work in a "flooded pipe" DDoS situation (e.g., [RFC9132]) ๐Ÿ”ฅ

this document introduces the CoAP Q-Block1 and Q-Block2 options. they let block-wise transfers work with a series of Non-confirmable messages, instead of lock-stepping with Confirmable messages (Section 3) ๐Ÿฆ‹ basically, this doc fills a missing piece of [RFC7959]: block-wise transfers using Non-confirmable messages, where an entire body of data can be sent without needing intermediate acknowledgments from the peer. recovery is still available if it's needed ๐Ÿ’–

like [RFC7959], this spec doesn't remove any of the constraints from the base CoAP specification [RFC7252] that it's strictly layered on top of ๐Ÿงš

2. Terminology ๐Ÿ’œ

okay the key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here ๐ŸŽ€ so only the all-caps versions count as the real deal, not the lowercase ones ๐Ÿ™ˆ

you should be familiar with the terms and concepts in [RFC7252], [RFC7959], and [RFC8132] ๐Ÿ’… this document especially uses these key concepts:

  • Token: used to match responses to requests independently from the underlying messages (Section 5.3.1 of [RFC7252]) ๐Ÿงš
  • ETag: used as a resource-local identifier for telling apart representations of the same resource that vary over time (Section 5.10.6 of [RFC7252]) ๐Ÿฆ‹

the terms "payload" and "body" are defined in [RFC7959]. so "payload" is the content of a single CoAP message (aka a single block being transferred), and "body" is the entire resource representation that's being transferred in a block-wise fashion ๐ŸŒธ

  • Request-Tag: refers to an option that lets a CoAP server match message fragments that belong to the same request [RFC9175] ๐Ÿซถ
  • MAX_PAYLOADS: the maximum number of payloads that can be transmitted at any one time ๐Ÿ”ฅ
  • MAX_PAYLOADS_SET: the set of blocks, identified by block numbers, that have the same numeric result when divided by MAX_PAYLOADS. for example, if MAX_PAYLOADS is set to 10, a MAX_PAYLOADS_SET could be blocks #0 to #9, #10 to #19, etc. depending on the overall data size, there could be fewer than MAX_PAYLOADS blocks in the final MAX_PAYLOADS_SET ๐Ÿ‘‘

3. Alternative CoAP Block-Wise Transfer Options โœจ

okay so this is where the main event happens ๐Ÿ’… This document introduces the CoAP Q-Block1 and Q-Block2 options. They're built to shine with NON requests and responses ๐Ÿฆ‹

Bestie, NON messages make transmissions way faster ๐Ÿš€ Every block can be sent back-to-back, kinda like fragmented IP packets ๐Ÿงš, without waiting around for a response or the next request from the remote CoAP peer. Recovering missing blocks is quicker too, because several missing blocks can be requested in one single CoAP message ๐Ÿ’œ. Even when packet loss is asymmetrical, a body can still be sent and received by the peer ๐Ÿซถ, whether it's one payload or several, as long as no recovery is needed ๐ŸŒธ.

A CoAP endpoint can acknowledge all of the blocks or just a subset of them ๐Ÿ’… Basically, the receiving endpoint either tells the sending endpoint that everything arrived, or it reports every block in the body that hasn't been received yet ๐Ÿ“‹. Then the sending endpoint only retransmits the blocks that got lost along the way ๐Ÿ”

Similar transmission rate perks can apply to Confirmable messages if NSTART is raised from 1 (Section 4.7 of [RFC7252]) ๐Ÿ”ฅ. But ngl, Confirmable messages won't work well when packet loss is asymmetrical ๐Ÿ˜ฌ. Some examples with Confirmable messages are in Appendix A ๐Ÿ“š

There's little, if any, benefit to using these options with CoAP running over a reliable connection [RFC8323] ๐Ÿ™ˆ. In that case there's no difference between CON and NON, because they aren't used. Some examples using a reliable transport are in Appendix B ๐Ÿงš

The Q-Block1 and Q-Block2 options work similarly to the CoAP Block1 and Block2 options, respectively ๐ŸŽ€. They aren't a replacement for them, but they come with these perks โœจ:

  • They can operate in environments where packet loss is highly asymmetrical ๐ŸŒˆ.
  • They enable faster transmissions of sets of blocks of data with fewer packet interchanges ๐Ÿš€.
  • They support faster recovery should any of the blocks get lost in transmission ๐Ÿ’ช.
  • They support sending an entire body using NON messages without requiring that an intermediate response be received from the peer ๐Ÿ’Œ.

Now the not-so-cute part ๐Ÿ’€. The disadvantages of using the CoAP Block1 and Block2 options are as follows:

  • There is a loss of lock-stepping, so payloads are not always received in the correct order (blocks in ascending order) ๐ŸŒ€.
  • Additional congestion control measures need to be put in place for NON messages (Section 7.2) ๐Ÿšง.
  • To reduce the transmission times for CON transmissions of large bodies, NSTART needs to be increased from 1, but this affects congestion control and incurs a requirement to retune other parameters (Section 4.7 of [RFC7252]). Such tuning is out of scope of this document ๐Ÿ› ๏ธ.
  • Mixing of NON and CON during an exchange of requests/responses using Q-Block options is not supported ๐Ÿšซ.
  • The Q-Block options do not support stateless operation/random access ๐Ÿ™….
  • Proxying of Q-Block options is limited to caching full representations ๐Ÿ“ฆ.
  • There is no multicast support ๐Ÿ“ญ.

The Q-Block1 and Q-Block2 options can be used instead of the Block1 and Block2 options when the different transmission properties are required ๐Ÿฆ‹. If the new options aren't supported by a peer, transmissions can fall back to using the Block1 and Block2 options (Section 4.1) ๐Ÿ”™.

The deviations from the Block1 and Block2 options are specified in Section 4. Pointers to the appropriate sections in [RFC7959] are provided ๐Ÿ“.

The spec refers to the base CoAP methods defined in Section 5.8 of [RFC7252], plus the new CoAP methods FETCH, PATCH, and iPATCH, which are introduced in [RFC8132] ๐Ÿ’….

The No-Response option [RFC7967] was considered but got abandoned ๐Ÿ™…โ€โ™€๏ธ, since it doesn't apply to Q-Block2 responses. A unified solution is defined in the document โœจ.

3.1. CoAP Response Code (4.08) Usage

This document adds a media type for the 4.08 (Request Entity Incomplete) response ๐Ÿ“จ, defining an additional message format for reporting on payloads using the Q-Block1 option that are not received by the server ๐Ÿงพ.

See Section 5 for more details ๐Ÿ‘€

3.2. Applicability Scope

The block-wise transfer specified in [RFC7959] covers the general case using Confirmable messages ๐Ÿ’Œ, but it falls short when packet loss is highly asymmetrical or when there's no need for an acknowledgment ๐Ÿ˜ค. In other words, there's a need for Non-confirmable support ๐Ÿซถ.

The mechanism specified in this document provides roughly similar features to the Block1/Block2 options ๐ŸŒธ. It also has extra properties tailored to the intended use case of Non-confirmable transmission ๐Ÿ’œ. Concretely, this mechanism mainly targets applications such as DDoS Open Threat Signaling (DOTS) ๐Ÿ›ก๏ธ. Those apps can't use CON requests/responses because of potential packet loss, and they support application-specific mechanisms to check whether the remote peer isn't overloaded and is able to process the messages sent by a CoAP endpoint (e.g., DOTS heartbeats in Section 4.7 of [RFC9132]) ๐Ÿ’“. Other use cases are when an application sends data but has no need for an acknowledgment of receipt, and any data transmission loss is not critical ๐Ÿ“ฌ.

The mechanism includes guards to stop a CoAP agent from overloading the network by adopting an aggressive sending rate ๐Ÿ›‘. These guards MUST be followed in addition to the existing CoAP congestion control, as specified in Section 4.7 of [RFC7252] ๐Ÿšจ. See Section 7 for more details ๐Ÿ”.

Any usage outside the primary use case of Non-confirmable messages with block transfers should be carefully weighed against the potential loss of interoperability with generic CoAP applications (see the disadvantages listed in Section 3) โš–๏ธ. It is hoped that the experience gained with this mechanism can feed future extensions of the block-wise mechanism that will both be generally applicable and serve this particular use case ๐ŸŒˆ.

It is not recommended that these options are used in the "NoSec" security mode (Section 9 of [RFC7252]) ๐Ÿ”“, as the source endpoint needs to be trusted ๐Ÿค. Using Object Security for Constrained RESTful Environments (OSCORE) [RFC8613] does provide a security context, and hence a trust of the source endpoint that prepared the inner OSCORE content ๐Ÿ”. However, even with OSCORE, using the NoSec mode with these options may still be inadequate, for reasons discussed in Section 11 โš ๏ธ.

4. The Q-Block1 and Q-Block2 Options โœจ

4.1. Properties of the Q-Block1 and Q-Block2 Options โœจ

okay so here's the property tea ๐Ÿต for the Q-Block1 and Q-Block2 options! Table 1 is basically your cheat sheet, and it's formatted just like Table 4 of Section 5.10 of [RFC7252] ๐Ÿ’…. The C, U, N, and R columns stand for Critical, UnSafe, NoCacheKey, and Repeatable, which are all defined in Section 5.4 of [RFC7252] ๐Ÿซถ. Only the Critical and UnSafe columns get marked for Q-Block1. For Q-Block2, it's Critical, UnSafe, and Repeatable ๐ŸŽ€. Since these options are UnSafe, NoCacheKey has no meaning for them, so it gets a dash ๐Ÿ™ˆ.

      +=====+===+===+===+===+==========+========+========+=========+
      | No. | C | U | N | R | Name     | Format | Length | Default |
      +=====+===+===+===+===+==========+========+========+=========+
      | 19  | x | x | - |   | Q-Block1 | uint   | 0-3    | (none)  |
      +-----+---+---+---+---+----------+--------+--------+---------+
      | 31  | x | x | - | x | Q-Block2 | uint   | 0-3    | (none)  |
      +-----+---+---+---+---+----------+--------+--------+---------+

          Table 1: CoAP Q-Block1 and Q-Block2 Option Properties

Both options can show up in requests AND responses ๐Ÿฆ‹. Q-Block1 is about the request payload, and Q-Block2 is about the response payload. When Content-Format shows up alongside either one, the option applies to the whole body, not to each individual payload. That means it has to be the same across all payloads of the same body ๐Ÿงฉ.

Q-Block1 is handy for payload-bearing requests (POST, PUT, FETCH, PATCH, and iPATCH) and their responses ๐Ÿ“ฆ. Q-Block2 is useful for GET, POST, PUT, FETCH, PATCH, and iPATCH requests, plus payload-bearing responses with codes 2.01, 2.02, 2.04, and 2.05 (Section 5.5 of [RFC7252]) ๐ŸŒธ.

A CoAP endpoint (or proxy) MUST support either both or neither of the Q-Block1 and Q-Block2 options. No halfsies, bestie ๐Ÿ’€.

If Q-Block1 is in a request, or Q-Block2 comes back in a response, that's a block-wise transfer. The option tells you how this specific block fits into the entire body being moved around โœจ. If it shows up in the opposite direction, it gives extra control over how that payload gets formed or how it was processed ๐Ÿ‘‘.

To say they can handle Q-Block2 responses, the CoAP client MUST include the Q-Block2 option in a GET or similar request (e.g., FETCH), the Q-Block2 option in a PUT or similar request (e.g., POST), or the Q-Block1 option in a PUT or similar request. That way the server knows this client gets Q-Block stuff if it ever needs to send back a body spread across multiple payloads ๐Ÿซถ. Otherwise, the server would use the Block2 option (if supported) to send back a message body that's too big to fit into a single IP packet [RFC7959] ๐Ÿ“ฆ๐Ÿ’ฅ.

How a client decides whether it needs Q-Block1 or Q-Block2 can be driven by a local configuration parameter, an application trigger (like DOTS), and so on ๐Ÿ™ƒ. Those considerations are out of the scope of this document, so we're not going there today ๐Ÿฆ‹.

Implementing the Q-Block1 and Q-Block2 options is intended to be optional. HOWEVER, when either option is present in a CoAP message, it MUST be processed (or the message rejected). That's exactly why they're identified as critical options ๐Ÿ‘‘.

With CoAP over UDP, how a request gets rejected for critical options depends on the message type ๐ŸŒˆ. A Confirmable message with an unrecognized critical option gets a 4.02 (Bad Option) response (Section 5.4.1 of [RFC7252]) ๐Ÿ™…. A Non-confirmable message with an unrecognized critical option is either rejected with a Reset message or just silently ignored (Sections 5.4.1 and 4.3 of [RFC7252]) ๐Ÿ‘ป. So if you want a rejection message you can count on, it is REQUIRED that clients use a Confirmable message for figuring out support for the Q-Block1 and Q-Block2 options ๐ŸŽฏ. That Confirmable message can go out under the base CoAP congestion control setup from Section 4.7 of [RFC7252], which means NSTART doesn't need to be increased (Section 7.1) ๐Ÿšฆ.

Q-Block1 and Q-Block2 are unsafe to forward ๐Ÿ›‘. A CoAP proxy that doesn't understand the Q-Block1 (or Q-Block2) option must reject the request or response that uses either option (see Section 5.7.1 of [RFC7252]). Not a vibe.

Q-Block2 is repeatable when requesting retransmission of missing blocks, but not otherwise ๐Ÿ”. Outside of that case, any request carrying multiple Q-Block1 (or Q-Block2) options MUST be handled following the procedure in Section 5.4.5 of [RFC7252] ๐Ÿ’….

Q-Block1 and Q-Block2, just like Block1 and Block2, are both class E and class U for OSCORE processing (Table 2) ๐Ÿ”. The Q-Block1 (or Q-Block2) option MAY be an Inner or Outer option (Section 4.1 of [RFC8613]). Inner and Outer values are independent of each other. Inner is encrypted and integrity protected between clients and servers, and it provides message body identification when requests get fragmented end-to-end. Outer is visible to proxies and labels message bodies when requests get fragmented hop-by-hop ๐Ÿงš.

                       +========+==========+===+===+
                       | Number | Name     | E | U |
                       +========+==========+===+===+
                       | 19     | Q-Block1 | x | x |
                       +--------+----------+---+---+
                       | 31     | Q-Block2 | x | x |
                       +--------+----------+---+---+

                              Table 2: OSCORE
                             Protection of the
                           Q-Block1 and Q-Block2
                                  Options
Heads up, bestie ๐Ÿšจ: if Q-Block1 or Q-Block2 are included in a packet as Inner options, then Block1 or Block2 MUST NOT be included as Inner options. Similarly, there MUST NOT be a mix of Q-Block and Block options for the Outer options. Messages that don't play by these rules MUST be rejected with a 4.02 (Bad Option) ๐Ÿ’€.

The Q-Block and Block options can be mixed across Inner and Outer options, since those get handled independently of each other ๐ŸŒธ. For clarity, if OSCORE isn't being used, there MUST NOT be a mix of Q-Block and Block options in the same packet. ๐Ÿ™ˆ


4.2. Structure of the Q-Block1 and Q-Block2 Options ๐ŸŽ€

The structure of the Q-Block1 and Q-Block2 options follows the structure defined in Section 2.2 of [RFC7959] ๐Ÿงฑ.

There is no default value for these options ๐Ÿคท. The absence of one of them is equivalent to an option value of 0 with respect to the block number (NUM) and the more bit (M) that could be given in the option. In other words, it indicates that the current block is the first and only block of the transfer (block number is set to 0; M is unset). BUT, unlike the explicit value 0 (which would indicate a block size (SZX) of 0, and therefore a size value of 16 bytes), there is no specific size implied by the absence of the option. The size is left unspecified ๐Ÿ’–. (As for any uint, the explicit value 0 is efficiently indicated by a zero-length option. That's why it's semantically different from the option being absent ๐Ÿง .)

4.3. Using the Q-Block1 Option ๐Ÿ’–

okay so the Q-Block1 option is for when the client wants to send a huge pile of data to the server using POST, PUT, FETCH, PATCH, or iPATCH ๐Ÿš€. This is the move when the data and headers together won't fit in a single packet ๐Ÿ“ฆ.

When Q-Block1 is in play, the client MUST include a Request-Tag option [RFC9175] ๐ŸŽ€. The Request-Tag value MUST be the same for all of the requests carrying the body of data being sent ๐Ÿฆ‹. The Request-Tag is opaque, which means nobody gets to peek inside, but the client MUST ensure it is unique for every different body of transmitted data ๐Ÿ”.

Implementation Note: It is suggested that the client treats the Request-Tag as an unsigned integer of 8 bytes in length. An implementation may want to consider limiting this to 4 bytes to reduce packet overhead size. The initial Request-Tag value should be randomly generated and then subsequently incremented by the client whenever a new body of data is being transmitted between peers. ๐ŸŒธ

Section 4.6 discusses the use of the Size1 option ๐Ÿ“.

For Confirmable transmission, the server keeps acknowledging each packet, but it doesn't need to send a response (whether separate or piggybacked) until the server has successfully received the whole body ๐Ÿซถ. For Non-confirmable transmission, no response is required until either the server has successfully received the body or a timer expires with some of the payloads still missing ๐Ÿ˜ญ. In that second case, a "retransmit missing payloads" response is needed. For reliable transports (e.g., [RFC8323]), a response is not required until the server has successfully received the body โœจ.

Each individual message that carries a block of the body is treated as a new request (Section 6) ๐Ÿงš.

The client MUST send the payloads in order of increasing block number, starting from zero, until the body is complete (subject to any congestion control (Section 7)) ๐Ÿƒโ€โ™€๏ธ. On top of that, any missing payloads the server asks for must be separately transmitted with increasing block numbers ๐Ÿ’….

Here are the response codes that get used ๐Ÿ‘‘:

2.01 (Created) This response code indicates successful receipt of the entire body and that the resource was created ๐ŸŽ‰. The token to use MUST be one of the tokens that were received in a request for this block-wise exchange. However, it is desirable to provide the one used in the last received request, since that will aid any troubleshooting ๐Ÿ”. The client should then release all of the tokens used for this body. Note that the last received payload might not be the one with the highest block number ๐Ÿคทโ€โ™€๏ธ.

2.02 (Deleted) This response code indicates successful receipt of the entire body and that the resource was deleted when using POST (Section 5.8.2 of [RFC7252]) ๐Ÿ—‘๏ธ. The token to use MUST be one of the tokens that were received in a request for this block-wise exchange. However, it is desirable to provide the one used in the last received request. The client should then release all of the tokens used for this body.

2.04 (Changed) This response code indicates successful receipt of the entire body and that the resource was updated โœ๏ธ. The token to use MUST be one of the tokens that were received in a request for this block-wise exchange. However, it is desirable to provide the one used in the last received request. The client should then release all of the tokens used for this body.

2.05 (Content) This response code indicates successful receipt of the entire FETCH request body (Section 2 of [RFC8132]) and that the appropriate representation of the resource is being returned ๐Ÿ“ฌ. The token to use MUST be one of the tokens that were received in a request for this block-wise exchange. However, it is desirable to provide the one used in the last received request.

If the FETCH request includes the Observe option, then the server MUST use the same token as used for the 2.05 (Content) response for returning any triggered Observe responses, so that the client can match them up ๐Ÿ‘€. The client should then release all of the tokens used for this body apart from the one used for tracking an observed resource.

2.31 (Continue) This response code can be used to indicate that all of the blocks up to and including the Q-Block1 option block NUM (all having the M bit set) have been successfully received ๐Ÿ™Œ. The token to use MUST be one of the tokens that were received in a request for this latest MAX_PAYLOADS_SET block-wise exchange. However, it is desirable to provide the one used in the last received request. The client should then release all of the tokens used for this MAX_PAYLOADS_SET.

A response using this response code MUST NOT be generated for every received Q-Block1 option request ๐Ÿ™…โ€โ™€๏ธ. It SHOULD only be generated when all the payload requests are Non-confirmable and a MAX_PAYLOADS_SET has been received by the server. More details about the motivations for this optimization are discussed in Section 7.2 ๐Ÿง .

This response code SHOULD NOT be generated for CON, as this may cause duplicated payloads to unnecessarily be sent ๐Ÿ’€.

4.00 (Bad Request) This response code MUST be returned if the request does not include a Request-Tag option or a Size1 option but does include a Q-Block1 option ๐Ÿ™ˆ.

4.02 (Bad Option) This response code MUST be returned for a Confirmable request if the server does not support the Q-Block options. Note that a Reset message may be sent in case of a Non-confirmable request ๐Ÿซ .

4.08 (Request Entity Incomplete) As a reminder, this response code returned without content type application/missing-blocks+cbor-seq (Section 12.3) is handled as in Section 2.9.2 of [RFC7959] ๐Ÿ“.

This response code returned with content type application/missing-blocks+cbor-seq indicates that some of the payloads are missing and need to be resent ๐Ÿงฉ. The client then retransmits the individual missing payloads using the same Request-Tag, Size1, and Q-Block1 options, specifying the same NUM, SZX, and M bit values as those sent initially in the original (but not received) packets.

The Request-Tag value to use is determined by taking the token in the 4.08 (Request Entity Incomplete) response, locating the matching client request, and then using its Request-Tag ๐Ÿ”Ž.

The token to use in the 4.08 (Request Entity Incomplete) response MUST be one of the tokens that were received in a request for this block-wise body exchange. However, it is desirable to provide the one used in the last received request. See Section 5 for further information.

If the server has not received all the blocks of a body, but one or more NON payloads have been received, it SHOULD wait for NON_RECEIVE_TIMEOUT (Section 7.2) before sending a 4.08 (Request Entity Incomplete) response โณ.

4.13 (Request Entity Too Large) This response code can be returned under conditions similar to those discussed in Section 2.9.3 of [RFC7959] ๐Ÿ“.

This response code can be returned if there is insufficient space to create a response PDU with a block size of 16 bytes (SZX = 0) to send back all the response options as appropriate. In this case, the Size1 option is not included in the response ๐Ÿซง.

Further considerations related to the transmission timings of the 4.08 (Request Entity Incomplete) and 2.31 (Continue) response codes are discussed in Section 7.2 โฐ.

If a server receives payloads with different Request-Tags for the same resource, it should continue to process all the bodies, as it has no way of determining which is the latest version or which body, if any, the client is terminating the transmission for ๐Ÿค”.

If the client elects to stop the transmission of a complete body, then absent any local policy, the client MUST "forget" all tracked tokens associated with the body's Request-Tag, so that a Reset message is generated for the invalid token in the 4.08 (Request Entity Incomplete) response ๐Ÿ‘ป. On receipt of the Reset message, the server SHOULD delete the partial body ๐Ÿงน.

If the server receives a duplicate block with the same Request-Tag, it MUST ignore the payload of the packet but MUST still respond as if the block was received for the first time ๐Ÿชž.

A server SHOULD maintain a partial body (missing payloads) for NON_PARTIAL_TIMEOUT (Section 7.2) ๐Ÿ’œ.

4.4. Using the Q-Block2 Option ๐Ÿ’–

In a request for any block number, an unset M bit means the request is just for that one block. โœจ If the M bit is set, it means something different depending on the NUM value:

  • NUM is zero: This is a request for the entire body. ๐Ÿ‘‘
  • 'NUM modulo MAX_PAYLOADS' is zero, while NUM is not zero: This is used to confirm that the current MAX_PAYLOADS_SET (the latest block having block number NUM-1) has been successfully received, and that once the server gets this request, it can continue sending the next MAX_PAYLOADS_SET (the first block having block number NUM). This is the 'Continue' Q-Block-2, and conceptually it has the same usage (continue sending the next set of data) as 2.31 (Continue) does for Q-Block1. ๐Ÿฆ‹
  • Any other value of NUM: This is a request for that block and for all of the remaining blocks in the current MAX_PAYLOADS_SET. ๐ŸŒธ

If the request includes multiple Q-Block2 options and they overlap (for example, M being set for this and later blocks, and unset for this individual block), so that one block gets requested more than once, the server MUST only send back one instance of that block. ๐Ÿ™…โ€โ™€๏ธ This behavior is meant to prevent amplification attacks, so it's a big deal, bestie. ๐Ÿ”ฅ

The payloads the server sends back as a response MUST all have the same ETag (Section 5.10.6 of [RFC7252]) for the same body. The server MUST NOT use the same ETag value for different representations of a resource. ๐Ÿšซ

The ETag is opaque, but the server MUST ensure it is unique for every different body of transmitted data. ๐Ÿ”

Implementation Note: It is suggested that the server treats the ETag as an unsigned integer of 8 bytes in length. An implementation may want to consider limiting this to 4 bytes to reduce packet overhead size. The initial ETag value should be randomly generated and then subsequently incremented by the server whenever a new body of data is being transmitted between peers. ๐ŸŽ€

Section 4.6 discusses the use of the Size2 option. ๐Ÿงš

The client may elect to request any detected missing blocks, or it may just ignore the partial body. That decision is implementation specific. ๐Ÿคทโ€โ™€๏ธ

For NON payloads, the client SHOULD wait for NON_RECEIVE_TIMEOUT (Section 7.2) after the last received payload before requesting retransmission of any missing blocks. โณ Retransmission is requested by issuing a GET, POST, PUT, FETCH, PATCH, or iPATCH request that contains one or more Q-Block2 options defining the missing block(s). Generally, the M bit on the Q-Block2 option(s) SHOULD be unset, although the M bit MAY be set to request this and later blocks from this MAX_PAYLOADS_SET. See Section 10.2.4 for an example of this in action. ๐Ÿ“ Further considerations about transmission timing for missing requests are discussed in Section 7.2.

The missing block numbers the client requests MUST have an increasing block number in each additional Q-Block2 option, with no duplicates. The server SHOULD respond with a 4.00 (Bad Request) to requests that don't follow this behavior. ๐Ÿ™…โ€โ™€๏ธ The ordering rule is there to force the client to check for duplicates and remove them, and it also helps with troubleshooting. ๐Ÿ› ๏ธ

If the client receives a duplicate block with the same ETag, it MUST silently ignore the payload. ๐Ÿคซ

A client SHOULD maintain a partial body (missing payloads) for NON_PARTIAL_TIMEOUT (Section 7.2), or as defined by the Max-Age option (or its default of 60 seconds (Section 5.6.1 of [RFC7252])), whichever is less. โฑ๏ธ When the partial body is released, the client should then release all of the tokens used for this body apart from the token used to track a resource that is being observed. ๐Ÿ’…

The ETag option should not be used in the request for missing blocks, because the server could respond with a 2.03 (Valid) response with no payload. ๐Ÿ˜ฌ It can be used in the request if the client wants to check the freshness of the locally cached body response. ๐ŸงŠ

The server SHOULD maintain a cached copy of the body when using the Q-Block2 option, so it can facilitate retransmission of any missing payloads. ๐Ÿ’พ

If the server detects partway through a body transfer that the resource data has changed, and the server is not maintaining a cached copy of the old data, then the transmission is terminated. Any subsequent missing block requests MUST be responded to using the latest ETag and Size2 option values with the updated data. ๐Ÿ”„

If the server responds during a body update with a different ETag option value (because the resource representation has changed), the client should treat the partial body with the old ETag as no longer fresh. ๐Ÿฅ€ The client may then request all of the new data by specifying Q-Block2 with block number '0' and the M bit set. ๐ŸŒˆ

If the server transmits a new body of data (for example, a triggered Observe notification) with a new ETag to the same client as an additional response, the client should remove any partially received body held for a previous ETag for that resource, since it is unlikely the missing blocks can be retrieved. ๐Ÿ—‘๏ธ

If there is not enough space to create a response PDU with a block size of 16 bytes (SZX = 0) to send back all the response options as appropriate, a 4.13 (Request Entity Too Large) is returned without the Size1 option. ๐Ÿ“ฆ

For Confirmable traffic, the server typically acknowledges the initial request using an Acknowledgment (ACK) with a piggybacked payload, and then sends the subsequent payloads of the MAX_PAYLOADS_SET as CON responses. ๐Ÿ“จ These CON responses are individually ACKed by the client. The server will detect failure to send a packet and SHOULD terminate the body transfer, but after a MAX_TRANSMIT_SPAN delay, the client can issue a separate GET, POST, PUT, FETCH, PATCH, or iPATCH for any missing blocks as needed. ๐Ÿซถ


4.5. Using the Observe Option ๐Ÿ‘€

For a request that uses Q-Block1, the Observe value [RFC7641] MUST be the same for all the payloads of the same body. This includes any missing payloads that are retransmitted. ๐Ÿ”

For a response that uses Q-Block2, the Observe value MUST be the same for all the payloads of the same body. This is different from Block2 usage, where the Observe value is only present in the first block (Section 3.4 of [RFC7959]). This includes payloads transmitted after the server receives the 'Continue' Q-Block2 option (Section 4.4). If a missing payload is requested by a client, then both the request and the response MUST NOT include the Observe option. ๐Ÿšซ๐Ÿ‘€


4.6. Using the Size1 and Size2 Options ๐Ÿ“

Section 4 of [RFC7959] defines two CoAP options: Size1, for indicating the size of the representation transferred in requests, and Size2, for indicating the size of the representation transferred in responses. ๐Ÿงฎ

For the Q-Block1 and Q-Block2 options, the Size1 or Size2 option values MUST exactly represent the size of the data on the body, so that any missing data can easily be determined. ๐ŸŽฏ

The Size1 option MUST be used with the Q-Block1 option when used in a request, and MUST be present in all payloads of the request, preserving the same value. The Size2 option MUST be used with the Q-Block2 option when used in a response, and MUST be present in all payloads of the response, preserving the same value. ๐Ÿ’ฏ


4.7. Using the Q-Block1 and Q-Block2 Options Together ๐Ÿค

The behavior is similar to the one defined in Section 3.3 of [RFC7959], with Q-Block1 substituted for Block1 and Q-Block2 substituted for Block2. ๐Ÿ”„


4.8. Using the Q-Block2 Option with Multicast ๐Ÿ“ก

Servers MUST ignore multicast requests that contain the Q-Block2 option. ๐Ÿ™…โ€โ™€๏ธ As a reminder, the Block2 option can be used as stated in Section 2.8 of [RFC7959]. ๐Ÿ’Œ


5. The Use of the 4.08 (Request Entity Incomplete) Response Code ๐Ÿงฉ

The 4.08 (Request Entity Incomplete) response code has a new content type, "application/missing-blocks+cbor-seq", used to indicate that the server has not received all of the blocks of the request body that it needs to proceed. Such messages must not be treated by the client as a fatal error. ๐Ÿซง

Likely causes are that the client has not sent all blocks, that some blocks were dropped during transmission, or that the client sent them a long enough time ago that the server has already discarded them. โŒ›

The new data payload of the 4.08 (Request Entity Incomplete) response, with content type "application/missing-blocks+cbor-seq", is encoded as a Concise Binary Object Representation (CBOR) Sequence [RFC8742]. It comprises one or more missing block numbers, encoded as CBOR unsigned integers [RFC8949]. The missing block numbers MUST be unique in each 4.08 (Request Entity Incomplete) response when created by the server. The client MUST ignore any duplicates in the same 4.08 (Request Entity Incomplete) response. ๐Ÿ”

The Content-Format option (Section 5.10.3 of [RFC7252]) MUST be used in the 4.08 (Request Entity Incomplete) response. It MUST be set to "application/missing-blocks+cbor-seq" (Section 12.3). ๐Ÿท๏ธ

The Concise Data Definition Language (CDDL) [RFC8610] (and see Section 4.1 of [RFC8742]) for the data describing these missing blocks is as follows: ๐Ÿ“œ

; This defines an array, the elements of which are to be used
; in a CBOR Sequence:
payload = [+ missing-block-number]
; A unique block number not received:
missing-block-number = uint

          Figure 1: Structure of the Missing Blocks Payload

This CDDL syntax MUST be followed. ๐Ÿ“

It is desirable that the token used for the response is the token that was used in the last block number received so far with the same Request-Tag value. Using any received token with the same Request-Tag would be acceptable, but giving the one used in the last received payload will help with troubleshooting. ๐Ÿ•ต๏ธโ€โ™€๏ธ The client will use the token to work out which request was sent previously, so it can get the Request-Tag value that was used. ๐ŸŽŸ๏ธ

If the size of the 4.08 (Request Entity Incomplete) response packet is larger than that defined by Section 4.6 of [RFC7252], then the number of reported missing blocks MUST be limited so the response can fit into a single packet. In that case, the server can send subsequent 4.08 (Request Entity Incomplete) responses containing those additional missing blocks when it receives a new request providing a missing payload with the same Request-Tag. ๐Ÿ“ฌ

The missing blocks MUST be reported in ascending order without any duplicates. The client SHOULD silently drop 4.08 (Request Entity Incomplete) responses that don't follow this behavior. ๐Ÿ—‚๏ธ

Implementation Note: Consider limiting the number of missing payloads to MAX_PAYLOADS to minimize the need for congestion control. The CBOR Sequence does not include any array wrapper. ๐ŸŽ

A 4.08 (Request Entity Incomplete) response with content type "application/missing-blocks+cbor-seq" SHOULD NOT be used when using Confirmable requests or a reliable connection [RFC8323], because the client will be able to determine that there is a transmission failure of a particular payload, and hence that the server is missing that payload. โœ…

6. The Use of Tokens ๐ŸŽ€

okay so bestie, every brand new request generally gets its own fresh Token ๐Ÿฆ‹ (and sometimes it must, see Section 4 of [RFC9175]). Any extra responses to a request all wear the same Token as the request they're answering, so you can always tell who's replying to who ๐Ÿ’…

Implementation Note: 8-byte tokens are lowkey genius ๐Ÿงš You can keep the bottom 32 bits the same for the same body, and let the upper 32 bits hold the current body's request number, bumping it on every request (retransmits count too! ๐Ÿ™ˆ). That way clients don't have to track all the per-request state, e.g., per Section 3 of [RFC8974] ๐Ÿ’– But heads up: if you're using NoSec, Section 5.2 of [RFC8974] has to be considered for the security implications ๐Ÿ’€

7. Congestion Control for Unreliable Transports ๐ŸŒŠ

so over an unreliable transport, every single block of one body MUST either all be Confirmable or all be Non-confirmable ๐Ÿซถ Keeping them all the same type is what makes the congestion control procedure way simpler, it's giving clean โœจ

quick reminder bestie: reliable transports don't need CoAP-specific congestion control at all, see [RFC8323] ๐ŸŒธ

7.1. Confirmable (CON) ๐Ÿ’œ

congestion control for CON requests and responses is spelled out in Section 4.7 of [RFC7252] ๐Ÿ“š If you want the faster transmission rates, NSTART has to go up from 1. BUT, and this is a big but ๐Ÿ˜ญ, the other CON congestion control parameters will need retuning to match. This document doesn't specify that tuning, because the applicability scope here assumes that all requests and responses using Q-Block1 and Q-Block2 will be Non-confirmable (Section 3.2) apart from the initial Q-Block functionality negotiation ๐Ÿ“

after a packet fails to transmit because it got lost, and MAX_TRANSMIT_SPAN time has passed (Section 4.8.2 of [RFC7252]), it's up to the implementation whether to send any further requests for the missing data ๐Ÿคทโ€โ™€๏ธโœจ

7.2. Non-confirmable (NON) ๐Ÿฆ‹

okay bestie, this part is where we get into the NON flavour of things ๐Ÿ’… It brings in a whole crew of new parameters: MAX_PAYLOADS, NON_TIMEOUT, NON_TIMEOUT_RANDOM, NON_RECEIVE_TIMEOUT, NON_MAX_RETRANSMIT, NON_PROBING_WAIT, and NON_PARTIAL_TIMEOUT. They're mainly for NON (Table 3) โœจ

Note: Randomness can naturally come from the traffic profile, from how PROBING_RATE gets computed (since it's an average), and from when the peer responds. Randomness is explicitly added to some of the congestion control parameters so that everything doesn't stay in sync when retrying ๐Ÿ™ˆ

MAX_PAYLOADS is configurable, with a default value of 10 ๐ŸŽ€ Both CoAP endpoints MUST have the same value, because otherwise you get transmission delays in one direction. The value MAY be negotiated between the endpoints to a common value using a higher-level protocol, which is out of scope for this document. This is the maximum number of payloads that can be transmitted at any one time.

Note: The default of 10 is picked for reasons similar to the ones in Section 5 of [RFC6928], especially given the target application in Section 3.2 ๐ŸŒธ

NON_TIMEOUT is used to work out the delay between sending MAX_PAYLOADS_SET for the same body. By default, NON_TIMEOUT has the same value as ACK_TIMEOUT (Section 4.8 of [RFC7252]) ๐Ÿ’œ

NON_TIMEOUT_RANDOM is the initial actual delay between sending the first two MAX_PAYLOADS_SETs of the same body. The same delay is then reused for the subsequent MAX_PAYLOADS_SETs. It's a random duration (not a whole number of seconds) between NON_TIMEOUT and (NON_TIMEOUT * ACK_RANDOM_FACTOR). ACK_RANDOM_FACTOR is set to 1.5, as discussed in Section 4.8 of [RFC7252] โœจ

NON_RECEIVE_TIMEOUT is the initial time to wait for a missing payload before requesting retransmission for the first time. Every time the missing payload gets re-requested, the Time-to-Wait value doubles ๐Ÿงš The time to wait is calculated like this:

Time-to-Wait = NON_RECEIVE_TIMEOUT * (2 ** (Re-Request-Count - 1))

NON_RECEIVE_TIMEOUT has a default value of twice NON_TIMEOUT. NON_RECEIVE_TIMEOUT MUST always be greater than NON_TIMEOUT_RANDOM by at least one second, so the sender of the payloads has the chance to start sending the next MAX_PAYLOADS_SET before the receiver times out ๐Ÿซถ

NON_MAX_RETRANSMIT is the maximum number of times a request for retransmission of missing payloads can happen without a response from the remote peer. After that happens, the local endpoint SHOULD consider the body stale, remove any body, and release the tokens and Request-Tag on the client (or the ETag on the server). By default, NON_MAX_RETRANSMIT has the same value as MAX_RETRANSMIT (Section 4.8 of [RFC7252]) ๐Ÿ“

NON_PROBING_WAIT is used to limit the potential wait needed when using PROBING_RATE. By default, it's computed in a way that's similar to EXCHANGE_LIFETIME (Section 4.8.2 of [RFC7252]), but with ACK_TIMEOUT, MAX_RETRANSMIT, and PROCESSING_DELAY swapped out for NON_TIMEOUT, NON_MAX_RETRANSMIT, and NON_TIMEOUT_RANDOM, respectively:

NON_PROBING_WAIT = NON_TIMEOUT * ((2 ** NON_MAX_RETRANSMIT) - 1) *
ACK_RANDOM_FACTOR + (2 * MAX_LATENCY) + NON_TIMEOUT_RANDOM

NON_PARTIAL_TIMEOUT is used for expiring partially received bodies. By default, it's computed the same way as EXCHANGE_LIFETIME (Section 4.8.2 of [RFC7252]), but with ACK_TIMEOUT and MAX_RETRANSMIT swapped out for NON_TIMEOUT and NON_MAX_RETRANSMIT, respectively:

NON_PARTIAL_TIMEOUT = NON_TIMEOUT * ((2 ** NON_MAX_RETRANSMIT) -
1) * ACK_RANDOM_FACTOR + (2 * MAX_LATENCY) + NON_TIMEOUT

Here's the parameter cheat sheet ๐Ÿ“‹

                +=====================+===================+
                | Parameter Name      | Default Value     |
                +=====================+===================+
                | MAX_PAYLOADS        | 10                |
                +---------------------+-------------------+
                | NON_MAX_RETRANSMIT  | 4                 |
                +---------------------+-------------------+
                | NON_TIMEOUT         | 2 s               |
                +---------------------+-------------------+
                | NON_TIMEOUT_RANDOM  | between 2-3 s     |
                +---------------------+-------------------+
                | NON_RECEIVE_TIMEOUT | 4 s               |
                +---------------------+-------------------+
                | NON_PROBING_WAIT    | between 247-248 s |
                +---------------------+-------------------+
                | NON_PARTIAL_TIMEOUT | 247 s             |
                +---------------------+-------------------+

Table 3: Congestion Control Parameters

Now PROBING_RATE ๐Ÿฆ‹ It's the parameter that says the average data rate a CoAP endpoint must not go over when it's sending to a peer that doesn't respond. A single body gets subjected to PROBING_RATE (Section 4.7 of [RFC7252]), not the individual packets. If the wait time between sending bodies that aren't being responded to, based on PROBING_RATE, goes over NON_PROBING_WAIT, then the wait time gets capped at NON_PROBING_WAIT โœจ

Note: For the DOTS application in particular, PROBING_RATE and other transmission parameters are negotiated between peers. Even when they aren't negotiated, the DOTS application uses customized defaults, as discussed in Section 4.5.2 of [RFC9132]. Also, MAX_PAYLOADS, NON_MAX_RETRANSMIT, NON_TIMEOUT, NON_PROBING_WAIT, and NON_PARTIAL_TIMEOUT can be negotiated between DOTS peers, e.g., as per [DOTS-QUICK-BLOCKS]. When explicit values are configured for NON_PROBING_WAIT and NON_PARTIAL_TIMEOUT, those values get used without any jitter ๐ŸŽ€

Every NON 4.08 (Request Entity Incomplete) response is subject to PROBING_RATE ๐Ÿ’€ And so is every NON GET or FETCH request that uses a Q-Block2 option.

Sending lots of payloads for one body can cause congestion all by itself, so after every MAX_PAYLOADS_SET of a single body, a delay of NON_TIMEOUT_RANDOM MUST be introduced before the next MAX_PAYLOADS_SET gets sent. The exception is if a 'Continue' comes back from the peer for the current MAX_PAYLOADS_SET. In that case, the next MAX_PAYLOADS_SET MAY start transmitting immediately ๐Ÿƒโ€โ™€๏ธ

Note: Say the packets are 1500 bytes and the MAX_PAYLOADS_SET has 10 payloads. That works out to 1500 * 10 * 8 = 120 kbits. With a 2 second delay between MAX_PAYLOADS_SETs, that means an average speed requirement of 60 kbps for a single body if there are no responses at all. PROBING_RATE cuts that transmission rate down even further ๐Ÿ“

Sending a set of missing blocks for a body is limited to one MAX_PAYLOADS_SET at a time. So, a NON_TIMEOUT_RANDOM delay still gets observed between each MAX_PAYLOADS_SET ๐ŸŒˆ

For Q-Block1, if the server responds with a 2.31 (Continue) response code for the latest payload sent, the client can keep going with the next MAX_PAYLOADS_SET without any extra delay. But if the server responds with a 4.08 (Request Entity Incomplete) response code, the missing payloads SHOULD be retransmitted before the client goes into another NON_TIMEOUT_RANDOM delay before sending the next set of payloads ๐Ÿซ 

For the server receiving NON Q-Block1 requests, it SHOULD send back a 2.31 (Continue) response code on receipt of all of the MAX_PAYLOADS_SET, so the client doesn't unnecessarily hold up the transfer of the remaining blocks ๐Ÿ’– If not all of the MAX_PAYLOADS_SET arrived, the server SHOULD wait for NON_RECEIVE_TIMEOUT (exponentially scaled based on the repeat request count for a payload) before sending the 4.08 (Request Entity Incomplete) response code for the missing payload(s). If all of the MAX_PAYLOADS_SET arrived and a 2.31 (Continue) response code had been sent, but no more payloads showed up for NON_RECEIVE_TIMEOUT (exponentially scaled), the server SHOULD send a 4.08 (Request Entity Incomplete) response detailing the missing payloads after the block number that was indicated in the sent 2.31 (Continue) response code. And if the repeat response count of the 4.08 (Request Entity Incomplete) goes over NON_MAX_RETRANSMIT, the server SHOULD discard the partial body and stop requesting the missing payloads ๐Ÿ˜ญ

It's likely the client will start sending the next MAX_PAYLOADS_SET before the server times out waiting for the last block of the previous one ๐Ÿ‘€ When a payload from the next MAX_PAYLOADS_SET arrives, the server SHOULD send a 4.08 (Request Entity Incomplete) response code that lists any missing payloads from earlier MAX_PAYLOADS_SETs. When the client gets that 4.08 (Request Entity Incomplete) response code, it SHOULD send the missing payloads before carrying on with the rest of the MAX_PAYLOADS_SET, and then go into another NON_TIMEOUT_RANDOM delay before sending the next MAX_PAYLOADS_SET ๐ŸŽ€

For the client receiving NON Q-Block2 responses, it SHOULD send a 'Continue' Q-Block2 request (Section 4.4) for the next MAX_PAYLOADS_SET as soon as it has all of the current one. That stops the server from unnecessarily holding up the transfer of the remaining blocks ๐Ÿ’… Otherwise, the client SHOULD wait for NON_RECEIVE_TIMEOUT (exponentially scaled based on the repeat request count for a payload) before sending the request for the missing payload(s). If the repeat request count for a missing payload goes over NON_MAX_RETRANSMIT, the client SHOULD discard the partial body and stop requesting the missing payloads ๐Ÿ™ˆ

The server SHOULD recognize the 'Continue' Q-Block2 request as defined in Section 4.4, and just keep transmitting the body (including the Observe option, if that's appropriate for an unsolicited response). It shouldn't treat 'Continue' as a request for the remaining missing blocks ๐Ÿฆ‹

Same energy from the other side: it's likely the server will start sending the next MAX_PAYLOADS_SET before the client times out waiting for the last block of the previous one โญ When the client gets a payload from the new MAX_PAYLOADS_SET, it SHOULD send a request indicating any missing payloads from earlier MAX_PAYLOADS_SETs. When the server gets that request, it SHOULD send the missing payloads before continuing with the rest of the MAX_PAYLOADS_SET, and then go into another NON_TIMEOUT_RANDOM delay before sending the next MAX_PAYLOADS_SET ๐ŸŒธ

Good news: the client does not need to acknowledge receipt of the entire body ๐Ÿซถ

Note: If there's asymmetric traffic loss that stops responses from ever getting received, a NON_TIMEOUT_RANDOM delay will be observed after every transmission of MAX_PAYLOADS_SET. Even so, the endpoint receiving the body is still likely to receive the entire body ๐ŸŒˆ

8. Caching Considerations ๐Ÿ’พ

okay so caching block-based stuff is not straightforward in a proxy ๐Ÿ˜ญ for the Q-Block1 and Q-Block2 options, to keep things simple, the proxy is expected to reassemble the whole body (using any appropriate recovery options for packet loss) before passing it onward to the right CoAP endpoint. this doesn't stop an implementation from doing fancier per-payload caching ๐Ÿง , but how to do that is out of scope for this doc. sending the body onward doesn't need Q-Block1 or Q-Block2 either, because those options might not be supported on that link. so the proxy must fully support Q-Block1 and Q-Block2 ๐Ÿ’…

how the body gets cached in the CoAP client (for Q-Block1 transmissions) or the CoAP server (for Q-Block2 transmissions) is implementation specific, bestie ๐ŸŽ€

since the proxy caches the entire body, the Q-Block1 and Q-Block2 options get removed during block assembly, so they never reach the cache โœจ

for Q-Block2 responses, the ETag option value is tied to the data and sent onward to the CoAP client, but it is NOT part of the cache key ๐Ÿฆ‹

for requests with the Q-Block1 option, the Request-Tag option is linked to building the body from successive payloads, but it's also not part of the cache key ๐Ÿ™ˆ for the onward transmission of the body over CoAP, a new Request-Tag SHOULD be generated and used. ideally that new Request-Tag replaces the one the client used ๐ŸŒธ

here's the tea: it's possible that two or more CoAP clients are updating the same resource at the same time, going through a common proxy to the same CoAP server, using the Q-Block1 (or Block1) option. if that happens, the first client to finish building its body makes the proxy start transmitting that body to the CoAP server with an appropriate Request-Tag value. when the next client finishes building its body, any partial body transmission already in progress to the CoAP server gets terminated, and the transmission of the new body representation starts with a new Request-Tag value ๐Ÿ”ฅ also, it cannot be assumed that the proxy will always receive a complete body from a client, so keep that in mind ๐Ÿ‘€

a proxy that supports the Q-Block2 option MUST be prepared to receive a GET or similar request that indicates one or more missing blocks. from its cache, the proxy will serve whichever missing blocks it has, the same way a server would send all the appropriate Q-Block2 responses ๐Ÿ’– if a body matching the cache key isn't in the cache, the proxy MUST request the entire body from the CoAP server using the information in the cache key. no skipping that step, periodt ๐Ÿ’…

how long a CoAP endpoint (or proxy) keeps the body in its cache is implementation specific (for example, it might be based on Max-Age) โณ


9. HTTP Mapping Considerations ๐ŸŒˆ

quick reminder: the basic normative requirements on HTTP/CoAP mappings are defined in Section 10 of [RFC7252]. the implementation guidelines for HTTP/CoAP mappings are elaborated in [RFC8075] ๐Ÿ“š

the rules defined in Section 5 of [RFC7959] are to be followed ๐Ÿซถ


10. Examples with Non-confirmable Messages ๐Ÿฆ‹

this section gives some sample flows to show how the Q-Block1 and Q-Block2 options work with NON. examples with CON are in Appendix A ๐Ÿ’œ

the examples in the following subsections assume MAX_PAYLOADS is set to 10 and NON_MAX_RETRANSMIT is set to 4 ๐Ÿ“

here's the cheat sheet for the figures in the next subsections, so you can read them like a pro ๐Ÿ‘‘

T:     Token value

O:     Observe option value

M:     Message ID

RT:    Request-Tag

ET:    ETag

QB1:   Q-Block1 option values NUM/More/Size

QB2:   Q-Block2 option values NUM/More/Size

Size:  Actual block size encoded in SZX

\:     Trimming long lines

[[]]:  Comments

-->X:  Message loss (request)

X<--:  Message loss (response)

...:   Passage of time

Payload N:  Corresponds to the CoAP message that conveys a block
       number (N-1) of a given block-wise exchange.

10.1. Q-Block1 Option โœจ

10.1.1. A Simple Example ๐ŸŽ€

okay so Figure 2 shows an example of a NON PUT request carrying the Q-Block1 option. all the blocks reach the server, no drama ๐ŸŒธ

 CoAP        CoAP
Client      Server
  |          |
  +--------->| NON PUT /path M:0x81 T:0xc0 RT=9 QB1:0/1/1024
  +--------->| NON PUT /path M:0x82 T:0xc1 RT=9 QB1:1/1/1024
  +--------->| NON PUT /path M:0x83 T:0xc2 RT=9 QB1:2/1/1024
  +--------->| NON PUT /path M:0x84 T:0xc3 RT=9 QB1:3/0/1024
  |<---------+ NON 2.04 M:0xf1 T:0xc3
  |   ...    |

     Figure 2: Example of a NON Request with the Q-Block1 option
                            (without Loss)

10.1.2. Handling MAX_PAYLOADS Limits ๐Ÿงš

Figure 3 shows an example of a NON PUT request carrying the Q-Block1 option. the number of payloads goes over MAX_PAYLOADS, but all the blocks still reach the server ๐Ÿ’…

  CoAP        CoAP
 Client      Server
   |          |
   +--------->| NON PUT /path M:0x01 T:0xf1 RT=10 QB1:0/1/1024
   +--------->| NON PUT /path M:0x02 T:0xf2 RT=10 QB1:1/1/1024
   +--------->| [[Payloads 3 - 9 not detailed]]
   +--------->| NON PUT /path M:0x0a T:0xfa RT=10 QB1:9/1/1024
[[MAX_PAYLOADS_SET has been received]]
   |     [[MAX_PAYLOADS_SET receipt acknowledged by server]]
   |<---------+ NON 2.31 M:0x81 T:0xfa
   +--------->| NON PUT /path M:0x0b T:0xfb RT=10 QB1:10/0/1024
   |<---------+ NON 2.04 M:0x82 T:0xfb
   |   ...    |

  Figure 3: Example of a MAX_PAYLOADS NON Request with the Q-Block1
                        Option (without Loss)

10.1.3. Handling MAX_PAYLOADS with Recovery ๐Ÿ”

now imagine the client is sending a brand new body, but some blocks get dropped on the way, like in Figure 4 ๐Ÿ˜ฌ

  CoAP        CoAP
 Client      Server
   |          |
   +--------->| NON PUT /path M:0x11 T:0xe1 RT=11 QB1:0/1/1024
   +--->X     | NON PUT /path M:0x12 T:0xe2 RT=11 QB1:1/1/1024
   +--------->| [[Payloads 3 - 8 not detailed]]
   +--------->| NON PUT /path M:0x19 T:0xe9 RT=11 QB1:8/1/1024
   +--->X     | NON PUT /path M:0x1a T:0xea RT=11 QB1:9/1/1024
   [[Some of the MAX_PAYLOADS_SET has been received]]
   |   ...    |
[[NON_TIMEOUT_RANDOM (client) delay expires]]
   |     [[Client starts sending next MAX_PAYLOADS_SET]]
   +--->X     | NON PUT /path M:0x1b T:0xeb RT=11 QB1:10/1/1024
   +--------->| NON PUT /path M:0x1c T:0xec RT=11 QB1:11/1/1024
   |          |

  Figure 4: Example of a MAX_PAYLOADS NON Request with the Q-Block1
                          Option (with Loss)

when the server sees a payload from the next MAX_PAYLOADS_SET, it realizes some blocks from the previous MAX_PAYLOADS_SET are missing. so it asks for all the missing blocks in one go (Figure 5) ๐Ÿง  it does this by listing which blocks from the previous MAX_PAYLOADS_SET haven't been received, in the data portion of the response (Section 5). the token used in the response should be the token that was used in the last received payload ๐Ÿซถ the client can then work out the Request-Tag by matching the token with the sent request โœจ

  CoAP        CoAP
 Client      Server
   |          |
   |<---------+ NON 4.08 M:0x91 T:0xec [Missing 1,9]
   |     [[Client responds with missing payloads]]
   +--------->| NON PUT /path M:0x1d T:0xed RT=11 QB1:1/1/1024
   +--------->| NON PUT /path M:0x1e T:0xee RT=11 QB1:9/1/1024
   |     [[Client continues sending next MAX_PAYLOADS_SET]]
   +--------->| NON PUT /path M:0x1f T:0xef RT=11 QB1:12/0/1024
   |   ...    |
[[NON_RECEIVE_TIMEOUT (server) delay expires]]
   |     [[The server realizes a block is still missing and asks
   |        for the missing one]]
   |<---------+ NON 4.08 M:0x92 T:0xef [Missing 10]
   +--------->| NON PUT /path M:0x20 T:0xf0 RT=11 QB1:10/1/1024
   |<---------+ NON 2.04 M:0x93 T:0xf0
   |   ...    |

      Figure 5: Example of a NON Request with the Q-Block1 Option
                            (Block Recovery)

10.1.4. Handling Recovery if Failure Occurs ๐Ÿ’€

Figure 6 shows a NON PUT request carrying the Q-Block1 option where recovery kicks in but eventually fails ๐Ÿ˜ญ

  CoAP        CoAP
 Client      Server
   |          |
   +--------->| NON PUT /path M:0x91 T:0xd0 RT=12 QB1:0/1/1024
   +--->X     | NON PUT /path M:0x92 T:0xd1 RT=12 QB1:1/1/1024
   +--------->| NON PUT /path M:0x93 T:0xd2 RT=12 QB1:2/0/1024
   |   ...    |
[[NON_RECEIVE_TIMEOUT (server) delay expires]]
   |     [[The server realizes a block is missing and asks
   |        for the missing one.  Retry #1]]
   |<---------+ NON 4.08 M:0x01 T:0xd2 [Missing 1]
   |   ...    |
[[2 * NON_RECEIVE_TIMEOUT (server) delay expires]]
   |     [[The server realizes a block is still missing and asks
   |        for the missing one.  Retry #2]]
   |<---------+ NON 4.08 M:0x02 T:0xd2 [Missing 1]
   |   ...    |
[[4 * NON_RECEIVE_TIMEOUT (server) delay expires]]
   |     [[The server realizes a block is still missing and asks
   |        for the missing one.  Retry #3]]
   |<---------+ NON 4.08 M:0x03 T:0xd2 [Missing 1]
   |   ...    |
[[8 * NON_RECEIVE_TIMEOUT (server) delay expires]]
   |     [[The server realizes a block is still missing and asks
   |        for the missing one.  Retry #4]]
   |<---------+ NON 4.08 M:0x04 T:0xd2 [Missing 1]
   |   ...    |
[[16 * NON_RECEIVE_TIMEOUT (server) delay expires]]
   |     [[NON_MAX_RETRANSMIT exceeded.  Server stops requesting
   |       the missing blocks and releases partial body]]
   |   ...    |

   Figure 6: Example of a NON Request with the Q-Block1 Option (with
                           Eventual Failure)

10.2. Q-Block2 Option ๐Ÿ“

these examples include the Observe option so you can see how it's used ๐Ÿ‘€ but heads up: the Observe option is not required for Q-Block2 ๐Ÿ™ˆ

10.2.1. A Simple Example ๐ŸŒˆ

Figure 7 shows an example of the Q-Block2 option. the client sends a NON GET carrying the Observe and Q-Block2 options. the Q-Block2 option gives a block size hint (1024 bytes). the server replies using four (4) blocks, which get sent to the client with no loss, and each block carries a Q-Block2 option ๐Ÿ’– the same thing happens again when an Observe is triggered, and none of the notification blocks get lost either โœจ

 CoAP        CoAP
Client      Server
  |          |
  +--------->| NON GET /path M:0x01 T:0xc0 O:0 QB2:0/1/1024
  |<---------+ NON 2.05 M:0xf1 T:0xc0 O:1220 ET=19 QB2:0/1/1024
  |<---------+ NON 2.05 M:0xf2 T:0xc0 O:1220 ET=19 QB2:1/1/1024
  |<---------+ NON 2.05 M:0xf3 T:0xc0 O:1220 ET=19 QB2:2/1/1024
  |<---------+ NON 2.05 M:0xf4 T:0xc0 O:1220 ET=19 QB2:3/0/1024
  |   ...    |
  |     [[Observe triggered]]
  |<---------+ NON 2.05 M:0xf5 T:0xc0 O:1221 ET=20 QB2:0/1/1024
  |<---------+ NON 2.05 M:0xf6 T:0xc0 O:1221 ET=20 QB2:1/1/1024
  |<---------+ NON 2.05 M:0xf7 T:0xc0 O:1221 ET=20 QB2:2/1/1024
  |<---------+ NON 2.05 M:0xf8 T:0xc0 O:1221 ET=20 QB2:3/0/1024
  |   ...    |

   Figure 7: Example of NON Notifications with the Q-Block2 Option
                            (without Loss)

10.2.2. Handling MAX_PAYLOADS Limits โœจ

Okay bestie, Figure 8 is the same vibe as Figure 7, but now there are eleven (11) payloads, which is over MAX_PAYLOADS ๐Ÿ’… And the best part? Zero loss. Everything arrives. Iconic ๐Ÿฆ‹

     CoAP        CoAP
    Client      Server
      |          |
      +--------->| NON GET /path M:0x01 T:0xf0 O:0 QB2:0/1/1024
      |<---------+ NON 2.05 M:0x81 T:0xf0 O:1234 ET=21 QB2:0/1/1024
      |<---------+ NON 2.05 M:0x82 T:0xf0 O:1234 ET=21 QB2:1/1/1024
      |<---------+ [[Payloads 3 - 9 not detailed]]
      |<---------+ NON 2.05 M:0x8a T:0xf0 O:1234 ET=21 QB2:9/1/1024
   [[MAX_PAYLOADS_SET has been received]]
      |     [[MAX_PAYLOADS_SET acknowledged by client using
      |       'Continue' Q-Block2]]
      +--------->| NON GET /path M:0x02 T:0xf1 QB2:10/1/1024
      |<---------+ NON 2.05 M:0x8b T:0xf0 O:1234 ET=21 QB2:10/0/1024
      |   ...    |
      |     [[Observe triggered]]
      |<---------+ NON 2.05 M:0x91 T:0xf0 O:1235 ET=22 QB2:0/1/1024
      |<---------+ NON 2.05 M:0x92 T:0xf0 O:1235 ET=22 QB2:1/1/1024
      |<---------+ [[Payloads 3 - 9 not detailed]]
      |<---------+ NON 2.05 M:0x9a T:0xf0 O:1235 ET=22 QB2:9/1/1024
   [[MAX_PAYLOADS_SET has been received]]
      |     [[MAX_PAYLOADS_SET acknowledged by client using
      |       'Continue' Q-Block2]]
      +--------->| NON GET /path M:0x03 T:0xf2 QB2:10/1/1024
      |<---------+ NON 2.05 M:0x9b T:0xf0 O:1235 ET=22 QB2:10/0/1024
   [[Body has been received]]
      |   ...    |

      Figure 8: Example of NON Notifications with the Q-Block2 Option
                               (without Loss)

10.2.3. Handling MAX_PAYLOADS with Recovery ๐Ÿ”ฅ

Figure 9 is where things get a little messy ๐Ÿ˜ญ An Observe gets triggered, but some of the notification blocks go missing in transit. No panic though, because the client notices the gaps and asks for them again. It does this by listing the missing blocks as one or more Q-Block2 options. Main character energy for sure ๐Ÿ‘‘

     CoAP        CoAP
    Client      Server
      |   ...    |
      |     [[Observe triggered]]
      |<---------+ NON 2.05 M:0xa1 T:0xf0 O:1236 ET=23 QB2:0/1/1024
      |     X<---+ NON 2.05 M:0xa2 T:0xf0 O:1236 ET=23 QB2:1/1/1024
      |<---------+ [[Payloads 3 - 9 not detailed]]
      |     X<---+ NON 2.05 M:0xaa T:0xf0 O:1236 ET=23 QB2:9/1/1024
   [[Some of the MAX_PAYLOADS_SET has been received]]
      |   ...    |
   [[NON_TIMEOUT_RANDOM (server) delay expires]]
      |     [[Server sends next MAX_PAYLOADS_SET]]
      |<---------+ NON 2.05 M:0xab T:0xf0 O:1236 ET=23 QB2:10/0/1024
      |     [[On seeing a payload from the next MAX_PAYLOADS_SET,
      |       client realizes blocks are missing and asks for the
      |       missing ones in one go]]
      +--------->| NON GET /path M:0x04 T:0xf3 QB2:1/0/1024\
      |          |                             QB2:9/0/1024
      |     X<---+ NON 2.05 M:0xac T:0xf3 ET=23 QB2:1/1/1024
      |<---------+ NON 2.05 M:0xad T:0xf3 ET=23 QB2:9/1/1024
      |   ...    |
   [[NON_RECEIVE_TIMEOUT (client) delay expires]]
      |     [[Client realizes block is still missing and asks for
      |       missing block]]
      +--------->| NON GET /path M:0x05 T:0xf4 QB2:1/0/1024
      |<---------+ NON 2.05 M:0xae T:0xf4 ET=23 QB2:1/1/1024
   [[Body has been received]]
      |   ...    |

      Figure 9: Example of NON Notifications with the Q-Block2 Option
                              (Block Recovery)

10.2.4. Handling Recovery by Setting the M Bit ๐Ÿงš

Figure 10 shows a case where an Observe is triggered, but only the first two notification blocks actually reach the client ๐Ÿฅบ To get the missing blocks back, the client sends one request with a single Q-Block2 option that has the M bit set. One ask, all the blocks. Efficient and slay ๐Ÿ’–

     CoAP        CoAP
    Client      Server
      |   ...    |
      |     [[Observe triggered]]
      |<---------+ NON 2.05 M:0xb1 T:0xf0 O:1237 ET=24 QB2:0/1/1024
      |<---------+ NON 2.05 M:0xb2 T:0xf0 O:1237 ET=24 QB2:1/1/1024
      |     X<---+ NON 2.05 M:0xb3 T:0xf0 O:1237 ET=24 QB2:2/1/1024
      |     X<---+ [[Payloads 4 - 9 not detailed]]
      |     X<---+ NON 2.05 M:0xb9 T:0xf0 O:1237 ET=24 QB2:9/1/1024
   [[Some of the MAX_PAYLOADS_SET has been received]]
      |   ...    |
   [[NON_TIMEOUT_RANDOM (server) delay expires]]
      |     [[Server sends next MAX_PAYLOADS_SET]]
      |     X<---+ NON 2.05 M:0xba T:0xf0 O:1237 ET=24 QB2:10/0/1024
      |   ...    |
   [[NON_RECEIVE_TIMEOUT (client) delay expires]]
      |     [[Client realizes blocks are missing and asks for the
      |       missing ones in one go by setting the M bit]]
      +--------->| NON GET /path M:0x06 T:0xf5 QB2:2/1/1024
      |<---------+ NON 2.05 M:0xbb T:0xf5 ET=24 QB2:2/1/1024
      |<---------+ [[Payloads 3 - 9 not detailed]]
      |<---------+ NON 2.05 M:0xc2 T:0xf5 ET=24 QB2:9/1/1024
   [[MAX_PAYLOADS_SET has been received]]
      |     [[MAX_PAYLOADS_SET acknowledged by client using 'Continue'
      |       Q-Block2]]
      +--------->| NON GET /path M:0x87 T:0xf6 QB2:10/1/1024
      |<---------+ NON 2.05 M:0xc3 T:0xf0 O:1237 ET=24 QB2:10/0/1024
   [[Body has been received]]
      |   ...    |

      Figure 10: Example of NON Notifications with the Q-Block2 Option
                    (Block Recovery with the M Bit Set)

10.3. Q-Block1 and Q-Block2 Options ๐ŸŒˆ

10.3.1. A Simple Example ๐Ÿ’œ

Figure 11 is a FETCH that uses both the Q-Block1 and Q-Block2 options, plus an Observe option, all at once ๐Ÿคฏ Nothing gets lost here, so it's a clean, smooth flow. Literally goals ๐Ÿ“

    CoAP        CoAP
   Client      Server
     |          |
     +--------->| NON FETCH /path M:0x10 T:0x90 O:0 RT=30 QB1:0/1/1024
     +--------->| NON FETCH /path M:0x11 T:0x91 O:0 RT=30 QB1:1/1/1024
     +--------->| NON FETCH /path M:0x12 T:0x93 O:0 RT=30 QB1:2/0/1024
     |<---------+ NON 2.05 M:0x60 T:0x93 O:1320 ET=90 QB2:0/1/1024
     |<---------+ NON 2.05 M:0x61 T:0x93 O:1320 ET=90 QB2:1/1/1024
     |<---------+ NON 2.05 M:0x62 T:0x93 O:1320 ET=90 QB2:2/1/1024
     |<---------+ NON 2.05 M:0x63 T:0x93 O:1320 ET=90 QB2:3/0/1024
     |   ...    |
     |     [[Observe triggered]]
     |<---------+ NON 2.05 M:0x64 T:0x93 O:1321 ET=91 QB2:0/1/1024
     |<---------+ NON 2.05 M:0x65 T:0x93 O:1321 ET=91 QB2:1/1/1024
     |<---------+ NON 2.05 M:0x66 T:0x93 O:1321 ET=91 QB2:2/1/1024
     |<---------+ NON 2.05 M:0x67 T:0x93 O:1321 ET=91 QB2:3/0/1024
     |   ...    |

      Figure 11: Example of a NON FETCH with the Q-Block1 and Q-Block2
                           Options (without Loss)

10.3.2. Handling MAX_PAYLOADS Limits ๐ŸŽ€

Figure 12 runs the same scenario as Figure 11, but now there are eleven (11) payloads in both directions, which is over MAX_PAYLOADS ๐Ÿ™ˆ Still no loss, so everyone's fine.

     CoAP        CoAP
    Client      Server
      |          |
      +--------->| NON FETCH /path M:0x30 T:0xa0 O:0 RT=10 QB1:0/1/1024
      +--------->| NON FETCH /path M:0x31 T:0xa1 O:0 RT=10 QB1:1/1/1024
      +--------->| [[Payloads 3 - 9 not detailed]]
      +--------->| NON FETCH /path M:0x39 T:0xa9 O:0 RT=10 QB1:9/1/1024
   [[MAX_PAYLOADS_SET has been received]]
      |     [[MAX_PAYLOADS_SET acknowledged by server]]
      |<---------+ NON 2.31 M:0x80 T:0xa9
      +--------->| NON FETCH /path M:0x3a T:0xaa O:0 RT=10 QB1:10/0/1024
      |<---------+ NON 2.05 M:0x81 T:0xaa O:1334 ET=21 QB2:0/1/1024
      |<---------+ NON 2.05 M:0x82 T:0xaa O:1334 ET=21 QB2:1/1/1024
      |<---------+ [[Payloads 3 - 9 not detailed]]
      |<---------+ NON 2.05 M:0x8a T:0xaa O:1334 ET=21 QB2:9/1/1024
   [[MAX_PAYLOADS_SET has been received]]
      |     [[MAX_PAYLOADS_SET acknowledged by client using
      |       'Continue' Q-Block2]]
      +--------->| NON FETCH /path M:0x3b T:0xab QB2:10/1/1024
      |<---------+ NON 2.05 M:0x8b T:0xaa O:1334 ET=21 QB2:10/0/1024
      |   ...    |
      |     [[Observe triggered]]
      |<---------+ NON 2.05 M:0x8c T:0xaa O:1335 ET=22 QB2:0/1/1024
      |<---------+ NON 2.05 M:0x8d T:0xaa O:1335 ET=22 QB2:1/1/1024
      |<---------+ [[Payloads 3 - 9 not detailed]]
      |<---------+ NON 2.05 M:0x95 T:0xaa O:1335 ET=22 QB2:9/1/1024
   [[MAX_PAYLOADS_SET has been received]]
      |     [[MAX_PAYLOADS_SET acknowledged by client using
      |       'Continue' Q-Block2]]
      +--------->| NON FETCH /path M:0x3c T:0xac QB2:10/1/1024
      |<---------+ NON 2.05 M:0x96 T:0xaa O:1335 ET=22 QB2:10/0/1024
   [[Body has been received]]
      |   ...    |

      Figure 12: Example of a NON FETCH with the Q-Block1 and Q-Block2
                           Options (without Loss)

Quick note ๐Ÿ“ Because 'Continue' was used, the server keeps the same token (0xaa). That's because 'Continue' isn't a request for a brand new set of packets. It's just the server being told to keep going with its transmission (Section 7.2) โœจ

10.3.3. Handling Recovery โœจ

okay so now picture this: some blocks just vanish into the void ๐Ÿ’€ on the way over. Figure 13 shows exactly that mess.

     CoAP        CoAP
    Client      Server
      |          |
      +--------->| NON FETCH /path M:0x50 T:0xc0 O:0 RT=31 QB1:0/1/1024
      +--->X     | NON FETCH /path M:0x51 T:0xc1 O:0 RT=31 QB1:1/1/1024
      +--->X     | NON FETCH /path M:0x52 T:0xc2 O:0 RT=31 QB1:2/1/1024
      +--------->| NON FETCH /path M:0x53 T:0xc3 O:0 RT=31 QB1:3/0/1024
      |   ...    |
   [[NON_RECEIVE_TIMEOUT (server) delay expires]]

      Figure 13: Example of a NON FETCH with the Q-Block1 and Q-Block2
                            Options (with Loss)

The server is like "wait, where did blocks 1 and 2 go?" ๐Ÿ™ˆ So it asks for all the missing ones in one go (Figure 14). It does that by listing which blocks it hasn't gotten in the data portion of the response. The token on that response is the token from the last payload it actually received. The client can then figure out the Request-Tag by matching that token to the request it sent. Iconic detective work ๐Ÿ•ต๏ธโ€โ™€๏ธ๐Ÿ”ฅ

     CoAP        CoAP
    Client      Server
      |          |
      |<---------+ NON 4.08 M:0xa0 T:0xc3 [Missing 1,2]
      |     [[Client responds with missing payloads]]
      +--------->| NON FETCH /path M:0x54 T:0xc4 O:0 RT=31 QB1:1/1/1024
      +--------->| NON FETCH /path M:0x55 T:0xc5 O:0 RT=31 QB1:2/1/1024
      |     [[Server received FETCH body,
      |       starts transmitting response body]]
      |<---------+ NON 2.05 M:0xa1 T:0xc3 O:1236 ET=23 QB2:0/1/1024
      |     X<---+ NON 2.05 M:0xa2 T:0xc3 O:1236 ET=23 QB2:1/1/1024
      |<---------+ NON 2.05 M:0xa3 T:0xc3 O:1236 ET=23 QB2:2/1/1024
      |     X<---+ NON 2.05 M:0xa4 T:0xc3 O:1236 ET=23 QB2:3/0/1024
      |   ...    |
   [[NON_RECEIVE_TIMEOUT (client) delay expires]]
      |          |

        Figure 14: Example of a NON Request with the Q-Block1 Option
                             (Server Recovery)

Now the client notices that not every payload of the response came back ๐Ÿ˜ญ so it asks for the missing blocks in one go (Figure 15). Note: following Section 2.7 of [RFC7959], the FETCH request does not include the Q-Block1 or any payload. Lowkey a glow-up for minimal requests โœจ

     CoAP        CoAP
    Client      Server
      |          |
      +--------->| NON FETCH /path M:0x56 T:0xc6 RT=31 QB2:1/0/1024\
      |          |                                     QB2:3/0/1024
      |     [[Server receives FETCH request for missing payloads,
      |       starts transmitting missing blocks]]
      |     X<---+ NON 2.05 M:0xa5 T:0xc6 ET=23 QB2:1/1/1024
      |<---------+ NON 2.05 M:0xa6 T:0xc6 ET=23 QB2:3/0/1024
      |   ...    |
   [[NON_RECEIVE_TIMEOUT (client) delay expires]]
      |     [[Client realizes block is still missing and asks for
      |       missing block]]
      +--------->| NON FETCH /path M:0x57 T:0xc7 RT=31 QB2:1/0/1024
      |     [[Server receives FETCH request for missing payload,
      |       starts transmitting missing block]]
      |<---------+ NON 2.05 M:0xa7 T:0xc7 ET=23 QB2:1/1/1024
   [[Body has been received]]
      |   ...    |
      |     [[Observe triggered]]
      |<---------+ NON 2.05 M:0xa8 T:0xc3 O:1337 ET=24 QB2:0/1/1024
      |     X<---+ NON 2.05 M:0xa9 T:0xc3 O:1337 ET=24 QB2:1/1/1024
      |<---------+ NON 2.05 M:0xaa T:0xc3 O:1337 ET=24 QB2:2/0/1024
   [[NON_RECEIVE_TIMEOUT (client) delay expires]]
      |     [[Client realizes block is still missing and asks for
      |       missing block]]
      +--------->| NON FETCH /path M:0x58 T:0xc8 RT=31 QB2:1/0/1024
      |     [[Server receives FETCH request for missing payload,
      |       starts transmitting missing block]]
      |<---------+ NON 2.05 M:0xa7 T:0xc8 ET=24 QB2:1/1/1024
   [[Body has been received]]
      |   ...    |

        Figure 15: Example of a NON Request with the Q-Block1 Option
                             (Client Recovery)

11. Security Considerations ๐Ÿ”

Okay, bestie, this part is serious, so read it with your whole brain ๐Ÿง ๐Ÿ’… Security considerations discussed in Section 7 of [RFC7959] should be taken into account.

Security considerations discussed in Sections 11.3 and 11.4 of [RFC7252] should also be taken into account.

OSCORE gives end-to-end protection to all information that isn't required for proxy operations, and it requires that a security context gets set up (Section 3.1 of [RFC8613]). It can be trusted that the source endpoint is legitimate even if the NoSec mode is used. However, an intermediary node can modify the unprotected Outer Q-Block1 and/or Q-Block2 options to make a Q-Block transfer fail, or to keep requesting all the blocks by setting the M bit, which causes attack amplification. ๐Ÿ˜ฑ As discussed in Section 12.1 of [RFC8613], applications need to consider that certain message fields and message types are not protected end to end and may be spoofed or manipulated. Therefore, it is NOT RECOMMENDED to use the NoSec mode if either the Q-Block1 or Q-Block2 option is used.

If OSCORE is not used, it is also NOT RECOMMENDED to use the NoSec mode if either the Q-Block1 or Q-Block2 option is used. ๐Ÿšซ

If NoSec is being used, Appendix D.5 of [RFC8613] discusses the security analysis and considerations for unprotected message fields, even if OSCORE is not being used.

Security considerations related to the use of Request-Tag are discussed in Section 5 of [RFC9175].


12. IANA Considerations ๐ŸŒธ

12.1. CoAP Option Numbers Registry

IANA has added the following entries to the "CoAP Option Numbers" subregistry [IANA-Options] defined in [RFC7252] within the "Constrained RESTful Environments (CoRE) Parameters" registry:

                     +========+==========+===========+
                     | Number | Name     | Reference |
                     +========+==========+===========+
                     | 19     | Q-Block1 | RFC 9177  |
                     +--------+----------+-----------+
                     | 31     | Q-Block2 | RFC 9177  |
                     +--------+----------+-----------+

                         Table 4: Additions to CoAP
                          Option Numbers Registry

12.2. Media Type Registration

IANA has registered the "application/missing-blocks+cbor-seq" media type in the "Media Types" registry [IANA-MediaTypes]. This registration follows the procedures specified in [RFC6838]. Here's the full template, no cap ๐Ÿ’…

   Type name:  application

   Subtype name:  missing-blocks+cbor-seq

   Required parameters:  N/A

   Optional parameters:  N/A

   Encoding considerations:  Must be encoded as a CBOR Sequence
      [RFC8742], as defined in Section 5 of RFC 9177.

   Security considerations:  See Section 11 of RFC 9177.

   Interoperability considerations:  N/A

   Published specification:  RFC 9177

   Applications that use this media type:  Data serialization and
      deserialization.  In particular, the type is used by applications
      relying upon block-wise transfers, allowing a server to specify
      non-received blocks and request their retransmission, as defined
      in Section 4 of RFC 9177.

   Fragment identifier considerations:  N/A

   Additional information:  N/A

   Person & email address to contact for further information:  IETF,
      iesg@ietf.org

   Intended usage:  COMMON

   Restrictions on usage:  none

   Author:  See Authors' Addresses section of RFC 9177.

   Change controller:  IESG

   Provisional registration?  No

12.3. CoAP Content-Formats Registry

IANA has registered the following CoAP Content-Format for the "application/missing-blocks+cbor-seq" media type in the "CoAP Content-Formats" registry [IANA-Format] defined in [RFC7252] within the "Constrained RESTful Environments (CoRE) Parameters" registry:

   +=====================================+==========+=====+===========+
   | Media Type                          | Encoding | ID  | Reference |
   +=====================================+==========+=====+===========+
   | application/missing-blocks+cbor-seq | -        | 272 | RFC 9177  |
   +-------------------------------------+----------+-----+-----------+

            Table 5: Addition to CoAP Content-Format Registry

13. References ๐Ÿ“š

13.1. Normative References

   [RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
              Requirement Levels", BCP 14, RFC 2119,
              DOI 10.17487/RFC2119, March 1997,
              <https://www.rfc-editor.org/info/rfc2119>.

   [RFC6838]  Freed, N., Klensin, J., and T. Hansen, "Media Type
              Specifications and Registration Procedures", BCP 13,
              RFC 6838, DOI 10.17487/RFC6838, January 2013,
              <https://www.rfc-editor.org/info/rfc6838>.

   [RFC7252]  Shelby, Z., Hartke, K., and C. Bormann, "The Constrained
              Application Protocol (CoAP)", RFC 7252,
              DOI 10.17487/RFC7252, June 2014,
              <https://www.rfc-editor.org/info/rfc7252>.

   [RFC7641]  Hartke, K., "Observing Resources in the Constrained
              Application Protocol (CoAP)", RFC 7641,
              DOI 10.17487/RFC7641, September 2015,
              <https://www.rfc-editor.org/info/rfc7641>.

   [RFC7959]  Bormann, C. and Z. Shelby, Ed., "Block-Wise Transfers in
              the Constrained Application Protocol (CoAP)", RFC 7959,
              DOI 10.17487/RFC7959, August 2016,
              <https://www.rfc-editor.org/info/rfc7959>.

   [RFC8075]  Castellani, A., Loreto, S., Rahman, A., Fossati, T., and
              E. Dijk, "Guidelines for Mapping Implementations: HTTP to
              the Constrained Application Protocol (CoAP)", RFC 8075,
              DOI 10.17487/RFC8075, February 2017,
              <https://www.rfc-editor.org/info/rfc8075>.

   [RFC8132]  van der Stok, P., Bormann, C., and A. Sehgal, "PATCH and
              FETCH Methods for the Constrained Application Protocol
              (CoAP)", RFC 8132, DOI 10.17487/RFC8132, April 2017,
              <https://www.rfc-editor.org/info/rfc8132>.

   [RFC8174]  Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
              2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
              May 2017, <https://www.rfc-editor.org/info/rfc8174>.

   [RFC8323]  Bormann, C., Lemay, S., Tschofenig, H., Hartke, K.,
              Silverajan, B., and B. Raymor, Ed., "CoAP (Constrained
              Application Protocol) over TCP, TLS, and WebSockets",
              RFC 8323, DOI 10.17487/RFC8323, February 2018,
              <https://www.rfc-editor.org/info/rfc8323>.

   [RFC8610]  Birkholz, H., Vigano, C., and C. Bormann, "Concise Data
              Definition Language (CDDL): A Notational Convention to
              Express Concise Binary Object Representation (CBOR) and
              JSON Data Structures", RFC 8610, DOI 10.17487/RFC8610,
              June 2019, <https://www.rfc-editor.org/info/rfc8610>.

   [RFC8613]  Selander, G., Mattsson, J., Palombini, F., and L. Seitz,
              "Object Security for Constrained RESTful Environments
              (OSCORE)", RFC 8613, DOI 10.17487/RFC8613, July 2019,
              <https://www.rfc-editor.org/info/rfc8613>.

   [RFC8742]  Bormann, C., "Concise Binary Object Representation (CBOR)
              Sequences", RFC 8742, DOI 10.17487/RFC8742, February 2020,
              <https://www.rfc-editor.org/info/rfc8742>.

   [RFC8949]  Bormann, C. and P. Hoffman, "Concise Binary Object
              Representation (CBOR)", STD 94, RFC 8949,
              DOI 10.17487/RFC8949, December 2020,
              <https://www.rfc-editor.org/info/rfc8949>.

   [RFC9175]  Amsรผss, C., PreuรŸ Mattsson, J., and G. Selander,
              "Constrained Application Protocol (CoAP): Echo, Request-
              Tag, and Token Processing", RFC 9175,
              DOI 10.17487/RFC9175, February 2022,
              <https://www.rfc-editor.org/info/rfc9175>.

13.2. Informative References ๐Ÿ“š

Okay, this is the extra-credit reading list ๐Ÿค“ Each citation is kept exactly as written.

Appendix A. Examples with Confirmable Messages ๐ŸŽ€

Time for the CON (confirmable) examples! โœจ These all assume NSTART has been bumped up to 3, and they follow the conventions from Section 10.

A.1. Q-Block1 Option ๐Ÿ’–

Let's start with the Q-Block1 option riding along on a CON request. Every block gets acknowledged, which is what the "ACK" notes are showing ๐Ÿซถ

     CoAP        CoAP
    Client      Server
      |          |
      +--------->| CON PUT /path M:0x01 T:0xf0 RT=10 QB1:0/1/1024
      +--------->| CON PUT /path M:0x02 T:0xf1 RT=10 QB1:1/1/1024
      +--------->| CON PUT /path M:0x03 T:0xf2 RT=10 QB1:2/1/1024
   [[NSTART(3) limit reached]]
      |<---------+ ACK 0.00 M:0x01
      +--------->| CON PUT /path M:0x04 T:0xf3 RT=10 QB1:3/0/1024
      |<---------+ ACK 0.00 M:0x02
      |<---------+ ACK 0.00 M:0x03
      |<---------+ ACK 2.04 M:0x04
      |          |

        Figure 16: Example of a CON Request with the Q-Block1 Option
                               (without Loss)

Now here's the spicy version ๐ŸŒถ๏ธ A brand new body of data is sent, but a few blocks get dropped on the way. The client just keeps retrying whichever blocks never got an ACK. Main character recovery arc ๐Ÿฆ‹

     CoAP        CoAP
    Client      Server
      |          |
      +--------->| CON PUT /path M:0x05 T:0xf4 RT=11 QB1:0/1/1024
      +--->X     | CON PUT /path M:0x06 T:0xf5 RT=11 QB1:1/1/1024
      +--->X     | CON PUT /path M:0x07 T:0xf6 RT=11 QB1:2/1/1024
   [[NSTART(3) limit reached]]
      |<---------+ ACK 0.00 M:0x05
      +--------->| CON PUT /path M:0x08 T:0xf7 RT=11 QB1:3/1/1024
      |<---------+ ACK 0.00 M:0x08
      |   ...    |
   [[ACK TIMEOUT (client) for M:0x06 delay expires]]
      |     [[Client retransmits packet]]
      +--------->| CON PUT /path M:0x06 T:0xf5 RT=11 QB1:1/1/1024
   [[ACK TIMEOUT (client) for M:0x07 delay expires]]
      |     [[Client retransmits packet]]
      +--->X     | CON PUT /path M:0x07 T:0xf6 RT=11 QB1:2/1/1024
      |<---------+ ACK 0.00 M:0x06
      |   ...    |
   [[ACK TIMEOUT exponential backoff (client) delay expires]]
      |     [[Client retransmits packet]]
      +--->X     | CON PUT /path M:0x07 T:0xf6 RT=11 QB1:2/1/1024
      |   ...    |
   [[Either body transmission failure (acknowledge retry timeout)
      or successfully transmitted]]

        Figure 17: Example of a CON Request with the Q-Block1 Option
                              (Block Recovery)

Okay, so about the giving-up part ๐Ÿ’€ It's up to the implementation whether the application stops trying to send this body of data once MAX_RETRANSMIT is hit for any payload. The other option is to separately try to start a new transmission of only the payloads that never got acknowledged, under these rough traffic conditions. Totally the implementation's call ๐Ÿ’…

If transient network losses are possible, then the use of NON should be considered. ๐Ÿฆ‹

A.2. Q-Block2 Option ๐ŸŒธ

Next up is the Q-Block2 option with Confirmable messages, shown in Figure 18. It's giving notifications, observe triggers, and a whole lot of retries ๐Ÿ“

    Client      Server
      |          |
      +--------->| CON GET /path M:0x01 T:0xf0 O:0 QB2:0/1/1024
      |<---------+ ACK 2.05 M:0x01 T:0xf0 O:1234 ET=21 QB2:0/1/1024
      |<---------+ CON 2.05 M:0xe1 T:0xf0 O:1234 ET=21 QB2:1/1/1024
      |<---------+ CON 2.05 M:0xe2 T:0xf0 O:1234 ET=21 QB2:2/1/1024
      |<---------+ CON 2.05 M:0xe3 T:0xf0 O:1234 ET=21 QB2:3/0/1024
      |--------->+ ACK 0.00 M:0xe1
      |--------->+ ACK 0.00 M:0xe2
      |--------->+ ACK 0.00 M:0xe3
      |   ...    |
      |     [[Observe triggered]]
      |<---------+ CON 2.05 M:0xe4 T:0xf0 O:1235 ET=22 QB2:0/1/1024
      |<---------+ CON 2.05 M:0xe5 T:0xf0 O:1235 ET=22 QB2:1/1/1024
      |<---------+ CON 2.05 M:0xe6 T:0xf0 O:1235 ET=22 QB2:2/1/1024
   [[NSTART(3) limit reached]]
      |--------->+ ACK 0.00 M:0xe4
      |<---------+ CON 2.05 M:0xe7 T:0xf0 O:1235 ET=22 QB2:3/0/1024
      |--------->+ ACK 0.00 M:0xe5
      |--------->+ ACK 0.00 M:0xe6
      |--------->+ ACK 0.00 M:0xe7
      |   ...    |
      |     [[Observe triggered]]
      |<---------+ CON 2.05 M:0xe8 T:0xf0 O:1236 ET=23 QB2:0/1/1024
      |     X<---+ CON 2.05 M:0xe9 T:0xf0 O:1236 ET=23 QB2:1/1/1024
      |     X<---+ CON 2.05 M:0xea T:0xf0 O:1236 ET=23 QB2:2/1/1024
   [[NSTART(3) limit reached]]
      |--------->+ ACK 0.00 M:0xe8
      |<---------+ CON 2.05 M:0xeb T:0xf0 O:1236 ET=23 QB2:3/0/1024
      |--------->+ ACK 0.00 M:0xeb
      |   ...    |
   [[ACK TIMEOUT (server) for M:0xe9 delay expires]]
      |     [[Server retransmits packet]]
      |<---------+ CON 2.05 M:0xe9 T:0xf0 O:1236 ET=23 QB2:1/1/1024
   [[ACK TIMEOUT (server) for M:0xea delay expires]]
      |     [[Server retransmits packet]]
      |     X<---+ CON 2.05 M:0xea T:0xf0 O:1236 ET=23 QB2:2/1/1024
      |--------->+ ACK 0.00 M:0xe9
      |   ...    |
   [[ACK TIMEOUT exponential backoff (server) delay expires]]
      |     [[Server retransmits packet]]
      |     X<---+ CON 2.05 M:0xea T:0xf0 O:1236 ET=23 QB2:2/1/1024
      |   ...    |
   [[Either body transmission failure (acknowledge retry timeout)
      or successfully transmitted]]

      Figure 18: Example of CON Notifications with the Q-Block2 Option

The same caveat applies here ๐Ÿ™ˆ It's up to the implementation whether the application process stops trying to send this particular body of data on reaching MAX_RETRANSMIT for any payload, or separately tries to initiate the new transmission of the payloads that have not been acknowledged under these adverse traffic conditions.

If transient network losses are possible, then the use of NON should be considered. ๐Ÿงš

Appendix B. Examples with Reliable Transports ๐Ÿ’œ

Now for the reliable-transport examples โœจ The same conventions from Section 10 apply here too.

B.1. Q-Block1 Option ๐ŸŽ€

Let's look at the Q-Block1 option over a reliable transport, shown in Figure 19. There's no acknowledgment of packets at the CoAP layer, just the final result ๐Ÿ’…

    CoAP        CoAP
   Client      Server
     |          |
     +--------->| PUT /path T:0xf0 RT=10 QB1:0/1/1024
     +--------->| PUT /path T:0xf1 RT=10 QB1:1/1/1024
     +--------->| PUT /path T:0xf2 RT=10 QB1:2/1/1024
     +--------->| PUT /path T:0xf3 RT=10 QB1:3/0/1024
     |<---------+ 2.04
     |          |

     Figure 19: Example of a Reliable Request with the Q-Block1 Option

If transient network losses are possible, then the use of unreliable transport with NON should be considered. ๐ŸŒˆ

B.2. Q-Block2 Option ๐Ÿ“

Last one! The Q-Block2 option with a reliable transport is shown in Figure 20 ๐Ÿง

   Client      Server
     |          |
     +--------->| GET /path T:0xf0 O:0 QB2:0/1/1024
     |<---------+ 2.05 T:0xf0 O:1234 ET=21 QB2:0/1/1024
     |<---------+ 2.05 T:0xf0 O:1234 ET=21 QB2:1/1/1024
     |<---------+ 2.05 T:0xf0 O:1234 ET=21 QB2:2/1/1024
     |<---------+ 2.05 T:0xf0 O:1234 ET=21 QB2:3/0/1024
     |   ...    |
     |     [[Observe triggered]]
     |<---------+ 2.05 T:0xf0 O:1235 ET=22 QB2:0/1/1024
     |<---------+ 2.05 T:0xf0 O:1235 ET=22 QB2:1/1/1024
     |<---------+ 2.05 T:0xf0 O:1235 ET=22 QB2:2/1/1024
     |<---------+ 2.05 T:0xf0 O:1235 ET=22 QB2:3/0/1024
     |   ...    |

        Figure 20: Example of Notifications with the Q-Block2 Option

If transient network losses are possible, then the use of unreliable transport with NON should be considered. ๐Ÿฆ‹

Acknowledgments ๐Ÿซถ

Big thanks to Achim Kraus, Jim Schaad, and Michael Richardson for their comments ๐Ÿ’–

Special thanks to Christian Amsรผss, Carsten Bormann, and Marco Tiloca for their suggestions and several reviews, which improved this specification significantly โœจ Thanks to Francesca Palombini for the AD review. Thanks to Pete Resnick for the Gen-ART review, Colin Perkins for the TSVART review, and Emmanuel Baccelli for the IOT-DIR review. Thanks to Martin Duke, ร‰ric Vyncke, Benjamin Kaduk, Roman Danyliw, John Scudder, and Lars Eggert for the IESG review ๐Ÿ‘‘

Some text from [RFC7959] is reused for the readers' convenience. ๐ŸŒธ

Authors' Addresses ๐Ÿ’Œ

Mohamed Boucadair Orange 35000 Rennes France Email: mohamed.boucadair@orange.com

Jon Shallow United Kingdom Email: supjps-ietf@jpshallow.com

๐Ÿ“ฆ where this page comes from