Repository navigation
fix(http): pass the connection socket to server 'connection' listeners #11328
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
37187b8
c4d039b
17e0cdc
3be2e68
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| Fixed `http.Server`/`https.Server` `'connection'` listeners receiving no | ||
| argument — Node passes the accepted `net.Socket`, the same object later | ||
| exposed as `req.socket` for every request on that connection, and libraries | ||
| key open connections by `conn.remoteAddress + ':' + conn.remotePort` and drop | ||
| them on the socket's `'close'` (e.g. the `server-destroy` package, used by | ||
| Astro's `@astrojs/node` adapter). Perry fired the listener with no args | ||
| (`emit_no_arg_to_listeners`); user code reading `conn.remoteAddress` then | ||
| threw `TypeError: Cannot read properties of undefined`. | ||
|
|
||
| Root cause / fix: `PENDING_CONNECTION_EVENTS` queued only the server handle. | ||
| Every accepted turnloop connection now also builds a connection-socket object | ||
| at accept time (`alloc_connection_socket`, reusing the existing | ||
| `IncomingMessage` type rather than a new one — it already has | ||
| `remoteAddress`/`remotePort`, an `on`/`once`/`destroy` dispatch surface, and a | ||
| `'close'` emit), and the queue carries `(server_handle, socket_handle)`. The | ||
| `'connection'` listener now receives that socket; `finish_request` assigns it | ||
| as `req.socket`/`req.connection` for every request on the connection, through | ||
| the override mechanism `#4904` already added for `new | ||
| http.IncomingMessage(socket)`, so `req.socket === conn`. When the connection's | ||
| terminal `Closed` completion arrives, its socket handle is queued (the | ||
| completion sink cannot run JS) and the pump fires `'close'` on it before | ||
| draining the server's own deferred `'close'` callback. HTTP/2's accept path | ||
| gets a socket argument too, through the same shared queue, without wiring | ||
| `req.socket` on H2 streams (out of scope: `Http2ServerRequest` has no such | ||
| alias). | ||
|
|
||
| Known gaps left out of scope: `res.socket`/`res.connection` still self-refer | ||
| instead of aliasing the connection socket; `conn.destroy()` fires the socket's | ||
| `'close'` immediately without tearing down the underlying TCP connection (it | ||
| reuses the generic `IncomingMessage.destroy()`, which never closed the | ||
| connection for a plain `req.destroy()` either); the `http2` ALPN-to-HTTP/1.1 | ||
| handoff (`adopt_alpn_http1`) still never fires `'connection'` at all (a | ||
| pre-existing gap, unrelated to this fix — `req.socket` is still wired there | ||
| for consistency). | ||
|
|
||
| Test: `test-parity/node-suite/http/server/connection-event-socket.ts` | ||
| (argument shape, `req.socket === conn`, `'close'` firing and connection | ||
| bookkeeping via the `server-destroy` idiom, event ordering). |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -494,7 +494,16 @@ fn on_accept(listener_id: i64, conn_id: i64) { | |
| } | ||
| } | ||
| insert(conn); | ||
| crate::server::server::queue_turnloop_connection_event(server_handle); | ||
| // Give HTTP/2's `'connection'` listener a | ||
| // real socket argument too, same shared drain path as HTTP/1.1. Not | ||
| // stored on `H2Conn` — HTTP/2 streams don't wire `req.socket` from it | ||
| // (out of scope here; Node's own `Http2ServerRequest` has no `.socket` | ||
| // alias to a per-connection object the way `http`'s does). | ||
| let connection_socket = crate::server::request::alloc_connection_socket( | ||
| peer.as_ref().map(|e| e.address.clone()).unwrap_or_default(), | ||
| peer.as_ref().map(|e| e.port).unwrap_or(0), | ||
| ); | ||
| crate::server::server::queue_turnloop_connection_event(server_handle, connection_socket); | ||
|
Comment on lines
+497
to
+506
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: sed -n '485,525p' crates/perry-ext-http/src/server/turnloop_h2/conn.rs
sed -n '1310,1350p' crates/perry-ext-http/src/server/turnloop_h2/conn.rs
rg -n 'close_incoming_message|note_closed_socket|socket_handle|read_start|on_closed' crates/perry-ext-http/src/server/turnloop_h2/conn.rsRepository: PerryTS/perry Length of output: 4472 🏁 Script executed: set -eu
printf '%s\n' '--- direct definitions and usages ---'
rg -n -C 8 'fn alloc_connection_socket|alloc_connection_socket|queue_turnloop_connection_event|turnloop_connection_closed|close_incoming_message|note_closed_socket|socket_handle|on_closed|destroy_connection|fn insert|fn forget|owned_ids' crates/perry-ext-http/src
printf '%s\n' '--- changed range and comparison summary ---'
git diff --stat 182e375978a8bfed26ce382e55ee7273ee104ad3 3be2e68f11111942a7a9a6a9936e9391fe6e1811 -- crates/perry-ext-http/src/server/turnloop_h2/conn.rs
git diff --unified=20 182e375978a8bfed26ce382e55ee7273ee104ad3 3be2e68f11111942a7a9a6a9936e9391fe6e1811 -- crates/perry-ext-http/src/server/turnloop_h2/conn.rsRepository: PerryTS/perry Length of output: 41434 🏁 Script executed: set -eu
rg -n -C 8 'fn alloc_connection_socket|alloc_connection_socket|queue_turnloop_connection_event|turnloop_connection_closed|close_incoming_message|note_closed_socket|socket_handle|on_closed|destroy_connection|fn insert|fn forget|owned_ids' crates/perry-ext-http/src
printf '%s\n' '--- changed range and comparison summary ---'
git diff --stat 182e375978a8bfed26ce382e55ee7273ee104ad3 3be2e68f11111942a7a9a6a9936e9391fe6e1811 -- crates/perry-ext-http/src/server/turnloop_h2/conn.rs
git diff --unified=20 182e375978a8bfed26ce382e55ee7273ee104ad3 3be2e68f11111942a7a9a6a9936e9391fe6e1811 -- crates/perry-ext-http/src/server/turnloop_h2/conn.rsRepository: PerryTS/perry Length of output: 41869 🏁 Script executed: set -eu
printf '%s\n' '--- H2 ownership, dispatch, and terminal paths ---'
sed -n '320,420p' crates/perry-ext-http/src/server/turnloop_h2/conn.rs
sed -n '700,790p' crates/perry-ext-http/src/server/turnloop_h2/conn.rs
sed -n '880,945p' crates/perry-ext-http/src/server/turnloop_h2/conn.rs
sed -n '1180,1260p' crates/perry-ext-http/src/server/turnloop_h2/conn.rs
printf '%s\n' '--- socket allocator and close implementation ---'
rg -n -C 12 'pub\(crate\)? fn alloc_connection_socket|fn alloc_connection_socket|pub\(crate\)? fn close_incoming_message|fn close_incoming_message|take_closed_sockets|drain.*connection|PENDING_CONNECTION_EVENTS|js_node_http_server_process_pending' crates/perry-ext-http/src
printf '%s\n' '--- connection-event pump ---'
rg -n -C 20 'PENDING_CONNECTION_EVENTS|queue_turnloop_connection_event|close_incoming_message|take_closed_sockets' crates/perry-ext-http/src/server/server crates/perry-ext-http/src/server/request.rs crates/perry-ext-http/src/lib.rsRepository: PerryTS/perry Length of output: 42633 🏁 Script executed: set -eu
printf '%s\n' '--- server pump exact ranges ---'
rg -n 'pub extern "C" fn js_node_http_server_process_pending|fn process_pending|PENDING_CONNECTION_EVENTS|take_closed_sockets|close_incoming_message|fire_server_event' crates/perry-ext-http/src/server/server.rs crates/perry-ext-http/src/server/server/*.rs crates/perry-ext-http/src/server/request.rs
sed -n '820,980p' crates/perry-ext-http/src/server/server.rs
sed -n '840,905p' crates/perry-ext-http/src/server/request.rs
printf '%s\n' '--- H2 terminal exact ranges ---'
sed -n '1180,1265p' crates/perry-ext-http/src/server/turnloop_h2/conn.rs
sed -n '1300,1345p' crates/perry-ext-http/src/server/turnloop_h2/conn.rsRepository: PerryTS/perry Length of output: 14189 🏁 Script executed: set -eu
sed -n '1078,1142p' crates/perry-ext-http/src/server/server.rs
sed -n '235,250p' crates/perry-ext-http/src/server/server.rs
rg -n -C 6 'drop_handle\(.*socket|drop_handle\(socket|connection_socket|socket_handle' crates/perry-ext-http/src/server/server crates/perry-ext-http/src/server/turnloop_serve crates/perry-ext-http/src/server/turnloop_h2Repository: PerryTS/perry Length of output: 21268 🏁 Script executed: set -eu
sed -n '45,125p' crates/perry-ext-http/src/server/turnloop_h2/conn.rs
sed -n '450,515p' crates/perry-ext-http/src/server/turnloop_h2/conn.rsRepository: PerryTS/perry Length of output: 5729 🏁 Script executed: set -eu
rg -n -A 55 -B 5 'struct H2Conn' crates/perry-ext-http/src/server/turnloop_h2/conn.rsRepository: PerryTS/perry Length of output: 3442 Retain the HTTP/2 connection socket until transport close. The HTTP/2 accept path queues the socket handle but does not store it on Retain the handle until terminal cleanup and enqueue it from Suggested fixdiff --git a/crates/perry-ext-http/src/server/turnloop_h2/conn.rs b/crates/perry-ext-http/src/server/turnloop_h2/conn.rs
@@
pub(crate) peer_address: String,
pub(crate) peer_port: u16,
+ pub(crate) socket_handle: i64,
@@
peer_address: peer.as_ref().map(|e| e.address.clone()).unwrap_or_default(),
peer_port: peer.as_ref().map(|e| e.port).unwrap_or(0),
+ socket_handle: 0,
@@
- insert(conn);
let connection_socket = crate::server::request::alloc_connection_socket(
peer.as_ref().map(|e| e.address.clone()).unwrap_or_default(),
peer.as_ref().map(|e| e.port).unwrap_or(0),
);
+ conn.socket_handle = connection_socket;
+ insert(conn);
crate::server::server::queue_turnloop_connection_event(server_handle, connection_socket);
@@
crate::server::http2_server::mark_turnloop_session_closed(conn.session_handle);
+ if conn.socket_handle != 0 {
+ crate::server::turnloop_serve::note_closed_socket(conn.socket_handle);
+ }
if conn.server_handle != 0 {
crate::server::server::turnloop_connection_closed(id);
}
diff --git a/crates/perry-ext-http/src/server/turnloop_serve/conn.rs b/crates/perry-ext-http/src/server/turnloop_serve/conn.rs
@@
-fn note_closed_socket(socket_handle: i64) {
+pub(crate) fn note_closed_socket(socket_handle: i64) {🤖 Prompt for AI Agents |
||
| if let Err(_err) = tl::read_start(conn_id) { | ||
| destroy_connection(conn_id); | ||
| return; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,7 +19,10 @@ use perry_ffi::turnloop_net as tl; | |
| use turnloop_http::http1; | ||
|
|
||
| use super::wire::{self, Framing}; | ||
| use crate::server::request::{alloc_incoming_message, IncomingMessage}; | ||
| use crate::server::request::{ | ||
| alloc_connection_socket, alloc_incoming_message, handle_to_pointer_f64, incoming_socket_assign, | ||
| IncomingMessage, | ||
| }; | ||
| use crate::server::response::{alloc_server_response_for_turnloop, ResponseShape}; | ||
| use crate::server::server::{with_base_server, HttpPendingRequest}; | ||
|
|
||
|
|
@@ -72,6 +75,11 @@ pub(crate) struct Conn { | |
| server_handle: i64, | ||
| peer_address: String, | ||
| peer_port: u16, | ||
| /// The `IncomingMessage`-shaped object | ||
| /// handed to `server.on('connection', ...)` and shared as `req.socket` | ||
| /// by every request this connection carries. One per connection, built | ||
| /// once in `start_connection`/`adopt_alpn_http1`. | ||
| socket_handle: i64, | ||
| decoder: http1::Decoder, | ||
| input: Vec<u8>, | ||
| building: Option<Building>, | ||
|
|
@@ -143,6 +151,28 @@ pub(crate) fn take_aborted() -> Vec<i64> { | |
| std::mem::take(&mut *queue) | ||
| } | ||
|
|
||
| /// Connection-socket handles (`alloc_connection_socket`) whose TCP connection | ||
| /// has fully closed. Same pattern as `aborted()`: the completion sink cannot | ||
| /// run JS, so the pump drains this and fires the socket's `'close'` | ||
| /// listeners on its own tick. | ||
| fn closed_sockets() -> &'static Mutex<Vec<i64>> { | ||
| static CLOSED: OnceLock<Mutex<Vec<i64>>> = OnceLock::new(); | ||
| CLOSED.get_or_init(|| Mutex::new(Vec::new())) | ||
| } | ||
|
|
||
| fn note_closed_socket(socket_handle: i64) { | ||
| closed_sockets() | ||
| .lock() | ||
| .unwrap_or_else(|e| e.into_inner()) | ||
| .push(socket_handle); | ||
| } | ||
|
|
||
| /// Take the connection-socket handles due a `'close'` emit. | ||
| pub(crate) fn take_closed_sockets() -> Vec<i64> { | ||
| let mut queue = closed_sockets().lock().unwrap_or_else(|e| e.into_inner()); | ||
| std::mem::take(&mut *queue) | ||
| } | ||
|
|
||
| /// Note that this connection's in-flight request (if any) will never be | ||
| /// answered, exactly once per request. | ||
| fn note_aborted(id: i64) { | ||
|
|
@@ -267,15 +297,19 @@ pub(crate) fn start_connection( | |
| } | ||
| } | ||
| let peer = tl::peer_address(conn_id); | ||
| let peer_address = peer.as_ref().map(|e| e.address.clone()).unwrap_or_default(); | ||
| let peer_port = peer.as_ref().map(|e| e.port).unwrap_or(0); | ||
| let keep_alive_timeout_ms = | ||
| with_base_server(server_handle, |s| s.keep_alive_timeout).unwrap_or(5_000.0); | ||
| let socket_handle = alloc_connection_socket(peer_address.clone(), peer_port); | ||
| conns().lock().unwrap_or_else(|e| e.into_inner()).insert( | ||
| conn_id, | ||
| Conn { | ||
| id: conn_id, | ||
| server_handle, | ||
| peer_address: peer.as_ref().map(|e| e.address.clone()).unwrap_or_default(), | ||
| peer_port: peer.as_ref().map(|e| e.port).unwrap_or(0), | ||
| peer_address, | ||
| peer_port, | ||
| socket_handle, | ||
| decoder: http1::Decoder::new(http1::Mode::Request, Default::default()), | ||
| input: Vec::with_capacity(8 * 1024), | ||
| building: None, | ||
|
|
@@ -293,7 +327,7 @@ pub(crate) fn start_connection( | |
| websocket: false, | ||
| }, | ||
| ); | ||
| crate::server::server::queue_turnloop_connection_event(server_handle); | ||
| crate::server::server::queue_turnloop_connection_event(server_handle, socket_handle); | ||
| arm_idle(conn_id); | ||
| if let Err(_err) = tl::read_start(conn_id) { | ||
| destroy_connection(conn_id); | ||
|
|
@@ -327,13 +361,19 @@ pub(crate) fn adopt_alpn_http1( | |
| with_base_server(server_handle, |s| s.keep_alive_timeout).unwrap_or(5_000.0); | ||
| let mut input = Vec::with_capacity(8 * 1024); | ||
| input.extend_from_slice(&leftover); | ||
| // Pre-existing gap, not this fix's scope: this ALPN handoff never queued | ||
| // `'connection'` (no `queue_turnloop_connection_event` call below) even | ||
| // before connection sockets existed. `req.socket` on it is still wired so a | ||
| // request landing here is consistent with the ordinary accept path. | ||
| let socket_handle = alloc_connection_socket(peer_address.clone(), peer_port); | ||
| conns().lock().unwrap_or_else(|e| e.into_inner()).insert( | ||
| id, | ||
| Conn { | ||
| id, | ||
| server_handle, | ||
| peer_address, | ||
| peer_port, | ||
| socket_handle, | ||
| decoder: http1::Decoder::new(http1::Mode::Request, Default::default()), | ||
| input, | ||
| building: None, | ||
|
|
@@ -621,6 +661,10 @@ fn finish_request(c: &mut Conn, building: Building) -> (HttpPendingRequest, bool | |
| "1.1".to_string() | ||
| }; | ||
| let im_handle = alloc_incoming_message(im); | ||
| // `req.socket === conn` for the | ||
| // `'connection'` listener's argument — same handle, every request on | ||
| // this connection. | ||
| incoming_socket_assign(im_handle, handle_to_pointer_f64(c.socket_handle)); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Assign the connection socket to upgrade requests. When a request takes 🤖 Prompt for AI Agents |
||
| let sr_handle = alloc_server_response_for_turnloop(c.id, c.seq, im_handle); | ||
|
|
||
| let is_check_continue = building.expects_continue | ||
|
|
@@ -1057,11 +1101,21 @@ fn on_closed(id: i64) { | |
| // ever passing through `destroy_connection`. | ||
| note_aborted(id); | ||
| cancel_idle(id); | ||
| let owned = conns() | ||
| let removed = conns() | ||
| .lock() | ||
| .unwrap_or_else(|e| e.into_inner()) | ||
| .remove(&id) | ||
| .is_some(); | ||
| .remove(&id); | ||
| let owned = removed.is_some(); | ||
| // The connection is fully gone — queue its | ||
| // socket's `'close'` for the pump's next tick. This sink runs inside | ||
| // `dispatch_staged` and must not run JS (same rule as `note_aborted` | ||
| // above), so `js_node_http_server_process_pending` (via | ||
| // `take_closed_sockets`) fires the listeners instead. | ||
| if let Some(conn) = removed { | ||
| if conn.socket_handle != 0 { | ||
| note_closed_socket(conn.socket_handle); | ||
|
Comment on lines
+1114
to
+1116
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift Preserve socket-close delivery after a raw upgrade. When 🤖 Prompt for AI Agents🩺 Stability & Availability | 🟠 Major | ⚡ Quick win 🔎 Supported by static analysis🤖 get_repo_knowledge executed:
Length of output: 14465 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- target diff ---'
git diff --no-ext-diff --unified=80 182e375978a8bfed26ce382e55ee7273ee104ad3 3be2e68f11111942a7a9a6a9936e9391fe6e1811 -- crates/perry-ext-http/src/server/turnloop_serve/conn.rs
printf '%s\n' '--- relevant files ---'
rg -l -g '*.rs' 'alloc_connection_socket|take_closed_sockets|drop_handle|note_closed_socket|socket_handle|H2Conn' crates | sort
printf '%s\n' '--- target definitions and call sites ---'
rg -n -C 8 'alloc_connection_socket|take_closed_sockets|drop_handle|note_closed_socket|socket_handle|struct H2Conn|impl H2Conn' crates/perry-ext-http crates/perry-runtime crates 2>/dev/null | head -n 1200
printf '%s\n' '--- registry-related definitions ---'
rg -n -C 12 'struct .*Registry|Registry|handles|drop_handle|alloc_handle|register.*socket|socket.*handle' crates | head -n 1200Repository: PerryTS/perry Length of output: 41861 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- exact relevant symbols ---'
rg -n -C 16 'alloc_connection_socket|take_closed_sockets|drop_handle|queue_turnloop_connection_event|js_node_http_server_process_pending|socket_handle' crates/perry-ext-http crates/perry-runtime crates/perry-stdlib
printf '%s\n' '--- files defining socket allocation or handle dropping ---'
rg -l 'alloc_connection_socket|drop_handle|take_closed_sockets|queue_turnloop_connection_event' crates/perry-ext-http crates/perry-runtime crates/perry-stdlib | sort
printf '%s\n' '--- HTTP/2 handle allocation context ---'
rg -n -C 30 'alloc_connection_socket|H2Conn|struct H2' crates/perry-ext-http/src/serverRepository: PerryTS/perry Length of output: 45659 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- defining files ---'
for pattern in 'fn alloc_connection_socket' 'take_closed_sockets' 'fn drop_handle' 'queue_turnloop_connection_event' 'fn js_node_http_server_process_pending' 'alloc_connection_socket('; do
printf '\nPATTERN: %s\n' "$pattern"
rg -l "$pattern" crates/perry-ext-http crates/perry-runtime crates/perry-stdlib | sort
done
printf '%s\n' '--- allocator and request socket definitions ---'
rg -n -C 30 'fn alloc_connection_socket|pub.*alloc_connection_socket|incoming_socket_assign|handle_to_pointer_f64' crates/perry-ext-http/src
printf '%s\n' '--- closed-socket consumer and pump ---'
rg -n -C 40 'take_closed_sockets|js_node_http_server_process_pending|queue_turnloop_connection_event' crates/perry-ext-http/src
printf '%s\n' '--- handle registry primitives ---'
rg -n -C 25 'pub.*fn drop_handle|fn drop_handle|pub.*fn register_handle|fn register_handle|struct HandleRegistry|HANDLE_REGISTRY|quarantine' crates/perry-runtime/src crates/perry-stdlib/src
printf '%s\n' '--- HTTP/2 connection allocation ---'
rg -n -C 40 'alloc_connection_socket|socket_handle|struct H2Conn|H2Conn' crates/perry-ext-http/src/server/turnloop_h2Repository: PerryTS/perry Length of output: 45373 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- line locations ---'
rg -n 'alloc_connection_socket|incoming_socket_assign|handle_to_pointer_f64' crates/perry-ext-http/src/server/request.rs
rg -n 'take_closed_sockets|take_aborted|process_pending|queue_turnloop_connection_event' crates/perry-ext-http/src/server/server.rs crates/perry-ext-http/src/server/turnloop_serve/mod.rs
rg -n 'pub.*fn drop_handle|fn drop_handle|pub.*fn register_handle|fn register_handle|struct HandleRegistry|quarantine' crates/perry-stdlib/src/common/handle.rs
rg -n 'struct H2Conn|H2Conn \\{|session_handle|alloc_connection_socket|destroy_connection|fn on_closed|fn graceful_close' crates/perry-ext-http/src/server/turnloop_h2/conn.rs
printf '%s\n' '--- request allocator ---'
sed -n '1,260p' crates/perry-ext-http/src/server/request.rs
printf '%s\n' '--- server closed-socket pump references ---'
sed -n '1,260p' crates/perry-ext-http/src/server/server.rs
rg -n -C 25 'take_closed_sockets|take_aborted|process_pending|queue_turnloop_connection_event' crates/perry-ext-http/src/server/server.rs crates/perry-ext-http/src/server/turnloop_serve/mod.rs
printf '%s\n' '--- handle registry ---'
sed -n '1,280p' crates/perry-stdlib/src/common/handle.rsRepository: PerryTS/perry Length of output: 3579 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- request socket allocator ---'
sed -n '880,1045p' crates/perry-ext-http/src/server/request.rs
printf '%s\n' '--- closed-socket pump ---'
sed -n '1060,1165p' crates/perry-ext-http/src/server/server.rs
sed -n '1,100p' crates/perry-ext-http/src/server/turnloop_serve/mod.rs
printf '%s\n' '--- handle registry ---'
sed -n '1,220p' crates/perry-stdlib/src/common/handle.rs
printf '%s\n' '--- HTTP/2 lifecycle locations ---'
rg -n 'struct H2Conn|H2Conn \{|session_handle|alloc_connection_socket|destroy_connection|on_closed|graceful_close' crates/perry-ext-http/src/server/turnloop_h2/conn.rs
printf '%s\n' '--- HTTP/2 constructors and terminal paths ---'
sed -n '100,260p' crates/perry-ext-http/src/server/turnloop_h2/conn.rs
sed -n '1180,1375p' crates/perry-ext-http/src/server/turnloop_h2/conn.rsRepository: PerryTS/perry Length of output: 41061 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- HTTP extension handle destruction ---'
rg -n -C 8 'drop_handle|take_handle|iter_handle_ids_of::<IncomingMessage>|IncomingMessage' crates/perry-ext-http/src/server crates/perry-ext-http/src | grep -E 'drop_handle|take_handle|iter_handle_ids_of|IncomingMessage' | head -n 500
printf '%s\n' '--- HTTP/2 socket allocation block ---'
sed -n '420,535p' crates/perry-ext-http/src/server/turnloop_h2/conn.rs
printf '%s\n' '--- HTTP/2 connection record fields and insert ---'
sed -n '630,690p' crates/perry-ext-http/src/server/turnloop_h2/conn.rs
printf '%s\n' '--- all direct socket-handle destruction references ---'
rg -n 'drop_handle\s*\(|take_handle\s*\(|close_incoming_message\s*\(' crates/perry-ext-http/src crates/perry-stdlib/src | head -n 500Repository: PerryTS/perry Length of output: 41955 Release connection-socket handles after the close event.
Add an explicit owner for each connection-socket handle. Drop the handle after its close listeners run, and apply the same cleanup to HTTP/2 on every terminal path, including 🤖 Prompt for AI Agents |
||
| } | ||
| } | ||
| crate::server::server::turnloop_connection_closed(id); | ||
| if owned { | ||
| // The rustls session has to go BEFORE the id does. `turnloop_tls_io` | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Dispatch connection events to
Http2SecureServer.When the HTTP/2 accept path queues its
Http2SecureServerhandle, this loop finds listeners only onHttpServerorHttpsServer. It therefore discards the queued event without calling the HTTP/2 server’s'connection'listeners. IncludeHttp2SecureServerand itsbasein the listener lookup before testing whether the list is empty.🤖 Prompt for AI Agents