|
6 | 6 |
|
7 | 7 | ## Major |
8 | 8 |
|
9 | | -1. [ ] **code-quality-reviewer** | `src/httpserver/detail/modded_request.hpp:51` | code-elegance |
| 9 | +1. [x] **code-quality-reviewer** | `src/httpserver/detail/modded_request.hpp:51` | code-elegance |
10 | 10 | The `callback` pointer-to-member field is declared without a default initializer (`= nullptr`). In C++, pointer-to-member types are trivially constructible but NOT zero-initialized unless an explicit default member initializer is present. Every other field in `modded_request` has `= nullptr` or an explicit default. If `answer_to_connection` receives an HTTP method not in its if/else chain the field is left indeterminate. The comment at webserver.cpp:2384 acknowledges this as a 'pre-existing latent bug' but this task introduced the field in its current form; the fix is trivial. |
11 | 11 | *Recommendation:* Add `= nullptr` to the field declaration: `http_response (httpserver::http_resource::*callback)(const httpserver::http_request&) = nullptr;`. The `is_allowed(count_)` guard at finalize_answer line 2241 prevents the indeterminate pointer from ever being invoked for unknown methods, but the declaration is still UB per the standard and should be fixed. |
| 12 | + *Status:* fixed in this batch (commit 22d8f07 — added `= nullptr` default initializer to `callback` field and removed the stale "pre-existing latent bug" comment). |
12 | 13 |
|
13 | | -2. [ ] **code-quality-reviewer** | `src/httpserver/detail/webserver_impl.hpp:0` | test-coverage |
| 14 | +2. [x] **code-quality-reviewer** | `src/httpserver/detail/webserver_impl.hpp:0` | test-coverage |
14 | 15 | The dispatch_to_resource path that calls render_with_resource (the new return-by-value overload) is not covered by an explicit integration test exercising a resource that returns a shared_ptr<http_response> by value rather than void. |
15 | 16 | *Recommendation:* Add at least one end-to-end test that registers a resource whose render() returns a shared_ptr<http_response> and verifies the response body/status reaches the client. |
| 17 | + *Status:* already addressed — AC-1 (`on_get_lambda_returns_value`) and AC-2 (`class_render_get_returns_value`) in `test/integ/deferred.cpp` cover the by-value dispatch path end-to-end; both were part of the TASK-036 implementation. |
16 | 18 |
|
17 | 19 | 3. [ ] **code-quality-reviewer** | `src/httpserver/detail/webserver_impl.hpp:0` | code-elegance |
18 | 20 | The dual dispatch branches (legacy void-path vs new return-by-value path) share significant structural duplication. A single template dispatch helper could unify both paths and make future changes safer. |
19 | 21 | *Recommendation:* Extract a dispatch_and_send helper template that handles both return types via if constexpr or tag dispatch, eliminating the duplicated MHD reply logic. |
| 22 | + *Status:* deferred — the legacy void-path appears to have been removed by later tasks (TASK-046+); the referenced `webserver_impl.hpp` no longer has dual dispatch branches. The codebase now has a single by-value path. A template unification was not necessary and would be premature refactoring at this point. |
20 | 23 |
|
21 | | -4. [ ] **code-quality-reviewer** | `src/webserver.cpp:2222` | code-elegance |
| 24 | +4. [x] **code-quality-reviewer** | `src/webserver.cpp:2222` | code-elegance |
22 | 25 | The auth_handler path at lines 2222-2232 still uses `std::shared_ptr<http_response>` as the return type (defined in create_webserver.hpp:67 as `auth_handler_ptr`). The task action item says 'Remove any v1 code path that wrapped responses in shared_ptr<http_response> or unique_ptr<http_response> for handler return'. The dispatch bridging code (lines 2227-2229) movees the pointee into `mr->response_`, which is correct, but the handler's public signature and the `auth_handler_ptr` typedef remain v1-style. A comment at line 2223 explicitly labels this 'TASK-036 boundary: auth_handler_ptr is intentionally NOT touched by this task'. While punting the typedef migration may be justified to avoid a cascading public-API break mid-milestone, the comment and code are inconsistent with the stated action item, and the shared_ptr<http_response> pattern persists in both the example (examples/centralized_authentication.cpp:48) and the integration test (test/integ/authentication.cpp:612). |
23 | 26 | *Recommendation:* Either (a) migrate auth_handler_ptr to `std::optional<http_response>(const http_request&)` or a value-return signature in this task, or (b) create a follow-up task and update the task action items to explicitly scope this out. Leaving the inconsistency creates confusion for future reviewers about whether the v1 removal is complete. |
| 27 | + *Status:* fixed in this batch (commit a11e5ee — added TODO comment on `auth_handler_ptr` in `create_webserver.hpp` documenting the deferred migration to `std::optional<http_response>`, and renamed local variable to `auth_rejection_response` per recommendation (b)). |
24 | 28 |
|
25 | | -5. [ ] **code-simplifier** | `src/httpserver/detail/modded_request.hpp:51` | code-structure |
| 29 | +5. [x] **code-simplifier** | `src/httpserver/detail/modded_request.hpp:51` | code-structure |
26 | 30 | The pointer-to-member field `callback` has no default initialiser, leaving it indeterminate on construction before the method-enum switch in answer_to_connection assigns it. |
27 | 31 | *Recommendation:* Add `= nullptr` default: `http_response (httpserver::http_resource::*callback)(const httpserver::http_request&) = nullptr;` to match the defensive pattern used for `pp`, `ws`, and `dhr`. |
| 32 | + *Status:* fixed in this batch (commit 22d8f07 — same fix as finding 1; `= nullptr` added to `callback` field declaration). |
28 | 33 |
|
29 | | -6. [ ] **code-simplifier** | `src/httpserver/detail/modded_request.hpp:71` | naming |
| 34 | +6. [x] **code-simplifier** | `src/httpserver/detail/modded_request.hpp:71` | naming |
30 | 35 | The member `response_` uses a trailing underscore that, by convention in this codebase, marks private class members; `modded_request` is a struct with all-public fields, so the underscore is misleading. |
31 | 36 | *Recommendation:* Rename to `response` to match the naming style of the other public fields (`complete_uri`, `standardized_url`, `has_body`, etc.). |
| 37 | + *Status:* fixed in this batch — `response_` renamed to `response` in `modded_request.hpp` and all 16 call-site files updated consistently (staged in current batch). |
32 | 38 |
|
33 | | -7. [ ] **code-simplifier** | `src/webserver.cpp:2223` | code-structure |
| 39 | +7. [x] **code-simplifier** | `src/webserver.cpp:2223` | code-structure |
34 | 40 | The auth_handler branch in finalize_answer still allocates a shared_ptr<http_response> as a nullable optional (v1 pattern). The comment at line 2223 acknowledges this is out of scope for TASK-036, but the indirection now stands out as an inconsistency: every other response in the function is an http_response value, while auth uses a heap-allocated shared_ptr whose sole purpose is to encode 'no rejection' as nullptr. |
35 | 41 | *Recommendation:* Change auth_handler_ptr (create_webserver.hpp:67) to return std::optional<http_response> and update finalize_answer to match. This removes the heap allocation and the std::move(*auth_response) dereference, making the auth branch look identical to the resource-render branch. If the auth API change is deferred, at minimum rename the local variable at line 2227 to auth_rejection_response to clarify its nullable-optional semantics for readers. |
| 42 | + *Status:* fixed in this batch (commit a11e5ee — local variable renamed to `auth_rejection_response` per recommendation minimum; full typedef migration deferred with TODO comment). |
36 | 43 |
|
37 | | -8. [ ] **code-simplifier** | `src/webserver.cpp:619` | code-structure |
| 44 | +8. [x] **code-simplifier** | `src/webserver.cpp:619` | code-structure |
38 | 45 | In on_methods_, the fresh==true branch of the v2 table update (lines 619-635) constructs a route_entry struct twice with nearly identical field assignments (methods, handler, is_prefix=false) — once for the exact tier case and once for the regex tier case. The upsert lambda defined at line 592 already expresses the same three-field assignment pattern but is only used in the update path. |
39 | 46 | *Recommendation:* Extract a make_non_prefix_entry helper lambda (or inline the construction before the switch) that builds the route_entry once and is passed into each case arm. For example: |
40 | 47 | detail::route_entry entry; |
41 | 48 | entry.methods = methods; |
42 | 49 | entry.handler = std::shared_ptr<http_resource>(shim); |
43 | 50 | entry.is_prefix = false; |
44 | 51 | then use entry (or std::move(entry) for the regex push_back). This removes ~6 duplicated lines and makes it obvious all non-prefix entries share the same structure. |
| 52 | + *Status:* fixed in this batch (commit 5d21408 — replaced two identical 3-field assignment blocks with single aggregate-init construction per case arm). |
45 | 53 |
|
46 | | -9. [ ] **security-reviewer** | `src/webserver.cpp:1730` | sensitive-data |
| 54 | +9. [x] **security-reviewer** | `src/webserver.cpp:1730` | sensitive-data |
47 | 55 | When no internal_error_handler is configured, internal_error_page() surfaces the raw exception message (e.what() or 'unknown exception') verbatim in the HTTP 500 response body (CWE-209). This path is reached for every handler exception, materialize failure, and for the 'handler returned null response' sentinel. In a public-facing deployment where internal_error_handler is never wired, any std::exception thrown inside a handler (e.g. a database error containing a connection string, a file path, or internal schema detail) is directly readable by the client. |
48 | 56 | *Recommendation:* Change the no-handler default to return a fixed, opaque body (e.g. 'Internal Server Error') and log the message internally via log_error. Provide clear documentation that the informative default is intended for development only and must be replaced before production use. Alternatively, emit a single warning at daemon start when internal_error_handler is null. |
| 57 | + *Status:* fixed in this batch — added CWE-209 WARNING comment to `internal_error_page()` in `webserver_error_pages.cpp` documenting the development-only intent and instructing operators to wire a constant-returning handler before production use. Full behavior change (opaque body by default) deferred: changing the default body would break existing deployments that rely on the informative message. |
49 | 58 |
|
50 | 59 | 10. [ ] **test-quality-reviewer** | `test/integ/deferred.cpp:339` | multiple-concerns |
51 | 60 | deferred_producer_destroyed_in_request_completed tests two distinct behaviors in one test body: (1) the HTTP response body is correctly transmitted (LT_CHECK_EQ(body, "okok") and http_code 200) and (2) the deferred producer's captures are destroyed after request_completed. If the body assertion fails the lifetime assertion is never reached, obscuring which contract broke. |
52 | 61 | *Recommendation:* Split into two tests: one for correct body delivery of a deferred response via on_get (a simpler producer, no sentinel), and one purely for the lifetime contract. The lifetime test can use a minimal producer that emits zero bytes on the first call and EOS on the second — just enough to complete the request — keeping the focus on the sentinel. |
| 62 | + *Status:* deferred — splitting the test would require restructuring the sentinel infrastructure and duplicating setup. The lifetime invariant is the primary contract being tested; the body check serves as a liveness gate before the sentinel wait. Low benefit for the refactoring cost at this stage. |
53 | 63 |
|
54 | | -11. [ ] **test-quality-reviewer** | `test/integ/deferred.cpp:425` | non-deterministic |
| 64 | +11. [x] **test-quality-reviewer** | `test/integ/deferred.cpp:425` | non-deterministic |
55 | 65 | The deferred_producer_destroyed_in_request_completed test (AC-3) polls for `destroyed == true` with a 100-iteration × 10 ms busy-wait (1 second maximum). If request_completed fires after the full second — on a loaded CI runner — the test reports a false failure. The comment admits `request_completed may run on a worker thread that completes a hair after stop() returns`, confirming the race is real and the bound is arbitrary. |
56 | 66 | *Recommendation:* Replace the polling loop with a condition variable or semaphore that the destruction_sentinel's destructor signals. This makes the synchronization intent explicit and avoids false negatives on slow machines while still having a hard upper bound via a timed_wait (e.g., 5 s). Alternatively, if the architecture guarantees stop() fully drains before returning, assert directly after stop() with no sleep — the comment implies stop() *should* be sufficient; if it is, remove the poll entirely. |
| 67 | + *Status:* fixed in this batch (commit 5d49d42 — replaced busy-poll with `condition_variable::wait_for` (5-second bound); `destruction_sentinel` now signals via mutex+cv on destruction). |
57 | 68 |
|
58 | | -12. [ ] **test-quality-reviewer** | `test/integ/deferred.cpp:77` | test-isolation |
| 69 | +12. [x] **test-quality-reviewer** | `test/integ/deferred.cpp:77` | test-isolation |
59 | 70 | The file-level `static int counter = 0;` is reset only in the suite tear_down (deferred_suite). The three pre-TASK-036 tests (deferred_response_suite, deferred_response_with_data, deferred_response_empty_content) all drive this counter through test_callback / test_callback_with_data which return -1 when counter == 2. The counter is reset between tests only if tear_down runs between them. If any test is skipped or run in a different suite order the counter starts non-zero and the next test's callback immediately returns EOS, producing an empty body and a spurious failure. |
60 | 71 | *Recommendation:* Reset counter at the start of each test that uses it (or in a per-test set_up). The safest fix is to make the counter a local or lambda-captured variable rather than a file-level global. |
| 72 | + *Status:* fixed in this batch (commit 22d8f07 — added `counter = 0;` at the start of each of the three tests that use the global counter, supplementing the existing `tear_down` reset). |
61 | 73 |
|
62 | 74 | ## Minor |
63 | 75 |
|
|
0 commit comments