Repository navigation
feat(port): serve a CWIST app over lwIP's raw TCP API (#447 Phase 3) - #452
Merged
Merged
Conversation
- include/cwist/sys/port/lwip.h + src/sys/port/lwip.c: a listener on a raw-API tcp_pcb (NO_SYS-safe). Requests are framed by Content-Length with the parser's CL/TE rules (501 for Transfer-Encoding, 400 for CL+TE or a bad/conflicting CL, 431/413 above the configured caps), answered with 100 Continue on Expect, parsed and run through cwist_app_dispatch, and serialized. Keep-alive follows request and response as on the socket path; pipelined requests are answered in order. Responses go out zero-copy via tcp_write and are freed once acknowledged; while one is in flight, excess input is refused back to lwIP so the window throttles the peer. Connection cap, idle timeout via tcp_poll, HEAD without body, SOF_REUSEADDR when SO_REUSE is on. - The adapter is compiled by the firmware next to its own lwIP and lwipopts.h; it uses only public CWIST API and is not part of libcwist. - lib/lwip: lwIP STABLE-2_2_1_RELEASE, vendored for the tests only. - tests/test_lwip_adapter.c (make test): real lwIP TCP over the loopback netif in one process, covering keep-alive, pipelining, split bodies, 100-continue, every framing error, Connection: close, HTTP/1.0, HEAD, a 20 KB response through a 2-segment send window, the connection cap, the idle timeout and teardown. Clean under ASan/UBSan/LSan. - tests/baremetal/hello_lwip.c joins make baremetal-smoke: the same stack on Cortex-M7 under qemu-arm.
cppcheck analyzes src/ without the firmware's lwIP tree, so LWIP_TCP was undefined and the requirement check fired. lwip/opt.h always defines LWIP_TCP and LWIP_CALLBACK_API, so check only once it has been seen; a real build with LWIP_TCP=0 still hits the #error.
gg582
added a commit
that referenced
this pull request
Oct 26, 2026
Conflict: docs/api/v4.0-api-baseline.txt, where dev's lwIP adapter API (#452) and this branch's Redis rate-limit API land in the same sections. Regenerated with `make api-baseline` from the merged headers: against dev it adds only this PR's entries, against the branch only dev's.
gg582
added a commit
that referenced
this pull request
Oct 27, 2026
…in (#454) * feat(port): serve a CWIST app over lwIP's raw TCP API (#447 Phase 3) (#452) * feat(port): serve a CWIST app over lwIP's raw TCP API (#447 Phase 3) - include/cwist/sys/port/lwip.h + src/sys/port/lwip.c: a listener on a raw-API tcp_pcb (NO_SYS-safe). Requests are framed by Content-Length with the parser's CL/TE rules (501 for Transfer-Encoding, 400 for CL+TE or a bad/conflicting CL, 431/413 above the configured caps), answered with 100 Continue on Expect, parsed and run through cwist_app_dispatch, and serialized. Keep-alive follows request and response as on the socket path; pipelined requests are answered in order. Responses go out zero-copy via tcp_write and are freed once acknowledged; while one is in flight, excess input is refused back to lwIP so the window throttles the peer. Connection cap, idle timeout via tcp_poll, HEAD without body, SOF_REUSEADDR when SO_REUSE is on. - The adapter is compiled by the firmware next to its own lwIP and lwipopts.h; it uses only public CWIST API and is not part of libcwist. - lib/lwip: lwIP STABLE-2_2_1_RELEASE, vendored for the tests only. - tests/test_lwip_adapter.c (make test): real lwIP TCP over the loopback netif in one process, covering keep-alive, pipelining, split bodies, 100-continue, every framing error, Connection: close, HTTP/1.0, HEAD, a 20 KB response through a 2-segment send window, the connection cap, the idle timeout and teardown. Clean under ASan/UBSan/LSan. - tests/baremetal/hello_lwip.c joins make baremetal-smoke: the same stack on Cortex-M7 under qemu-arm. * fix(port): keep cppcheck off the lwIP option #error cppcheck analyzes src/ without the firmware's lwIP tree, so LWIP_TCP was undefined and the requirement check fired. lwip/opt.h always defines LWIP_TCP and LWIP_CALLBACK_API, so check only once it has been seen; a real build with LWIP_TCP=0 still hits the #error. (cherry picked from commit ad7ecad) * feat(middleware): add shared Redis rate limiting (#453) * feat(middleware): add shared Redis rate limiting * test(redis): fix GCC buffer and pipe declarations * fix(redis): Potential fix for pull request finding 'CodeQL / Potentially overflowing call to snprintf' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * fix(redis): validate frame encoding before pooled I/O Preserve the upstream Autofix commit and supersede its void encoder with the independently reviewed checked implementation. Reject formatting failures and capacity mismatches before acquiring a connection; cover bounds and formatter failures without suppressing CodeQL. --------- Co-authored-by: 이윤진(Lee Yunjin) <168180007+gg582@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Lee Yunjin <gzblues61@daum.net> (cherry picked from commit 29df1ac) * Potential fix for pull request finding 'Empty except' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> --------- Co-authored-by: Jiho Lee <optional.int@kakao.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 3 of the bare-metal STM32 epic (#447): the lwIP transport adapter. It builds on the Phase 1 platform layer (#449).
API (
include/cwist/sys/port/lwip.h)The firmware compiles
src/sys/port/lwip.cnext to its own lwIP andlwipopts.h, because the adapter depends on its TCP options. The adapter uses only the raw API, so it works underNO_SYS=1. It also uses only public CWIST API (cwist_http_parse_request_len→cwist_app_dispatch→cwist_http_response_serialize), so it is not part oflibcwist, and no core code changes in this PR.Behaviour
Expect: 100-continuegets an interim100 Continuebefore the body is read.cfg.keep_alive && req->keep_alive && res->keep_alive, the same rule as the socket path. Pipelined requests are answered in order, one exchange at a time per connection. HEAD gets the head only.tcp_writewithoutCOPY, and the buffer is freed only after the peer acknowledges it. Sending continues fromtcp_sent;ERR_MEMis retried fromtcp_poll.ERR_MEM), so the TCP window throttles the peer.max_connsare aborted. Idle connections time out throughtcp_pollandsys_now().SO_REUSE=1the listener setsSOF_REUSEADDR, so it can be re-created while connections it closed sit in TIME_WAIT. The tests hit exactly this case.cwist_async) and upgraded (WebSocket) responses. This is documented in the header.Tests
lwIP
STABLE-2_2_1_RELEASEis vendored aslib/lwip, for tests only.tests/lwip/holds a NO_SYSlwipopts.h,arch/cc.h, and an in-process raw-API client. Client and server pcbs talk through lwIP's loopback netif on a fake clock, so every byte goes through real lwIP TCP.test_lwip_adapter(inmake test, host):Connection: closefrom the handler, and HTTP/1.0I checked the assertions are live by changing one expectation and watching it fail. The test is also clean under
-fsanitize=address,undefinedwith leak detection.make baremetal-smokenow also runshello-lwip.elf: the same adapter and lwIP on Cortex-M7 underqemu-arm, covering keep-alive, pipelining with a split body, an 8 KB response through the 2-segment window, a 413, and connection release. All pass.Sizes (Cortex-M7, -Os, test
lwipopts.h)ram_heap24 KB + pbuf pool 24 KB (test sizing), 8 KB test payloadThe lwIP pools in the test config are generous. A production
lwipopts.hsizes them to the board, and Phase 6 docs will carry a sizing table.Local verification
make test_lwip_adapter,make test_baremetal_port: passmake baremetal-smoke: hello, hello-rtc, hello-lwip all passcheck_test_wiring,check_api_baseline(4 functions + 2 types added),check_env_knobs,check_api_test_coverage,check_inline_exports,malloc_lint.sh: OKarm-none-eabi-gcc -Wall -Wextraandclang -Wall -Wextraon the adapter: no warningsNot in this PR
baremetal-smoke(Phase 5)Refs #447