Skip to content

fix(grpc/http): review follow-ups — mask drift, body caps, flow-control, HEAD, compression API#109

Merged
EdmondDantes merged 1 commit into
mainfrom
fix/grpc-review-followups
Jul 9, 2026
Merged

fix(grpc/http): review follow-ups — mask drift, body caps, flow-control, HEAD, compression API#109
EdmondDantes merged 1 commit into
mainfrom
fix/grpc-review-followups

Conversation

@EdmondDantes

Copy link
Copy Markdown
Contributor

Follow-up fixes from a code review of the merged gRPC branch (#104). Seven confirmed correctness bugs plus layering/perf/cleanup debt. No behaviour change to already-passing paths; clean build.

Correctness

  • gRPC-only h2c on workershttp_protocol_registration_mask() is now the single source for the protocol→mask rule, used by both addXHandler registration and the worker-side transit rebuild. Previously the rebuild dropped the HTTP2 bit for a gRPC-only server, so worker threads silently closed plaintext h2c connections.
  • Streaming body size cap — the cumulative max_body_size ceiling was restored for H2/H3 streaming bodies (a live-queued-only cap had replaced it). Waived per-stream only for gRPC (streaming RPCs are unbounded by design); the in-flight window still bounds memory.
  • H3 double flow-control credit — buffered→streaming upgrade tracks body_precredited so the spliced prefix isn't window-credited twice.
  • HEAD + streamingresponse->is_head stamped at dispatch; send() drops chunks so no DATA follows the header block (RFC 9110 §9.3.2), across all transports.
  • grpc-timeout overflow — clamp to UINT64_MAX instead of wrapping to a bogus small deadline.
  • Worker sink — checks the STREAM_HEADERS post result before building the chunk wire; the 100ms blocking nanosleep retry is replaced by a hidden one-shot timer + per-thread FIFO so the worker thread never blocks.
  • reactor_pool_exec — escapes the done-wait loop when the reactor reaches DONE (latent shutdown-hang guard).

Layering / performance

  • Classify gRPC oncehttp_request_classify_protocols() stamps grpc_mode + body-policy flags at headers-complete; transports read neutral flags, no longer #include grpc.h or re-parse content-type per readMessage().
  • Compression APIsetGrpcEncoding('gzip') declared before the first message (per-call, matching grpc-go/java/c++/python); the per-message writeMessage($msg, $compress) arg is removed, so a compressed frame with no declared encoding can no longer be expressed.
  • Event-driven backpressure — the reactor wakes a parked producer via stream_credit_wake instead of 2–32ms timer polling; same hysteresis wake for the H3 static throttle.

Cleanup

  • response_wire_discard() replaces three copy-pasted discard blocks.
  • async_coroutine_sleep_ms() shared helper (was 4 near-identical copies).
  • http_protocol_pick_handler() in the H3 coroutine entry.
  • deleted the dead http3_build_listener_local wrapper.

Tests

gRPC 15/15, h2+h3 80/80 pass. 008-grpc-compression updated to the new setGrpcEncoding API.

⚠️ Note: a pre-existing, non-deterministic teardown hang in core/055–057 (pool graceful-shutdown) is present on main independent of this PR — proven with a clean-tree repro. Tracked separately.

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Coverage

Total lines: 81.31% → 81.04% (-0.26 pp)

File Baseline Current Δ Touched
src/core/async_plain_event.c 92.59% 88.89% -3.70 pp
src/core/http_connection.c 74.90% 74.95% +0.05 pp
src/core/http_protocol_handlers.c 78.08% 79.75% +1.66 pp
src/core/http_protocol_strategy.h 0.00% 33.33% +33.33 pp
src/core/reactor_pool.c 79.41% 78.99% -0.43 pp
src/core/worker_dispatch.c 68.35% 67.60% -0.75 pp
src/grpc/grpc.c 92.91% 92.86% -0.06 pp
src/http1/http_parser.c 84.21% 84.27% +0.06 pp
src/http2/http2_session.c 88.72% 88.78% +0.06 pp
src/http2/http2_strategy.c 79.30% 79.35% +0.05 pp
src/http3/http3_callbacks.c 79.27% 79.36% +0.09 pp
src/http3/http3_connection.c 81.58% 81.43% -0.15 pp
src/http3/http3_dispatch.c 77.44% 78.41% +0.97 pp
src/http3/http3_io.c 86.55% 86.55% +0.00 pp
src/http3/http3_listener.c 76.72% 76.57% -0.15 pp
src/http3/http3_static_response.c 11.59% 10.74% -0.86 pp
src/http3/http3_stream.c 96.39% 93.98% -2.41 pp
src/http_body_stream.c 83.61% 84.85% +1.24 pp
src/http_request.c 82.93% 82.93% +0.00 pp
src/http_response.c 87.44% 86.20% -1.25 pp
src/http_response_internal.h 100.00% 100.00% +0.00 pp
src/http_response_server_api.c 88.36% 88.54% +0.18 pp
src/http_server_class.c 70.42% 69.07% -1.36 pp
src/log/http_log.c 67.14% 67.03% -0.11 pp

❌ Regression in touched files (> 1.0 pp drop)

  • src/core/async_plain_event.c dropped -3.70 pp
  • src/http_response.c dropped -1.25 pp
  • src/http_server_class.c dropped -1.36 pp

Add [coverage-drop-ok] to a commit message in this PR to override.

…ol, HEAD, compression API

Review of the merged gRPC branch (#104) surfaced 7 confirmed bugs plus
layering/cleanup debt. Fixes, no behaviour change to passing paths:

Correctness:
- protocol→mask: single http_protocol_registration_mask() used by both
  addXHandler registration and the worker-side transit rebuild, so a
  gRPC-only server keeps the HTTP2 bit on worker threads (h2c no longer
  silently refused).
- H2/H3 streaming request bodies: restore the cumulative max_body_size
  ceiling that a live-only cap had dropped; waived per-stream only for
  gRPC (unbounded by design). In-flight cap still bounds memory.
- H3 buffered→streaming upgrade: track body_precredited so spliced
  prefix bytes aren't flow-control-credited twice.
- HEAD: response->is_head stamped at dispatch; send() drops chunks so no
  DATA follows the header block (RFC 9110 §9.3.2) on every transport.
- grpc-timeout: clamp to UINT64_MAX instead of uint64 overflow wrap.
- worker sink: check STREAM_HEADERS post result before building the chunk
  wire; replace the 100ms blocking nanosleep retry with a hidden one-shot
  timer + per-thread FIFO so the worker thread never blocks.
- reactor_pool_exec: escape the done-wait loop when the reactor reaches
  DONE (latent shutdown-hang guard).

Layering / performance:
- Classify gRPC once: http_request_classify_protocols() stamps grpc_mode
  + body policy flags at headers-complete; transports read neutral flags
  and no longer include grpc.h or re-parse content-type per readMessage.
- Compression API: setGrpcEncoding('gzip') declared before the first
  message (per-call, matching grpc-go/java/c++); drop the per-message
  writeMessage($msg, $compress) arg — a compressed frame with no declared
  encoding can no longer be expressed.
- Event-driven credit backpressure: reactor wakes the parked producer via
  stream_credit_wake instead of 2–32ms timer polling; same for the H3
  static throttle hysteresis wake.

Cleanup:
- response_wire_discard() replaces three copy-pasted discard blocks.
- async_coroutine_sleep_ms() shared helper (was 4 near-identical copies).
- http_protocol_pick_handler() in the H3 coroutine entry.
- delete the dead http3_build_listener_local wrapper.
@EdmondDantes EdmondDantes force-pushed the fix/grpc-review-followups branch from 86c7537 to 04aa3ad Compare July 9, 2026 08:04
@EdmondDantes EdmondDantes merged commit ed56619 into main Jul 9, 2026
0 of 5 checks passed
@EdmondDantes EdmondDantes deleted the fix/grpc-review-followups branch July 9, 2026 08:17
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.

1 participant