Repository navigation
Refactor the HTTP Engine - #59
Conversation
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #59
Scan targets checked: wolfcert-src, wolfcert-bugs
Not reviewed (no scan target covers them): docs/EMBEDDED.md, src/http.c
Coverage: 5 of 8 in-scope changed file(s) opened by the reviewer; not opened: src/scep/scep_client.c, tests/unit/test_http.c, wolfcert/est.h
Fenrir result: Approved ✅
No new issues found in the changed files.
Advisory only — this automated result does not count as a GitHub approval.
Review tier: Lite
b37906b to
d464770
Compare
Frauschi
left a comment
There was a problem hiding this comment.
Built the head and ran the suite plain with -Werror and under ASan+UBSan (30/30 both), plus leaks on the transport, TLS and EST/SCEP session tests. All clean. Having the state machine as the only engine is a nice simplification. A few smaller items inline.
- sm_read_head() reads at most WOLFCERT_HTTP_HEADER_BUDGET bytes of header block and fails with WOLFCERT_ERR_PROTOCOL and a message once the budget is reached; nb_read_some() takes the limit sm_rx may fill to and fails when sm_rx already holds more. - chunked_decode_into() decodes a chunked body in place; wolfcert_http_chunked_decode() wraps it, and DynBuf and dyn_append() are removed. - Chunked, Content-Length and close-delimited bodies hand sm_rx over as the response body through sm_take_body(). A Content-Length body grows sm_rx once to the whole response; a close-delimited body is capped at max_response_bytes. - With WOLFSSL_STATIC_MEMORY and a pool in use, sm_rx grows to the full allowance in one step for a chunked or close-delimited body. - docs/EMBEDDED.md and README.md describe the header budget, the client response buffer and pool sizing. - Tests cover a header block over the budget and a close-delimited body over the cap, and the cap tests pad the body instead of the headers. Based on the patch from the wolfSSL#59 review, without its separate Content-Length buffer and in-place header parsing.
d464770 to
debe4b5
Compare
- The non-blocking state machine, with a new chunked body state, is the only response engine; the blocking reader is removed. - Blocking and _nb session requests share http_session_call(); while a request is in flight, only _nb with its resp continues it. - wolfcert_http_request() runs over a session with Connection: close. - Bytes past a response close the session, and a closed session gets WOLFCERT_ERR_CONN_CLOSED; residual and its replay are removed. - The request head is wiped once sent, and the header search resumes where the previous read stopped. - wolfcert/http.h, src/internal.h and docs/EMBEDDED.md describe the changes, and tests cover each of them.
- Remove WolfCertHttpSessionCfg.nonblocking; a TLS session starts in SM_HANDSHAKE and either request call works on any session. - A failed handshake returns WOLFCERT_ERR_TLS, or WOLFCERT_ERR_MEMORY when memory runs out, with the wolfSSL code; session close sends close_notify without waiting. - EST's two session opens now give the same session. - Docs cover mode changes between requests for transports, the new WOLFCERT_ERR_RC_WC(), session open not verifying the server, and req->body staying valid across _nb polls. - est_client_cases.h prints the rc, the wolfSSL code and the message when session_get_cacerts() fails in session_basic_auth(). - Tests drop the test_nb_* copies and cover truncated replies, the TLS error, an allocation failure in the handshake and mixed calls on EST sessions.
- sm_read_head() reads at most WOLFCERT_HTTP_HEADER_BUDGET bytes of header block and fails with WOLFCERT_ERR_PROTOCOL and a message once the budget is reached; nb_read_some() takes the limit sm_rx may fill to and fails when sm_rx already holds more. - chunked_decode_into() decodes a chunked body in place; wolfcert_http_chunked_decode() wraps it, and DynBuf and dyn_append() are removed. - Chunked, Content-Length and close-delimited bodies hand sm_rx over as the response body through sm_take_body(). A Content-Length body grows sm_rx once to the whole response; a close-delimited body is capped at max_response_bytes. - With WOLFSSL_STATIC_MEMORY and a pool in use, sm_rx first tries to grow to the full allowance in one step for a chunked or close-delimited body; when growing fails, nb_read_some() returns WOLFCERT_ERR_MEMORY with a message naming the settings. - docs/EMBEDDED.md describes the header budget, the client response buffer, pool sizing and how to fit the one-step block into the pool's largest bucket. - Tests cover a header block over the budget, a close-delimited body over the cap and chunked replies under a cap no pool block fits, and the cap tests pad the body instead of the headers. Based on the patch from the wolfSSL#59 review, without its separate Content-Length buffer and in-place header parsing.
- inspect_headers() picks the body state and resets sm_scan; sm_chunked is removed. - sm_tx_off replaces sm_head_off and sm_body_off. - closed and sm_head_request are one-bit fields. - sm_read_head(), sm_read_body_cl() and sm_read_body_chunked() report a close before the response ends as WOLFCERT_ERR_IO with a message; the Content-Length one gives the bytes received and expected. - test_truncated_reply_is_io() adds a cut-off chunked reply and checks each message.
- sm_read_head() reads at most WOLFCERT_HTTP_HEADER_BUDGET bytes of header block and fails with WOLFCERT_ERR_PROTOCOL and a message once the budget is reached; nb_read_some() takes the limit sm_rx may fill to and fails when sm_rx already holds more. - chunked_decode_into() decodes a chunked body in place; wolfcert_http_chunked_decode() wraps it, and DynBuf and dyn_append() are removed. - Chunked, Content-Length and close-delimited bodies hand sm_rx over as the response body through sm_take_body(). A Content-Length body grows sm_rx once to the whole response; a close-delimited body is capped at max_response_bytes. - With WOLFSSL_STATIC_MEMORY and a pool in use, sm_rx grows to the full allowance in one step for a chunked or close-delimited body. - docs/EMBEDDED.md and README.md describe the header budget, the client response buffer and pool sizing. - Tests cover a header block over the budget and a close-delimited body over the cap, and the cap tests pad the body instead of the headers. Based on the patch from the wolfSSL#59 review, without its separate Content-Length buffer and in-place header parsing.
debe4b5 to
91253a8
Compare
- sm_read_head() reads at most WOLFCERT_HTTP_HEADER_BUDGET bytes of header block and fails with WOLFCERT_ERR_PROTOCOL and a message once the budget is reached; nb_read_some() takes the limit sm_rx may fill to and fails when sm_rx already holds more. - chunked_decode_into() decodes a chunked body in place; wolfcert_http_chunked_decode() wraps it, and DynBuf and dyn_append() are removed. - Chunked, Content-Length and close-delimited bodies hand sm_rx over as the response body through sm_take_body(). A Content-Length body grows sm_rx once to the whole response; a close-delimited body is capped at max_response_bytes. - With WOLFSSL_STATIC_MEMORY and a pool in use, sm_rx first tries to grow to the full allowance in one step for a chunked or close-delimited body, and grows as bytes arrive when that fails. - docs/EMBEDDED.md describes the header budget, the client response buffer, pool sizing and how to fit the one-step block into the pool's largest bucket. - Tests cover a header block over the budget, a close-delimited body over the cap and a chunked reply under a cap larger than any pool block, and the cap tests pad the body instead of the headers. Based on the patch from the wolfSSL#59 review, without its separate Content-Length buffer and in-place header parsing.
91253a8 to
3839b30
Compare
- sm_read_head() reads at most WOLFCERT_HTTP_HEADER_BUDGET bytes of header block and fails with WOLFCERT_ERR_PROTOCOL and a message once the budget is reached; nb_read_some() takes the limit sm_rx may fill to and fails when sm_rx already holds more. - chunked_decode_into() decodes a chunked body in place; wolfcert_http_chunked_decode() wraps it, and DynBuf and dyn_append() are removed. - Chunked, Content-Length and close-delimited bodies hand sm_rx over as the response body through sm_take_body(). A Content-Length body grows sm_rx once to the whole response; a close-delimited body is capped at max_response_bytes. - With WOLFSSL_STATIC_MEMORY and a pool in use, sm_rx grows to the full allowance in one step for a chunked or close-delimited body, and fails with a message naming the settings when no pool block is that large. - docs/EMBEDDED.md describes the header budget, the client response buffer, pool sizing and how to fit the one-step block into the pool's largest bucket. - Tests cover a header block over the budget, a close-delimited body over the cap and a chunked reply whose cap no pool block fits, and the cap tests pad the body instead of the headers. Based on the patch from the wolfSSL#59 review, without its separate Content-Length buffer and in-place header parsing.
354e3ca to
87f7cb8
Compare
- sm_read_head() reads at most WOLFCERT_HTTP_HEADER_BUDGET bytes of header block and fails with WOLFCERT_ERR_PROTOCOL and a message once the budget is reached; nb_read_some() takes the limit sm_rx may fill to and fails when sm_rx already holds more. - chunked_decode_into() decodes a chunked body in place; wolfcert_http_chunked_decode() wraps it, and DynBuf and dyn_append() are removed. - Chunked, Content-Length and close-delimited bodies hand sm_rx over as the response body through sm_take_body(). A Content-Length body grows sm_rx once to the whole response; a close-delimited body is capped at max_response_bytes. - With WOLFSSL_STATIC_MEMORY and a pool in use, sm_rx grows to the full allowance in one step for a chunked or close-delimited body, and fails with a message naming the settings when no pool block is that large. - docs/EMBEDDED.md describes the header budget, the client response buffer, pool sizing and how to fit the one-step block into the pool's largest bucket. - Tests cover a header block over the budget, a close-delimited body over the cap and a chunked reply whose cap no pool block fits, and the cap tests pad the body instead of the headers. Based on the patch from the wolfSSL#59 review, without its separate Content-Length buffer and in-place header parsing.
Frauschi
left a comment
There was a problem hiding this comment.
Thanks for the thorough updates! All is fixed except one small static-memory issue, see #59 (comment)
87f7cb8 to
68ae328
Compare
Problem
src/http.chad two response engines, a blocking reader and the_nbstate machine, and they had drifted: chunked bodies, bytes past a body, handshake errors and bodies ending at the close were each handled differently. The state machine also copied every body, so a response peaked at about twice its size on the heap.Fix (
src/http.c)The state machine is the only engine; the blocking reader (about 450 lines) is deleted.
5f1f26druns blocking,_nband one-shot calls on it, chunked bodies included; bytes past a response close the session.bf4fcfdruns the TLS handshake on the first request and removesnonblocking.22a83e3(@Frauschi) caps the header block at 8 KiB and hands the receive buffer over asresp->body.68ae328trims the session struct and says why a truncated response failed.WolfCertHttpSessionCfg.nonblockingremoved*_session_open()no longer verifies the server; the first request returnsERR_TLS(blocking used to getERR_CRYPTO) orERR_MEMORY, withwolfcert_last_wolfssl_err()set_nbwith the samerespcontinues it; any other call getsBAD_ARGERR_CONN_CLOSED(wasBAD_ARG)ERR_PROTOCOL; a body ending at the close is capped atmax_response_bytesmax_response_bytes+ 8 KiB, else grows as before (up to 8192 bytes on default buckets); beyond that,ERR_MEMORYnaming both settings (EMBEDDED.md§4)_open/_open_asyncWolfCertTransport0and-1on one connection (ARCHITECTURE.md§4.6)Verification
-Werror. The tip is clean under ASan + UBSan, ASan + LeakSanitizer (Ubuntu 24.04) and the nightly ThreadSanitizer set, and passes autotoolsmake checkand the static-memory build.test_nb_*copies merge into cases run both ways. New cases cover chunked_nbreplies, truncation, bytes past a reply, the header budget, in-flight calls and handshake failures. Reverting a change fails a test, except the non-waitingclose_notify, the resumed header search and the global heap hint branch.Not in this PR
_open_async,ARCHITECTURE.md§4.3) is the next PR.Connection: close; timeouts on blocking reads;Transfer-EncodingwithContent-Length; short-write andWANT_WRITEtests.