Skip to content

perry-ext-http: failed https/http2 listen emits 'error', never 'listening' (follow-up to #11144) - #11180

Merged
proggeramlug merged 2 commits into
mainfrom
tokio-laneA2-listen-error-followup
Sep 24, 2026
Merged

proggeramlug merged 2 commits into
mainfrom
tokio-laneA2-listen-error-followup

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Follow-up to #11144. This fixes the CodeRabbit findings that were in #11144's diff. That PR merged at c1f36c25b before this fix could be pushed there.

Fixes

A failed listen() emits 'error' and never 'listening', on every server type.

  • HTTP/2 before: on a bind error, try_listen_on_turnloop returned Some((0, ..)). Both listen routes treated that as success, so they emitted 'listening' and ran the listen(cb) callback while server.listening === false.
  • HTTP/2 now: it returns H2Listen::{Bound, Failed, NoLoop, NoTls}. Failed queues the server's 'error', and only Bound arms 'listening'. The "tls config unavailable" message no longer covers the no-loop case.
  • HTTPS before: a bind failure only printed to stderr; no 'error' fired.
  • HTTPS now: it queues 'error' like http.createServer. The posted no-loop/busy case is queued as 'error' too, rather than printed.

post_to_owner separates "no loop" from "owner busy".

  • It now returns Posted::{Accepted, NoRoute, Busy}.
  • Only NoRoute is reported as ENOTSUP. An exhausted retry budget is EAGAIN.
  • Retries are bounded at 256, with a 1 ms backoff after 16 yields. Before, 64 spins mis-reported as ENOTSUP.

A SCHED_RR descriptor that adopt_stream refuses now frees its reserved handle id.

Tests

  • test-files/test_gap_listen_eaddrinuse.ts (new). A server holds a port, then http, https, http2 and http2-secure each listen() on it. Node 26.5.1 prints error EADDRINUSE listen listening=false cb=false listeningEvent=false for all four.
    • On c1f36c25b (before the fix) it gets PARITY_FAIL: https: no-error … and http2*: no-error listening=false cb=true listeningEvent=true.
    • With this change it passes, on this branch rebuilt on current main.
  • post_outcome_separates_no_route_from_a_busy_owner (new unit test). It drives the retry policy through an injected poster. It could not be written against the old bool return, which had no busy state.
  • The id-free on a refused adoption has no test. Forcing adopt_stream to fail needs a loop that refuses an attach, which no harness here has. I checked it by reading the code.

Validation

Run on perrymaster (Linux) with Node 26.5.1, on a fresh build of this branch on main c7d09635b (which includes main's turnloop alpha.8 bump).

  • Gap tests (PASS): test_gap_listen_eaddrinuse, test_gap_turnloop_http2_server, test_gap_turnloop_https_server, test_gap_turnloop_http_server, cluster_4962.
  • test_gap_http2_alpn_secure: PARITY_FAIL, as it already was on the perry-ext-http: node:http/https/http2 servers on turnloop only; drop hyper + hyper-util (tokio lane A2) #11144 base and branch.
  • cargo test --release -p perry-ext-http: 146 pass, 1 fail. The failure is tls_client::tests::needs_custom_client_logic, which already fails on main.
  • Gates: cargo fmt --all -- --check, check_file_size.sh, tokio_inventory.py and gc_runtime_root_holders.py all OK.
  • SKIP_COMPILE_GATES=1 run_lint_gates.sh: 87/88 script gates pass; the compile tier was not run. The one failure is its cargo-xwin step, which isn't installed on the host.
  • cargo xwin check -p perry-ext-http -p perry-runtime -p perry-ffi --target x86_64-pc-windows-msvc: OK on the pre-rebase commit (private cargo-xwin v0.23.0 install). It shows the same 3 pre-existing cluster_bind warnings.

Not in this PR

Two findings were real but predate #11144, so I filed them as issues:

Not run

Summary by CodeRabbit

  • Bug Fixes
    • HTTP, HTTPS, and HTTP/2 servers now report failed listen attempts through an error event instead of incorrectly signaling that listening began or only logging the failure.
    • When a server cannot reach its owner to start listening because the owner is temporarily busy, the failure is now reported as EAGAIN; ENOTSUP is reserved for cases where no loop exists.
    • Failed attempts to adopt a connection no longer leave a handle ID reserved.

Ralph Küpper added 2 commits September 24, 2026 00:54
…ing; distinguish busy postbox from no loop

Addresses CodeRabbit review on #11144:
- http2: a bind failure (Some((0, ..))) was treated as success and fired
  listening + the listen(cb) callback; https only printed to stderr. Both
  now queue the server error event, like http.Server.listen.
- post_to_owner returns Accepted / NoRoute / Busy; only NoRoute is ENOTSUP,
  an exhausted retry budget is EAGAIN. Retries back off after 16 yields.
- free the reserved id when adopt_stream refuses a SCHED_RR descriptor.
- test_gap_listen_eaddrinuse.ts: fails on c1f36c2, matches Node 26.5.1.
@coderabbitai

coderabbitai Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 88c13dd2-e0fd-4a2e-8ca1-32e94054bf27

📥 Commits

Reviewing files that changed from the base of the PR and between 25ef463 and c91dc50.

📒 Files selected for processing (9)
  • changelog.d/11180-ext-http-listen-error-followup.md
  • crates/perry-ext-http/src/server/http2_server.rs
  • crates/perry-ext-http/src/server/http2_server/turnloop_listen.rs
  • crates/perry-ext-http/src/server/https_server.rs
  • crates/perry-ext-http/src/server/server.rs
  • crates/perry-ext-http/src/server/server/deferred_events.rs
  • crates/perry-ext-http/src/server/turnloop_serve/mod.rs
  • crates/perry-ext-http/src/server/turnloop_serve/tests.rs
  • test-files/test_gap_listen_eaddrinuse.ts

Included review availability: Your plan provides up to 8 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

Listen failures on HTTPS and HTTP/2 now queue server error events instead of reporting bind failures to stderr. Owner-post failures distinguish missing routes from exhausted retries. The changes also add regression coverage for EADDRINUSE across HTTP, HTTPS, h2c, and secure HTTP/2 servers.

Changes

Listen failure handling

Layer / File(s) Summary
Owner-post outcomes and retries
crates/perry-ext-http/src/server/turnloop_serve/mod.rs, crates/perry-ext-http/src/server/turnloop_serve/tests.rs
post_to_owner returns accepted, no-route, or busy outcomes. It retries up to 256 times, yielding for the first 16 attempts and then sleeping 1 ms between attempts. Busy outcomes map to EAGAIN; failed stream adoption returns the allocated handle id. Tests cover the post outcomes and retry limit.
Listen error events and regression coverage
crates/perry-ext-http/src/server/http2_server/turnloop_listen.rs, crates/perry-ext-http/src/server/http2_server.rs, crates/perry-ext-http/src/server/https_server.rs, crates/perry-ext-http/src/server/server.rs, crates/perry-ext-http/src/server/server/deferred_events.rs, test-files/test_gap_listen_eaddrinuse.ts, changelog.d/11180-ext-http-listen-error-followup.md
HTTP/2 listen outcomes distinguish bound, failed, missing-loop, and missing-TLS cases. HTTPS and HTTP/2 bind and post failures queue server error events. Listen-post errors use the returned error code, and EAGAIN maps to “resource temporarily unavailable.” The regression test probes four server types on an occupied port and records error, listening, and callback signals.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to c91dc

The reported listen-failure behavior has no remaining actionable issue in this review and is ready to merge after normal checks.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 15 functions across 8 files. (1 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: failed HTTPS and HTTP/2 listens now emit 'error' instead of 'listening'.
Description check ✅ Passed The description provides a clear summary, detailed changes, related issue context, tests, validation results, known failures, and unrun checks. It does not copy the template headings or checklist, but…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 15 functions across 8 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR

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.

@proggeramlug

Copy link
Copy Markdown
Contributor Author

Ready for a train. Follow-up to #11144; carries the CodeRabbit fixes that missed its merge. Bind failures (EADDRINUSE) on http2, http2-secure and https now emit 'error' only: no 'listening' event and no listen callback, matching Node. A full postbox reports EAGAIN instead of ENOTSUP, and a refused SCHED_RR descriptor frees its handle id. The new gap test test_gap_listen_eaddrinuse fails at c1f36c2 and passes on the branch. It touches perry-ext-http only.

@proggeramlug
proggeramlug merged commit a861b1a into main Sep 24, 2026
53 of 55 checks passed
@proggeramlug
proggeramlug deleted the tokio-laneA2-listen-error-followup branch September 24, 2026 06:21
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