Skip to content

fix(blockwise): return 4.08 on incomplete Block1 and make receive cache limit configurable#677

Open
jkralik wants to merge 1 commit into
masterfrom
fix/issue-664-2
Open

fix(blockwise): return 4.08 on incomplete Block1 and make receive cache limit configurable#677
jkralik wants to merge 1 commit into
masterfrom
fix/issue-664-2

Conversation

@jkralik

@jkralik jkralik commented Jul 7, 2026

Copy link
Copy Markdown
Member

Fixes #664 point 2

Summary by CodeRabbit

  • New Features

    • Added an option to cap in-flight incoming block-wise assemblies (default 1024; set to 0 for unlimited) across UDP, TCP, and DTLS clients/servers.
    • Exposed a configurable setting for the receiving-messages cache limit.
  • Bug Fixes

    • Strengthened receive-side validation for block-wise POST transfers with missing previous blocks or invalid final blocks.
    • When the receiving cache is full, now returns ServiceUnavailable; other incomplete transfers map to RequestEntityIncomplete.
  • Tests

    • Added unit tests and an end-to-end regression test covering missing previous blocks, invalid final blocks, and bounded cache behavior.

Copilot AI review requested due to automatic review settings July 7, 2026 09:49
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ff060349-215b-42cc-825a-9913c8e2a9db

📥 Commits

Reviewing files that changed from the base of the PR and between 5303f42 and be2f7ae.

📒 Files selected for processing (12)
  • dtls/client.go
  • dtls/server/server.go
  • net/blockwise/blockwise.go
  • net/blockwise/blockwise_test.go
  • options/commonOptions.go
  • options/commonOptions_test.go
  • options/config/common.go
  • tcp/client.go
  • tcp/server/server.go
  • udp/blockwise_test.go
  • udp/client.go
  • udp/server/server.go
🚧 Files skipped from review as they are similar to previous changes (10)
  • options/commonOptions_test.go
  • tcp/client.go
  • tcp/server/server.go
  • dtls/client.go
  • options/config/common.go
  • udp/client.go
  • dtls/server/server.go
  • udp/server/server.go
  • options/commonOptions.go
  • net/blockwise/blockwise.go

📝 Walkthrough

Walkthrough

This PR adds a configurable limit for blockwise receiving assemblies, threads it through config and transport setup, and updates blockwise receive-path handling and tests to return the new cache-full and incomplete-request responses.

Changes

Bounded blockwise receiving cache and validation

Layer / File(s) Summary
Core blockwise cache limit and validation logic
net/blockwise/blockwise.go
Adds the receiving cache default, configurable max-entry state and setter, admission control for new assemblies, response mapping for cache-full errors, and stricter incomplete-body handling in the receiving path.
Configuration option for cache size
options/config/common.go, options/commonOptions.go, options/commonOptions_test.go
Adds the common config field, default initialization, option type, option constructor, and apply-test coverage for the new receiving cache maximum.
Wire cache limit into transport blockwise factories
dtls/client.go, dtls/server/server.go, tcp/client.go, tcp/server/server.go, udp/client.go, udp/server/server.go
Updates DTLS, TCP, and UDP client and server blockwise factories to set the configured receiving cache maximum before returning the BlockWise instance.
Unit tests for cache bound and validation
net/blockwise/blockwise_test.go
Adds tests covering missing previous final block handling, a non-zero single final block, and bounded receiving cache capacity.
UDP end-to-end regression test for missing previous block
udp/blockwise_test.go
Adds raw CoAP packet helpers and an end-to-end UDP test that sends an initial block and then a skipping final block to verify the server returns Continue and RequestEntityIncomplete.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant BlockWise
  participant ReceivingCache
  participant Client

  BlockWise->>ReceivingCache: getCachedReceivedMessage(token)
  alt cache full on miss
    ReceivingCache-->>BlockWise: errReceivingMessagesCacheFull
    BlockWise-->>Client: ServiceUnavailable
  else incomplete body
    BlockWise-->>Client: RequestEntityIncomplete
  else complete assembly
    BlockWise->>ReceivingCache: store/update cached request
    BlockWise-->>Client: Continue or next()
  end
Loading

Possibly related PRs

  • plgd-dev/go-coap#560: Also changes the DTLS/CoAP blockwise receiving flow in net/blockwise/blockwise.go, including handleReceivedMessage behavior around next() and errors.
  • plgd-dev/go-coap#619: Also changes net/blockwise/blockwise.go receiving-path cache handling and blockwise assembly flow.
  • plgd-dev/go-coap#627: Also touches the same blockwise receive/control-flow integration points and associated tests.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning It covers only the missing-previous-Block1 fix, not the other linked RFC 7252/7959 requirements for malformed datagrams, Content-Format, duplicate options, or If-None-Match. Implement the remaining RFC 7252/7959 behaviors or split them into separate PRs tied to the issue.
Out of Scope Changes check ⚠️ Warning The new cache-limit configurability and ServiceUnavailable mapping are not requested by #664's listed requirements. Move the cache-limit feature to a separate PR or link an issue that explicitly requests it.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is specific and matches the main changes: incomplete Block1 handling and configurable receive cache sizing.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-664-2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov-commenter

codecov-commenter commented Jul 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.52036% with 32 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.72%. Comparing base (defc9c6) to head (be2f7ae).

Files with missing lines Patch % Lines
net/blockwise/blockwise.go 83.24% 23 Missing and 9 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #677      +/-   ##
==========================================
+ Coverage   76.63%   76.72%   +0.08%     
==========================================
  Files          77       77              
  Lines        6095     6208     +113     
==========================================
+ Hits         4671     4763      +92     
- Misses       1042     1058      +16     
- Partials      382      387       +5     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses block-wise (RFC 7959 Block1) conformance gaps from issue #664 (point 2) by returning 4.08 (Request Entity Incomplete) when a Block1 transfer is completed with missing prior blocks, and by adding a configurable limit for the in-flight “receiving messages” blockwise assembly cache to prevent unbounded growth.

Changes:

  • Return 4.08 for incomplete Block1 transfers (e.g., final block received while earlier blocks are missing).
  • Add BlockwiseReceivingMessagesCacheMaxEntries config/option and plumb it through UDP/TCP/DTLS clients and servers into the blockwise layer.
  • Add regression tests (unit + UDP end-to-end) covering missing-previous-block behavior and bounded cache behavior.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
udp/server/server.go Plumbs the new receiving-cache max-entries setting into server-side blockwise.
udp/client.go Plumbs the new receiving-cache max-entries setting into client-side blockwise.
tcp/server/server.go Plumbs the new receiving-cache max-entries setting into server-side blockwise.
tcp/client.go Plumbs the new receiving-cache max-entries setting into client-side blockwise.
dtls/server/server.go Plumbs the new receiving-cache max-entries setting into server-side blockwise.
dtls/client.go Plumbs the new receiving-cache max-entries setting into client-side blockwise.
options/config/common.go Adds defaulted common config field for receiving-cache max entries.
options/commonOptions.go Introduces WithBlockwiseReceivingMessagesCacheMaxEntries option and applies it across transports.
options/commonOptions_test.go Verifies the new option correctly applies into configs.
net/blockwise/blockwise.go Implements cache limit + incomplete-transfer detection (4.08 path via existing error mapping).
net/blockwise/blockwise_test.go Adds unit tests for missing-previous-block handling and cache bounding.
udp/blockwise_test.go Adds UDP end-to-end regression test for missing-previous-block -> 4.08 behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread net/blockwise/blockwise.go Outdated
Comment thread net/blockwise/blockwise.go
Comment thread net/blockwise/blockwise.go Outdated
@jkralik jkralik force-pushed the fix/issue-664-2 branch from dd6a027 to fd21f78 Compare July 7, 2026 09:58

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tcp/client.go (1)

33-42: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Wiring is correct.

bw is configured with SetReceivingMessagesCacheMaxEntries matching the upstream BlockWise contract and the BlockwiseReceivingMessagesCacheMaxEntriesOpt config wiring shown in options/commonOptions.go.

One gap: only UDP gets an end-to-end regression test for this feature (per stack layer 5); DTLS/TCP client and server factories have no equivalent test verifying the configured limit is actually applied at runtime (e.g., asserting cache rejection behavior over a real TCP/DTLS connection). Consider adding at least a lightweight test asserting SetReceivingMessagesCacheMaxEntries is invoked with the configured value for these transports too.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tcp/client.go` around lines 33 - 42, Add runtime coverage for the TCP/DTLS
blockwise receiving cache limit, since only the UDP path is currently tested.
Use the client/server factory setup in tcp/client.go (and the matching DTLS
factory) to verify that blockwise.New is followed by
SetReceivingMessagesCacheMaxEntries with the configured value, ideally through
an end-to-end or lightweight transport test that exercises the actual factory
wiring and cache rejection behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@net/blockwise/blockwise.go`:
- Line 707: The return in blockwise handling uses fmt.Errorf with a static
string, which triggers the perfsprint lint rule. Update the receiving messages
cache-full error in the relevant blockwise path to use errors.New instead of
fmt.Errorf, keeping the message identical and preserving the surrounding return
signature.

---

Nitpick comments:
In `@tcp/client.go`:
- Around line 33-42: Add runtime coverage for the TCP/DTLS blockwise receiving
cache limit, since only the UDP path is currently tested. Use the client/server
factory setup in tcp/client.go (and the matching DTLS factory) to verify that
blockwise.New is followed by SetReceivingMessagesCacheMaxEntries with the
configured value, ideally through an end-to-end or lightweight transport test
that exercises the actual factory wiring and cache rejection behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8dacf627-65ef-4144-8634-03563c890632

📥 Commits

Reviewing files that changed from the base of the PR and between defc9c6 and dd6a027.

📒 Files selected for processing (12)
  • dtls/client.go
  • dtls/server/server.go
  • net/blockwise/blockwise.go
  • net/blockwise/blockwise_test.go
  • options/commonOptions.go
  • options/commonOptions_test.go
  • options/config/common.go
  • tcp/client.go
  • tcp/server/server.go
  • udp/blockwise_test.go
  • udp/client.go
  • udp/server/server.go

Comment thread net/blockwise/blockwise.go Outdated
@jkralik jkralik force-pushed the fix/issue-664-2 branch from fd21f78 to 5b64bbf Compare July 7, 2026 10:11

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
net/blockwise/blockwise.go (1)

737-767: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Release the unused pooled message on the loaded path

When LoadOrStore returns loaded, the freshly acquired msg is dropped after mg is reassigned to the cached entry. closeFn only releases the guard semaphore, so the orphaned pooled message is never returned to b.cc. Add b.cc.ReleaseMessage(msg) on the loaded path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@net/blockwise/blockwise.go` around lines 737 - 767, The loaded path in
blockwise.go leaks the freshly acquired pooled message because msg is replaced
by the cached guard from LoadOrStore and closeFn only releases the guard. Update
the request setup around newCloseStack, AcquireMessage, acquireMessageGuard, and
the LoadOrStore loaded branch to return the unused msg to b.cc with
ReleaseMessage before exiting that path, while keeping the existing guard
release behavior unchanged.
🧹 Nitpick comments (1)
net/blockwise/blockwise.go (1)

866-922: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

CI is failing on complexity for processReceivedMessage.

gocyclo (16 > 15) and SonarCloud cognitive complexity (18 > 15) both fail on this function. Extract the offset/copy/completion logic (Lines 904-920) into a helper (e.g. appendBlockAndMaybeFinalize) so the top-level flow stays under the threshold and the checks pass.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@net/blockwise/blockwise.go` around lines 866 - 922, `processReceivedMessage`
is over the cyclomatic/cognitive complexity threshold because the
offset/copy/finalization branch is still inline. Extract the block handling from
the `off := num * szx.Size()` section through the `continueReceivedRequest`
return into a helper such as `appendBlockAndMaybeFinalize`, and have
`processReceivedMessage` delegate to it. Keep the helper responsible for the
copy, incomplete-body check, optional `finalizeReceivedRequest`, and `getSzx`
update so the top-level flow is flattened and the complexity checks pass.

Sources: Linters/SAST tools, Pipeline failures

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@net/blockwise/blockwise.go`:
- Around line 737-767: The loaded path in blockwise.go leaks the freshly
acquired pooled message because msg is replaced by the cached guard from
LoadOrStore and closeFn only releases the guard. Update the request setup around
newCloseStack, AcquireMessage, acquireMessageGuard, and the LoadOrStore loaded
branch to return the unused msg to b.cc with ReleaseMessage before exiting that
path, while keeping the existing guard release behavior unchanged.

---

Nitpick comments:
In `@net/blockwise/blockwise.go`:
- Around line 866-922: `processReceivedMessage` is over the cyclomatic/cognitive
complexity threshold because the offset/copy/finalization branch is still
inline. Extract the block handling from the `off := num * szx.Size()` section
through the `continueReceivedRequest` return into a helper such as
`appendBlockAndMaybeFinalize`, and have `processReceivedMessage` delegate to it.
Keep the helper responsible for the copy, incomplete-body check, optional
`finalizeReceivedRequest`, and `getSzx` update so the top-level flow is
flattened and the complexity checks pass.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 851d596e-32c8-4309-93a1-03df21f1f4e6

📥 Commits

Reviewing files that changed from the base of the PR and between dd6a027 and fd21f78.

📒 Files selected for processing (12)
  • dtls/client.go
  • dtls/server/server.go
  • net/blockwise/blockwise.go
  • net/blockwise/blockwise_test.go
  • options/commonOptions.go
  • options/commonOptions_test.go
  • options/config/common.go
  • tcp/client.go
  • tcp/server/server.go
  • udp/blockwise_test.go
  • udp/client.go
  • udp/server/server.go
🚧 Files skipped from review as they are similar to previous changes (9)
  • dtls/client.go
  • tcp/server/server.go
  • udp/client.go
  • dtls/server/server.go
  • tcp/client.go
  • options/config/common.go
  • options/commonOptions.go
  • options/commonOptions_test.go
  • udp/server/server.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
net/blockwise/blockwise.go (1)

765-790: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Capture each guard in its own local. On the loaded path, both appendClose callbacks close over the reassigned mg, so they release the cached guard twice and never release the freshly acquired message. A concurrent LoadOrStore race can hit this and trigger a semaphore over-release panic while leaking msg.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@net/blockwise/blockwise.go` around lines 765 - 790, The LoadOrStore handling
in the request-guard setup is capturing the reassigned mg in both appendClose
callbacks, which can make the loaded path release the cached guard twice and
skip releasing the newly acquired one. In the acquire/loaded branch around
acquireMessageGuard, appendClose, and the requestGuard/cache.NewElement logic,
store the initial guard and the cached guard in separate local variables and
bind each close callback to the correct guard instance before returning.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@net/blockwise/blockwise.go`:
- Around line 765-790: The LoadOrStore handling in the request-guard setup is
capturing the reassigned mg in both appendClose callbacks, which can make the
loaded path release the cached guard twice and skip releasing the newly acquired
one. In the acquire/loaded branch around acquireMessageGuard, appendClose, and
the requestGuard/cache.NewElement logic, store the initial guard and the cached
guard in separate local variables and bind each close callback to the correct
guard instance before returning.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6c5d4c06-cc8d-40f1-ae6f-e62a2ee4112a

📥 Commits

Reviewing files that changed from the base of the PR and between fd21f78 and 5b64bbf.

📒 Files selected for processing (12)
  • dtls/client.go
  • dtls/server/server.go
  • net/blockwise/blockwise.go
  • net/blockwise/blockwise_test.go
  • options/commonOptions.go
  • options/commonOptions_test.go
  • options/config/common.go
  • tcp/client.go
  • tcp/server/server.go
  • udp/blockwise_test.go
  • udp/client.go
  • udp/server/server.go
🚧 Files skipped from review as they are similar to previous changes (9)
  • tcp/client.go
  • dtls/server/server.go
  • dtls/client.go
  • tcp/server/server.go
  • options/commonOptions.go
  • udp/client.go
  • options/commonOptions_test.go
  • udp/server/server.go
  • options/config/common.go

@jkralik jkralik force-pushed the fix/issue-664-2 branch from 5b64bbf to 5303f42 Compare July 7, 2026 12:04
@jkralik jkralik force-pushed the fix/issue-664-2 branch from 5303f42 to be2f7ae Compare July 7, 2026 12:34
@sonarqubecloud

sonarqubecloud Bot commented Jul 7, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RFC 7252 / 7959 Conformance Issues — Malformed-Datagram Session Teardown and Block-wise Transfer Gaps

3 participants