Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crates/perry-ext-http-server/src/dispatch_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ fn is_server_response_member(name: &str) -> bool {
| "writableEnded"
| "writableFinished"
| "finished"
| "destroyed"
| "writableCorked"
| "writableHighWaterMark"
| "writableLength"
Expand All @@ -235,6 +236,7 @@ fn is_server_response_member(name: &str) -> bool {
| "__get_writableEnded"
| "__get_writableFinished"
| "__get_finished"
| "__get_destroyed"
| "__get_sendDate"
| "__set_sendDate"
| "__get_strictContentLength"
Expand Down
28 changes: 26 additions & 2 deletions crates/perry-ext-http-server/src/handle_dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
//!
//! Issue #2153.

use perry_ffi::{alloc_string, get_handle, js_object_alloc_with_shape, JsValue, StringHeader};
use perry_ffi::{
alloc_string, get_handle, get_handle_mut, js_object_alloc_with_shape, JsValue, StringHeader,
};

use crate::http2_server::Http2SecureServer;
use crate::https_server::HttpsServer;
Expand Down Expand Up @@ -788,7 +790,16 @@ pub unsafe extern "C" fn js_ext_http_server_response_dispatch_method(
js_node_http_res_write_processing(handle);
undef
}
"destroy" => self_ref,
// #4975: `outgoingMessage.destroy()` flips the `destroyed` flag (read
// back by the `destroyed` getter) and returns `this`. A subsequent
// `write()` then errors its callback with `ERR_STREAM_DESTROYED`
// rather than buffering (see `js_node_http_res_write_with_cb`).
"destroy" => {
if let Some(sr) = get_handle_mut::<ServerResponse>(handle) {
sr.destroyed = true;
}
self_ref
}
"assignSocket" if !args.is_empty() => {
crate::response::js_node_http_res_assign_socket(handle, args[0]);
undef
Expand Down Expand Up @@ -834,6 +845,13 @@ pub unsafe extern "C" fn js_ext_http_server_response_dispatch_method(
"__get_writableEnded" => bool_value(js_node_http_res_writable_ended(handle) != 0),
"__get_writableFinished" => bool_value(js_node_http_res_writable_finished(handle) != 0),
"__get_finished" | "finished" => bool_value(js_node_http_res_finished(handle) != 0),
// #4975: `outgoingMessage.destroyed` getter, also reachable through the
// `__get_destroyed` codegen form.
"__get_destroyed" | "destroyed" => bool_value(
get_handle::<ServerResponse>(handle)
.map(|sr| sr.destroyed)
.unwrap_or(false),
),
"__get_sendDate" | "sendDate" => bool_value(js_node_http_res_send_date(handle) != 0),
"__set_sendDate" if !args.is_empty() => {
js_node_http_res_set_send_date(handle, args[0]);
Expand Down Expand Up @@ -952,6 +970,12 @@ pub unsafe extern "C" fn js_ext_http_server_response_dispatch_property(
"writableEnded" => bool_value(js_node_http_res_writable_ended(handle) != 0),
"writableFinished" => bool_value(js_node_http_res_writable_finished(handle) != 0),
"finished" => bool_value(js_node_http_res_finished(handle) != 0),
// #4975: `outgoingMessage.destroyed` — false until `destroy()`.
"destroyed" => bool_value(
get_handle::<ServerResponse>(handle)
.map(|sr| sr.destroyed)
.unwrap_or(false),
),
"writableCorked" => 0.0,
"writableHighWaterMark" => 65_536.0,
"writableLength" => get_handle::<ServerResponse>(handle)
Expand Down
23 changes: 23 additions & 0 deletions crates/perry-ext-http-server/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,11 @@ pub struct ServerResponse {
/// #4904: `res.write(chunk, cb)` callbacks, invoked in order when the
/// buffered body flushes on `.end()`.
pub pending_write_callbacks: Vec<i64>,
/// #4975: `outgoingMessage.destroy()` state. Node's `OutgoingMessage`
/// (and its `ServerResponse` subclass) exposes a `destroyed` getter that
/// flips `true` after `destroy()`, and a post-destroy `write(chunk, cb)`
/// invokes `cb` with an `ERR_STREAM_DESTROYED` error instead of buffering.
pub destroyed: bool,
}

/// Owned shape produced by `.end()` — the per-request oneshot channel
Expand Down Expand Up @@ -412,6 +417,7 @@ impl ServerResponse {
standalone_socket: f64::from_bits(TAG_UNDEFINED),
standalone_req_method: None,
pending_write_callbacks: Vec::new(),
destroyed: false,
}
}

Expand Down Expand Up @@ -1748,6 +1754,23 @@ pub extern "C" fn js_node_http_res_detach_socket(handle: i64, _socket: f64) {
/// flushes on `.end()`, preserving call order (#4904).
#[no_mangle]
pub extern "C" fn js_node_http_res_write_with_cb(handle: i64, chunk: f64, callback: i64) -> i32 {
// #4975: `write()` after `destroy()` must not buffer; Node invokes the
// callback with an `ERR_STREAM_DESTROYED` error and returns `false` (no
// `'error'` event is emitted, so an `on('error', …)` listener stays silent).
if get_handle::<ServerResponse>(handle)
.map(|sr| sr.destroyed)
.unwrap_or(false)
{
if callback != 0 {
let err = perry_ffi::error_value_with_code(
"Cannot call write after a stream was destroyed",
"ERR_STREAM_DESTROYED",
perry_ffi::ErrorKind::Error,
);
crate::http2_server::call1(callback, f64::from_bits(err.bits()));
}
return 0;
}
let bytes = jsvalue_to_body_bytes(chunk);
// Honor streaming mode (after `res.flushHeaders()` / a prior streamed
// `res.write`) exactly like `js_node_http_res_write`: the chunk must go down
Expand Down
1 change: 1 addition & 0 deletions crates/perry-stdlib/src/common/dispatch/method_dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,7 @@ pub unsafe extern "C" fn js_handle_method_dispatch(
| "__get_writableEnded"
| "__get_writableFinished"
| "__get_finished"
| "__get_destroyed"
| "__get_sendDate"
| "__set_sendDate"
| "__get_strictContentLength"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ pub unsafe extern "C" fn js_handle_property_dispatch(
| "writableEnded"
| "writableFinished"
| "finished"
| "destroyed"
| "writableCorked"
| "writableHighWaterMark"
| "writableLength"
Expand Down
Loading