CoAP Blockwise Transfers: Keep Representation Identity Stable Across Blocks
Accept a blockwise response only when all retained blocks belong to one representation and cover the expected byte range. A valid response to each individual GET is insufficient: a changing resource can supply the beginning of one version and the end of another.
This procedure targets ordinary Block2 response transfers under RFC 7959, with the response-body integrity guidance in RFC 9175. It excludes Block1 upload-state design, Q-Block, and reliable-transport extensions such as BERT. Record the actual stack version and supported extensions before applying the fixture; no implementation support or target test result is assumed.
1. Choose the representation acceptance key
Retain the resource identity and representation-affecting request options, the selected content format, and the representation’s ETag when supplied. Keep that object-level state separate from the Token used to correlate each individual request and response.
For changing resources, RFC 7959 section 2.4 recommends ETags on block responses. When ETags are available, the reassembling client is required to compare them. A mismatch means the bytes cannot be accepted as one representation. See the Block2 consistency rules.
ETag comparison is not peer authentication. RFC 9175 section 3.8 specifies stronger ETag use for its response-body protection model, including distinct ETags for distinct representations. Apply that alongside the actual protected exchange; the option alone does not turn an unauthenticated transfer into a trusted update. See response-body integrity guidance.

Missing ETags are not version evidence. If one or all blocks lack a tag, do not treat two absent values as a representation match. Require the applicable protected Block2/ETag contract, or independently established immutable-resource or snapshot identity. Without that evidence, fail the candidate or report consistency unproven under a bounded policy. This acceptance rule does not turn RFC 7959’s base recommendation into a universal ETag requirement.
2. Record byte coverage, not just the last block number
A Block2 response identifies a block number, a more-blocks flag, and a block size. For the RFC 7959 base encoding, the byte offset follows from the block number and selected block size. Validate that arithmetic before copying, and keep the assembled object’s size limit independent of the peer’s advertised size.
A final block with the more flag cleared is not proof that every preceding byte was obtained. A duplicate block is not additional progress. Track the expected next range for a sequential downloader, or a bounded coverage map for an implementation that deliberately supports out-of-order collection.
| Observation | Acceptance decision |
|---|---|
| Expected range, matching representation identity. | Retain the bytes and advance the verified coverage. |
| Duplicate range with identical bytes. | Apply the documented duplicate policy without double-counting progress. |
| Duplicate range with conflicting bytes. | Reject the candidate and preserve diagnostic evidence. |
| ETag differs from retained blocks. | Do not combine the versions; restart or reconcile through a defined policy. |
| Final block arrives with a gap. | Keep the object incomplete. |
| Size, offset, or content format violates the accepted contract. | Reject before copying or activating content. |
The table is an engineering acceptance policy. It complements protocol parsing and does not authorize accepting malformed messages that the stack should reject earlier.
3. Handle block-size negotiation explicitly
The server can use a smaller initial block size than requested, and the client must follow the actual response size in the subsequent sequence under RFC 7959. A downloader that increments a byte offset using its original preference can skip or repeat data. See section 2.4.
Record negotiated size and actual response length separately. The last block can be shorter. Do not infer a new block-size negotiation from that short final payload. Exercise initial negotiation and exact final-length boundaries in the test harness, rather than assuming the nominal buffer capacity matches every response.
Size2 is a size indication, not a command to allocate unlimited RAM. Cap the total representation, the number of retained ranges, and temporary storage before accepting data. If streaming to a staging file, keep incomplete content unavailable to consumers until the whole acceptance condition passes.
4. Decide what happens when the resource changes
Choose a bounded retry policy. One simple product policy discards the mixed candidate and restarts from block zero when an ETag changes. Another can deliberately reacquire earlier ranges for the new representation, provided every retained range is revalidated. Never keep old bytes just because the new response has the same total length.
The server is not required to retain a historical representation for the whole transfer. RFC 7959 notes that a rapidly changing resource can prevent a client from obtaining a consistent set of blocks. A retry loop therefore needs an attempt or elapsed-time budget and an explicit “no stable representation obtained” outcome.
If stable downloads are a product requirement, consider an application resource that names an immutable snapshot or a server policy that pins a snapshot for a bounded transfer. Those are service-design choices. Supplying an ETag does not itself compel the server to keep the old version available.
5. Use a changing-resource fixture
Create two synthetic representations, A and B, with the same total length but deliberately different patterns in every block. Give them distinct ETags. The fixture knows the complete bytes and can switch the served representation between responses.
- Download all of A and require exact byte equality before publication.
- Return A’s first block and B’s next block; require rejection of the mixed candidate.
- Repeat a block, then return a conflicting duplicate under the same ETag; require a visible fixture failure.
- Return the final block before a required middle range; require an incomplete state.
- Negotiate a smaller initial block size and verify continuous byte coverage.
- Change representation on every attempt and verify bounded failure rather than endless allocation or retries.
- Provide a size indication above the product cap and confirm rejection without a proportional allocation.
- Omit an ETag from one block, then from every block of a changing resource; require the explicit unproven-consistency outcome unless independent immutable identity is established.
Distinguish a parser-only replay from a test that exercises the library’s network, retransmission, and staging behavior. Keep the accepted object hash and range log, not merely a count of successful CoAP responses.
Hypothetical service decision
A device exposes a large diagnostic snapshot that changes as counters update. A client receives the header from snapshot A and the records from snapshot B. Both transfers individually use successful response codes, but the assembled object never existed on the server.
The corrected client enforces the representation oracle, and the service adds a bounded immutable snapshot endpoint if consistent retrieval is required. The ETag is interpreted according to the CoAP representation model, while Request-Tag remains a separate request-body mechanism rather than a replacement for Block2 version checking.
Release the downloader with a byte-coverage invariant, version-mismatch policy, memory cap, and changing-resource fixture. Complete transport is useful only when it produces one complete representation.