Skip to content

feat: S28.10 FreeRtosLwip TCP (octet-framed StreamSender over LwipRawTcpStream)#480

Merged
DavidCozens merged 3 commits into
mainfrom
feat/s28-10-freertoslwip-tcp
May 29, 2026
Merged

feat: S28.10 FreeRtosLwip TCP (octet-framed StreamSender over LwipRawTcpStream)#480
DavidCozens merged 3 commits into
mainfrom
feat/s28-10-freertoslwip-tcp

Conversation

@DavidCozens

@DavidCozens DavidCozens commented May 29, 2026

Copy link
Copy Markdown
Owner

Purpose

S28.10 (#473, epic E28) puts a real TCP transport on the FreeRtosLwip BDD
target: an RFC 6587 octet-framed SolidSyslogStreamSender over
SolidSyslogLwipRawTcpStream, proven on QEMU against the syslog-ng oracle —
plain delivery, runtime UDP→TCP switching, and reconnect after a server outage.

Closes #473.

Change Description

  • Wiring (main.c). Create a LwipRawTcpStream (connect timeout from the
    SOLIDSYSLOG_TCP_CONNECT_TIMEOUT_MS tunable; RtosSleep drives the bounded
    synchronous connect), a LwipRawAddress, and a StreamSender sharing the
    resolver + GetEndpoint/GetEndpointVersion with UDP (the oracle listens TCP
    on the same :5514). Wire it into inners[BDD_TARGET_SWITCH_TCP]; the TLS slot
    stays NullSender until S28.11. Teardown destroys the stream sender + stream +
    address in order.
  • Adapter fix (Platform/LwipRaw, Tier 2). tcp_reconnect surfaced a real
    gap: StreamSender reconnects only when Send returns false (it never
    Reads), but on a graceful peer FIN the adapter set Errored while leaving the
    pcb non-NULL, and Send guarded only on IsOpen — so tcp_write kept
    returning ERR_OK into a half-closed connection and the sender never
    reconnected. New intent-named predicate LwipRawTcpStream_IsWritable
    (IsOpen && !Errored) gates Send; Read still runs while Errored to drain
    then close. This matches FreeRTOS-Plus-TCP's send-side detection (why the +TCP
    lane already passed the scenario). Driven red→green by a new unit test.
  • CI (ci/docker-compose.bdd.yml). lwIP lane filter @udp and not @tcp …
    (@udp or @tcp) and not @tls and not @mtls and not @store. Enables
    tcp_transport, tcp_reconnect, and the UDP→TCP switching_transport
    scenario; tcp_singletask (@windows_wip) and TCP→TLS (@tls) stay excluded.

Test Evidence

  • Unit: Tests/Lwip/SolidSyslogLwipRawTcpStreamTest — added
    SendReturnsFalseAfterPeerFin (red→green); 56 tests, 0 failures (built in the
    cpputest-freertos host image, which sets LWIP_PATH).
  • Oracle (ci/docker-compose.bdd.yml, lwIP pair): 11 features / 28
    scenarios / 116 steps passed, 0 failed.
    tcp_transport, tcp_reconnect, and
    the UDP→TCP switch are green alongside the full UDP set; TLS/mTLS/store and the
    1500-MTU scenario are correctly skipped (target MAX_MESSAGE_SIZE=512).
  • Tree clang-format clean.

Areas Affected

  • Platform/LwipRaw/Source/SolidSyslogLwipRawTcpStream.c (Tier 2) + its test —
    Send now fails on peer close. Benefits any lwIP-Raw TCP/TLS integrator, not just
    this target.
  • Bdd/Targets/FreeRtosLwip/ (Tier 3) — TCP wiring, README.
  • .github/compose — advisory lwIP lane scope. No Core/ change.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added TCP transport support (plain text, octet-framed) to the FreeRTOS + lwIP target alongside existing UDP support.
  • Documentation

    • Updated scope documentation to clarify both UDP and TCP transport coverage.
    • Added development log entry documenting TCP integration and validation approach.
  • Tests

    • Extended test coverage to include TCP transport scenarios and connection lifecycle.

Review Change Stack

DavidCozens and others added 2 commits May 29, 2026 20:32
… reconnects

StreamSender reconnects only when Send returns false; it never Reads. On a
graceful peer FIN (RecvCallback with NULL p) the adapter set Errored but left
the pcb non-NULL, and Send guarded only on IsOpen — so tcp_write kept returning
ERR_OK into a doomed half-closed connection and the sender never reconnected
after the server recovered.

Gate Send with a new intent-named predicate IsWritable (IsOpen && !Errored) so a
post-FIN send fails, prompting StreamSender to close and reconnect on the next
message. Read still runs while Errored (drain queued bytes, then close on EOF).
Mirrors FreeRTOS-Plus-TCP's send-side closed-socket detection.

Driven red->green by SendReturnsFalseAfterPeerFin (Tests/Lwip, 56 tests green).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…RawTcpStream

Wire a real RFC 6587 octet-framed SolidSyslogStreamSender over
SolidSyslogLwipRawTcpStream into the SwitchingSender's TCP slot (TLS stays
NullSender until S28.11). Shares the resolver + endpoint callbacks with UDP
(syslog-ng oracle listens TCP on the same :5514); RtosSleep drives the bounded
synchronous connect.

Flip the advisory lwIP lane filter to (@udp or @tcp) — enables tcp_transport,
tcp_reconnect, and the UDP->TCP switching_transport scenario.

Oracle green: 11 features / 28 scenarios / 116 steps, 0 failed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@DavidCozens, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 47 minutes and 49 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a5b7a4b8-02e6-41c2-82cf-58283d22360a

📥 Commits

Reviewing files that changed from the base of the PR and between abb4a25 and 4b7956b.

📒 Files selected for processing (1)
  • misra_suppressions.txt
📝 Walkthrough

Walkthrough

This PR implements TCP transport support for the FreeRTOS + lwIP BDD target. It adds a peer-FIN send-gating predicate to the TCP stream adapter, wires a real octet-framed TCP sender into the BDD target's SwitchingSender, and updates documentation and CI configuration to reflect both UDP and TCP transport availability.

Changes

TCP Transport Integration

Layer / File(s) Summary
TCP stream send-gating on peer FIN
Platform/LwipRaw/Source/SolidSyslogLwipRawTcpStream.c, Tests/Lwip/SolidSyslogLwipRawTcpStreamTest.cpp
LwipRawTcpStream_IsWritable predicate suppresses sends when the stream is errored after peer FIN, forcing close/reconnect behavior while read drains continue. Test verifies sendBytes() fails and no tcp_write is issued post-FIN.
BDD FreeRtosLwip TCP transport wiring
Bdd/Targets/FreeRtosLwip/main.c
File header updated to document UDP and TCP wiring; interactive task creates LwipRawTcpStream adapter, SolidSyslogLwipRawAddress, and SolidSyslogStreamSender for RFC 6587 octet-framed streaming, then registers TCP sender into SwitchingSender. Teardown destroys all TCP components.
Documentation and CI scope updates
Bdd/Targets/FreeRtosLwip/README.md, DEVLOG.md, ci/docker-compose.bdd.yml
README Scope section explicitly enumerates UDP (S28.09) and TCP (S28.10); DEVLOG records TCP integration, peer-FIN handling, and BDD lane scope; CI tag filter changed to (@udpor@tcp) to enable TCP scenario execution on the FreeRtosLwip lane.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • #473 — This PR directly implements the S28.10 TCP scenario acceptance criteria: octet-framed SolidSyslogStreamSender over SolidSyslogLwipRawTcpStream green on FreeRtosLwip target with bounded connect/keepalive behavior.

Possibly related PRs

  • DavidCozens/solid-syslog#209 — Both PRs tighten TCP stream send-failure semantics by suppressing sends on connection error (lwIP peer FIN vs. POSIX timeout/error), implementing platform-specific reconnect gating.
  • DavidCozens/solid-syslog#342 — Both PRs adjust BDD tcp_reconnect test routing by extending scenario tag coverage (@tcp inclusion in lwIP FreeRtosLwip lane vs. @store tag refactoring in other targets).

Poem

🐰 A rabbit hops through lwIP's TCP stream,
With IsWritable guards on a FIN-ed dream;
No sends past the close—just reconnect and retry!
The switching sender picks TCP, and streams flow nigh.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: implementing S28.10 TCP support using an octet-framed StreamSender over LwipRawTcpStream for the FreeRtosLwip target.
Description check ✅ Passed The PR description is comprehensive and closely follows the template structure with Purpose, Change Description, Test Evidence, and Areas Affected sections clearly delineated.
Linked Issues check ✅ Passed The PR fully implements the acceptance criteria from issue #473: TCP feature verified green on bdd-freertos-qemu-lwip (11 features/28 scenarios/116 steps passed), with bounded connect and keepalive tested on QEMU.
Out of Scope Changes check ✅ Passed All changes are directly aligned with issue #473 scope: TCP wiring, adapter fix for peer FIN handling, CI lane filter update, and unit tests. TLS support (S28.11) is explicitly left as NullSender as intended.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/s28-10-freertoslwip-tcp

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 and usage tips.

@github-actions

Copy link
Copy Markdown
Contributor

☀️   Quality Summary

   🚦   build-linux-gcc: 100% successful (✔️ 1344 passed)
   🚦   build-freertos-host-tdd-plustcp: 100% successful (✔️ 1652 passed)
   🚦   build-linux-clang: 100% successful (✔️ 1296 passed)
   🚦   sanitize-linux-gcc: 100% successful (✔️ 1296 passed)
   🚦   integration-linux-openssl: 100% successful (✔️ 9 passed)
   🚦   integration-linux-mbedtls: 100% successful (✔️ 7 passed)
   🚦   integration-windows-openssl: 100% successful (✔️ 9 passed)
   🚦   bdd-linux-syslog-ng: 94% successful (✔️ 46 passed, 🙈 3 skipped)
   🚦   bdd-windows-otel: 90% successful (✔️ 44 passed, 🙈 5 skipped)
   🚦   bdd-freertos-qemu-plustcp: 86% successful (✔️ 42 passed, 🙈 7 skipped)
   🚦   build-windows-msvc: 100% successful (✔️ 1149 passed)
   🚦   build-linux-tunable-override: 100% successful (✔️ 1296 passed)
   ⚠️   Clang-Tidy: No warnings
   ⚠️   CPPCheck: No warnings


Created by Quality Monitor v1.14.0 (#f3859fd). More details are shown in the GitHub Checks Result.

…shift

The IsWritable forward declaration shifted the existing 11.3 / 11.5 cast
suppressions in SolidSyslogLwipRawTcpStream.c down one line (129->130,
138->139, 146->147), so cppcheck-misra re-fired previously-deviated findings.
Re-anchored via scripts/misra_renumber.py --apply; the gate exits 0 again.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

☀️   Quality Summary

   🚦   build-linux-gcc: 100% successful (✔️ 1344 passed)
   🚦   build-freertos-host-tdd-plustcp: 100% successful (✔️ 1652 passed)
   🚦   build-linux-clang: 100% successful (✔️ 1296 passed)
   🚦   sanitize-linux-gcc: 100% successful (✔️ 1296 passed)
   🚦   integration-linux-openssl: 100% successful (✔️ 9 passed)
   🚦   integration-linux-mbedtls: 100% successful (✔️ 7 passed)
   🚦   integration-windows-openssl: 100% successful (✔️ 9 passed)
   🚦   bdd-linux-syslog-ng: 94% successful (✔️ 46 passed, 🙈 3 skipped)
   🚦   bdd-windows-otel: 90% successful (✔️ 44 passed, 🙈 5 skipped)
   🚦   bdd-freertos-qemu-plustcp: 86% successful (✔️ 42 passed, 🙈 7 skipped)
   🚦   build-windows-msvc: 100% successful (✔️ 1149 passed)
   🚦   build-linux-tunable-override: 100% successful (✔️ 1296 passed)
   ⚠️   Clang-Tidy: No warnings
   ⚠️   CPPCheck: No warnings


Created by Quality Monitor v1.14.0 (#f3859fd). More details are shown in the GitHub Checks Result.

@DavidCozens DavidCozens merged commit ba7f9b1 into main May 29, 2026
25 checks passed
@DavidCozens DavidCozens deleted the feat/s28-10-freertoslwip-tcp branch May 29, 2026 19:54
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.

S28.10: FreeRtosLwip BDD — TCP scenario (octet-framed StreamSender over LwipRawTcpStream)

1 participant