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
73 changes: 73 additions & 0 deletions crates/perry-ext-http/src/client_request_surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,41 @@ fn dispatch_property(handle: Handle, property: &str) -> Option<f64> {
});
}
Some(match property {
"method" => {
with_handle_mut::<ClientRequestHandle, _, _>(handle, |req| string_value(&req.method))
.unwrap_or_else(undefined_value)
}
"protocol" => with_handle_mut::<ClientRequestHandle, _, _>(handle, |req| {
reqwest::Url::parse(&req.url)
.map(|u| string_value(&format!("{}:", u.scheme())))
.unwrap_or_else(|_| string_value(""))
})
.unwrap_or_else(undefined_value),
"host" => with_handle_mut::<ClientRequestHandle, _, _>(handle, |req| {
let host = reqwest::Url::parse(&req.url)
.ok()
.and_then(|u| u.host_str().map(|s| s.to_string()))
.unwrap_or_default();
string_value(&host)
})
.unwrap_or_else(undefined_value),
"path" => with_handle_mut::<ClientRequestHandle, _, _>(handle, |req| {
let path = reqwest::Url::parse(&req.url)
.map(|u| {
let mut path = u.path().to_string();
if path.is_empty() {
path.push('/');
}
if let Some(q) = u.query() {
path.push('?');
path.push_str(q);
}
path
})
.unwrap_or_default();
string_value(&path)
})
.unwrap_or_else(undefined_value),
"aborted" => js_http_client_request_aborted(handle),
"destroyed" => js_http_client_request_destroyed(handle),
"finished" => js_http_client_request_finished(handle),
Expand All @@ -358,6 +393,24 @@ fn dispatch_method(handle: Handle, method: &str, args: &[f64]) -> Option<f64> {
return None;
}
Some(match method {
"end" => {
unsafe {
client_request_end_impl(
handle,
args.first().copied().unwrap_or_else(undefined_value),
);
}
handle_value(handle)
}
"write" => {
unsafe {
client_request_write_impl(
handle,
args.first().copied().unwrap_or_else(undefined_value),
);
}
handle_value(handle)
}
"setHeader" => {
let name = string_arg(args, 0).unwrap_or_default();
let value = string_arg(args, 1).unwrap_or_default();
Expand All @@ -382,6 +435,26 @@ fn dispatch_method(handle: Handle, method: &str, args: &[f64]) -> Option<f64> {
"getHeaderNames" => headers_array(handle, false),
"getHeaders" => headers_object(handle),
"getRawHeaderNames" => headers_array(handle, true),
"setTimeout" => {
unsafe {
client_request_set_timeout_impl(handle, args.first().copied().unwrap_or(0.0));
}
handle_value(handle)
}
"listenerCount" => {
let event = string_arg(args, 0).unwrap_or_default();
get_handle_mut::<ClientRequestHandle>(handle)
.map(|req| {
let explicit = req.listeners.get(&event).map(|v| v.len()).unwrap_or(0);
let implicit_response = if event == "response" && req.response_callback != 0 {
1
} else {
0
};
(explicit + implicit_response) as f64
})
.unwrap_or(0.0)
}
"abort" => js_http_client_request_abort(handle),
"destroy" => handle_value(js_http_client_request_destroy(handle, undefined_value())),
"flushHeaders" | "cork" | "uncork" | "setNoDelay" | "setSocketKeepAlive" => {
Expand Down
41 changes: 33 additions & 8 deletions crates/perry-ext-http/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -612,10 +612,10 @@ fn dispatch_request(
let try_h = tokio::runtime::Handle::try_current();
std::hint::black_box(&try_h);
if try_h.is_err() {
eprintln!(
"[perry-ext-http] BUG: dispatch_request Handle::try_current returned Err — \
LTO has likely dead-stripped tokio's CONTEXT statics."
);
push_event(PendingHttpEvent::Error {
request_handle,
error_message: "http client runtime unavailable".to_string(),
});
return;
}
let handle = tokio::runtime::Handle::current();
Expand Down Expand Up @@ -786,10 +786,10 @@ fn dispatch_request_over_socket(
let try_h = tokio::runtime::Handle::try_current();
std::hint::black_box(&try_h);
if try_h.is_err() {
eprintln!(
"[perry-ext-http] BUG: dispatch_request_over_socket Handle::try_current returned \
Err — LTO has likely dead-stripped tokio's CONTEXT statics."
);
push_event(PendingHttpEvent::Error {
request_handle,
error_message: "http client runtime unavailable".to_string(),
});
return;
}
let handle = tokio::runtime::Handle::current();
Expand Down Expand Up @@ -1137,6 +1137,10 @@ pub unsafe extern "C" fn js_https_get_overload(args_array: i64) -> Handle {
/// `req.write(chunk)` — append data to the request body.
#[no_mangle]
pub unsafe extern "C" fn js_http_client_request_write(handle: Handle, body_f64: f64) -> Handle {
client_request_write_impl(handle, body_f64)
}

unsafe fn client_request_write_impl(handle: Handle, body_f64: f64) -> Handle {
if let Some(body) = extract_string_value(body_f64) {
with_handle_mut::<ClientRequestHandle, _, _>(handle, |req| {
req.body.extend_from_slice(body.as_bytes());
Expand All @@ -1150,6 +1154,10 @@ pub unsafe extern "C" fn js_http_client_request_write(handle: Handle, body_f64:
/// second call after `ended=true` is a no-op.
#[no_mangle]
pub unsafe extern "C" fn js_http_client_request_end(handle: Handle, body_f64: f64) -> Handle {
client_request_end_impl(handle, body_f64)
}

unsafe fn client_request_end_impl(handle: Handle, body_f64: f64) -> Handle {
if let Some(body) = extract_string_value(body_f64) {
with_handle_mut::<ClientRequestHandle, _, _>(handle, |req| {
req.body.extend_from_slice(body.as_bytes());
Expand Down Expand Up @@ -1242,6 +1250,10 @@ pub unsafe extern "C" fn js_http_on(
event_ptr: *const StringHeader,
callback: i64,
) -> Handle {
http_on_impl(handle, event_ptr, callback)
}

unsafe fn http_on_impl(handle: Handle, event_ptr: *const StringHeader, callback: i64) -> Handle {
ensure_gc_scanner_registered();
let event = match read_str(event_ptr) {
Some(e) => e,
Expand Down Expand Up @@ -1290,6 +1302,10 @@ pub unsafe extern "C" fn js_http_set_header(
/// `req.setTimeout(ms)`.
#[no_mangle]
pub unsafe extern "C" fn js_http_set_timeout(handle: Handle, ms: f64) -> Handle {
client_request_set_timeout_impl(handle, ms)
}

unsafe fn client_request_set_timeout_impl(handle: Handle, ms: f64) -> Handle {
with_handle_mut::<ClientRequestHandle, _, _>(handle, |req| {
req.timeout_ms = Some(ms.max(0.0) as u64);
});
Expand Down Expand Up @@ -1534,6 +1550,15 @@ fn body_chunk_value(body: &[u8], encoding: Option<&str>) -> f64 {
/// Number of pending events the main loop should drain.
#[no_mangle]
pub extern "C" fn js_http_has_pending() -> i32 {
let has_events = HTTP_PENDING_EVENTS
.lock()
.map(|q| !q.is_empty())
.unwrap_or(false);
if has_events {
unsafe {
js_http_process_pending();
}
}
HTTP_PENDING_EVENTS
.lock()
.map(|q| if q.is_empty() { 0 } else { 1 })
Expand Down
7 changes: 6 additions & 1 deletion crates/perry-hir/src/destructuring/var_decl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -626,9 +626,14 @@ pub(crate) fn lower_var_decl_with_destructuring(
_ => None,
};
if let Some(class_name) = class_name {
let class_module = if class_name == "ClientRequest" {
"http"
} else {
module_name
};
ctx.register_native_instance(
name.clone(),
module_name.to_string(),
class_module.to_string(),
class_name.to_string(),
);
}
Expand Down
1 change: 1 addition & 0 deletions crates/perry-hir/src/js_transform/local_natives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1323,6 +1323,7 @@ pub fn detect_native_instance_creation_with_context(
// factory call lived in (mirrors lower.rs:5517).
let owning_module = match (module.as_str(), method.as_str()) {
("tls", "connect") => "net".to_string(),
("https", "request" | "get") => "http".to_string(),
_ => module.clone(),
};
Some((owning_module, class_name.to_string()))
Expand Down
18 changes: 15 additions & 3 deletions crates/perry-hir/src/lower/module_decl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -739,9 +739,15 @@ pub(crate) fn lower_module_decl(
_ => None,
};
if let Some(class_name) = class_name {
let class_module =
if class_name == "ClientRequest" {
"http".to_string()
} else {
module_name_owned.clone()
};
ctx.register_native_instance(
name.clone(),
module_name_owned.clone(),
class_module.clone(),
class_name.to_string(),
);
// Also register as module-level native instance so it survives scope exits.
Expand All @@ -750,7 +756,7 @@ pub(crate) fn lower_module_decl(
// causing pool.query() inside functions to miss the Pool dispatch.
ctx.module_native_instances.push((
name.clone(),
module_name_owned,
class_module,
class_name.to_string(),
));
}
Expand Down Expand Up @@ -884,9 +890,15 @@ pub(crate) fn lower_module_decl(
_ => None,
};
if let Some(class_name) = class_name {
let class_module =
if class_name == "ClientRequest" {
"http"
} else {
module_name
};
ctx.register_native_instance(
name.clone(),
module_name.to_string(),
class_module.to_string(),
class_name.to_string(),
);
}
Expand Down
10 changes: 10 additions & 0 deletions crates/perry-stdlib/src/common/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,11 @@ pub unsafe extern "C" fn js_handle_method_dispatch(
return value;
}

#[cfg(feature = "http-client")]
if let Some(value) = unsafe { crate::http::dispatch_agent_method(handle, method_name, &args) } {
return value;
}

#[cfg(feature = "http-client")]
if let Some(value) = crate::http::dispatch_client_request_method(handle, method_name, &args) {
return value;
Expand Down Expand Up @@ -1599,6 +1604,11 @@ pub unsafe extern "C" fn js_handle_property_dispatch(
return value;
}

#[cfg(feature = "http-client")]
if let Some(value) = crate::http::dispatch_agent_property(handle, property_name) {
return value;
}

#[cfg(feature = "http-client")]
if let Some(value) = crate::http::dispatch_client_request_property(handle, property_name) {
return value;
Expand Down
10 changes: 9 additions & 1 deletion crates/perry-stdlib/src/common/dispatch_http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ pub(super) unsafe fn dispatch_client_request_method(
) -> Option<f64> {
if !matches!(
method_name,
"setHeader"
"end"
| "write"
| "setHeader"
| "setTimeout"
| "listenerCount"
| "getHeader"
| "hasHeader"
| "removeHeader"
Expand Down Expand Up @@ -65,6 +69,10 @@ pub(super) unsafe fn dispatch_client_request_property(
| "setHeader"
| "setTimeout"
| "listenerCount"
| "method"
| "protocol"
| "host"
| "path"
| "getHeader"
| "hasHeader"
| "removeHeader"
Expand Down
Loading