diff --git a/Cargo.lock b/Cargo.lock index 0036a1f..d0f0974 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -396,7 +396,7 @@ dependencies = [ [[package]] name = "http-handler" version = "1.0.0" -source = "git+https://github.com/platformatic/http-handler#3e685693556f63145ed122d0a2faabc81da1ae40" +source = "git+https://github.com/platformatic/http-handler#4797b91d6f0a3f2b09d22b3b28ef30bf755596e3" dependencies = [ "bytes", "futures-core", @@ -413,7 +413,7 @@ dependencies = [ [[package]] name = "http-rewriter" version = "1.0.0" -source = "git+https://github.com/platformatic/http-rewriter#94047c9863803e4b068fc92091a9515cc8432207" +source = "git+https://github.com/platformatic/http-rewriter#632f1fb76b732e4f9c4a1c373940376c3a897213" dependencies = [ "bytes", "http", @@ -524,9 +524,9 @@ dependencies = [ [[package]] name = "napi" -version = "3.6.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3af30fe8e799dda3a555c496c59e960e4cff1e931b63acbaf3a3b25d9fad22b6" +checksum = "f27a163b545fd2184d2efdccf3d3df56acdb63465f2fcfebcaee0463c1e91783" dependencies = [ "bitflags", "ctor", @@ -670,9 +670,9 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.26.0" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba0117f4212101ee6544044dae45abe1083d30ce7b29c4b5cbdfa2354e07383" +checksum = "ab53c047fcd1a1d2a8820fe84f05d6be69e9526be40cb03b73f86b6b03e6d87d" dependencies = [ "indoc", "libc", @@ -687,9 +687,9 @@ dependencies = [ [[package]] name = "pyo3-async-runtimes" -version = "0.26.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ee6d4cb3e8d5b925f5cdb38da183e0ff18122eb2048d4041c9e7034d026e23" +checksum = "57ddb5b570751e93cc6777e81fee8087e59cd53b5043292f2a6d59d5bd80fdfd" dependencies = [ "futures", "once_cell", @@ -700,18 +700,18 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.26.0" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fc6ddaf24947d12a9aa31ac65431fb1b851b8f4365426e182901eabfb87df5f" +checksum = "b455933107de8642b4487ed26d912c2d899dec6114884214a0b3bb3be9261ea6" dependencies = [ "target-lexicon", ] [[package]] name = "pyo3-ffi" -version = "0.26.0" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "025474d3928738efb38ac36d4744a74a400c901c7596199e20e45d98eb194105" +checksum = "1c85c9cbfaddf651b1221594209aed57e9e5cff63c4d11d1feead529b872a089" dependencies = [ "libc", "pyo3-build-config", @@ -719,9 +719,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.26.0" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e64eb489f22fe1c95911b77c44cc41e7c19f3082fc81cce90f657cdc42ffded" +checksum = "0a5b10c9bf9888125d917fb4d2ca2d25c8df94c7ab5a52e13313a07e050a3b02" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -731,9 +731,9 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.26.0" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "100246c0ecf400b475341b8455a9213344569af29a3c841d29270e53102e0fcf" +checksum = "03b51720d314836e53327f5871d4c0cfb4fb37cc2c4a11cc71907a86342c40f9" dependencies = [ "heck", "proc-macro2", @@ -873,9 +873,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "slab" diff --git a/Cargo.toml b/Cargo.toml index ed6fb40..a44f1cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,8 +33,8 @@ http-rewriter = { git = "https://github.com/platformatic/http-rewriter" } # Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix napi = { version = "3", default-features = false, features = ["napi4", "tokio_rt", "async"], optional = true } napi-derive = { version = "3", optional = true } -pyo3 = { version = "0.26.0", features = ["experimental-async"] } -pyo3-async-runtimes = { version = "0.26.0", features = ["tokio-runtime"] } +pyo3 = { version = "0.27.2", features = ["experimental-async"] } +pyo3-async-runtimes = { version = "0.27.0", features = ["tokio-runtime"] } thiserror = "2.0.12" tokio = { version = "1.45.1", features = ["full"] } libc = "0.2" diff --git a/package.json b/package.json index b26b75a..b9ff32a 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "version": "napi version" }, "optionalDependencies": { - "@platformatic/python-node-darwin-arm64": "^0.1.4", - "@platformatic/python-node-linux-x64-gnu": "^0.1.4" + "@platformatic/python-node-darwin-arm64": "^2.0.0", + "@platformatic/python-node-linux-x64-gnu": "^2.0.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3bfd0cf..0f34c32 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,11 +19,11 @@ importers: version: 0.18.1 optionalDependencies: '@platformatic/python-node-darwin-arm64': - specifier: ^0.1.4 - version: 0.1.10 + specifier: ^2.0.0 + version: 2.0.0 '@platformatic/python-node-linux-x64-gnu': - specifier: ^0.1.4 - version: 0.1.10 + specifier: ^2.0.0 + version: 2.0.0 packages: @@ -683,14 +683,14 @@ packages: cpu: [x64] os: [win32] - '@platformatic/python-node-darwin-arm64@0.1.10': - resolution: {integrity: sha512-FbvvFc1DcGn9MbiyPJ2zsAYzuESMiO49443wI383Liebb/HaIO32Y+E7JsM5tmf6A2larvRWoO4HprtjbZSUmA==} + '@platformatic/python-node-darwin-arm64@2.0.0': + resolution: {integrity: sha512-o1Dd+wDFWESbtvBhIxVEg0dAHbMjKbBifsZM/lPwNczCdNrSD/dtdDEyb6MsD22Fl93w9IVdv2MeztGak2LtMQ==} engines: {node: '>= 20'} cpu: [arm64] os: [darwin] - '@platformatic/python-node-linux-x64-gnu@0.1.10': - resolution: {integrity: sha512-raqvdM6W74qa/m6qjhr2OV0dPyt5VVmFD/e/X/wZllR3iDtpO/D7c5VQJ4HsY166VmYA7ffOGBoNh8luV1Gdng==} + '@platformatic/python-node-linux-x64-gnu@2.0.0': + resolution: {integrity: sha512-HLo6bgF5sC8nGAoBQbtqRWvE0QBNZtzsQ6+nMMX8+SgH6rxdUZGSH4W9Pt/22+9rzT/WcxmATv/uddACW86akw==} engines: {node: '>= 20'} cpu: [x64] os: [linux] @@ -1332,10 +1332,10 @@ snapshots: '@oxlint/win32-x64@0.18.1': optional: true - '@platformatic/python-node-darwin-arm64@0.1.10': + '@platformatic/python-node-darwin-arm64@2.0.0': optional: true - '@platformatic/python-node-linux-x64-gnu@0.1.10': + '@platformatic/python-node-linux-x64-gnu@2.0.0': optional: true '@tybys/wasm-util@0.10.1': diff --git a/scripts/update-version.mjs b/scripts/update-version.mjs index 69df885..b7bfa99 100644 --- a/scripts/update-version.mjs +++ b/scripts/update-version.mjs @@ -3,4 +3,10 @@ import { readFile, writeFile } from 'fs/promises' const version = process.argv[2].replace(/^v/, '') const packageJson = JSON.parse(await readFile('package.json', 'utf8')) packageJson.version = version +// Update platform-specific deps to match release version +for (const dep of Object.keys(packageJson.optionalDependencies)) { + if (dep.startsWith(packageJson.name)) { + packageJson.optionalDependencies[dep] = `^${version}` + } +} await writeFile('package.json', JSON.stringify(packageJson, null, 2)) diff --git a/src/asgi/http.rs b/src/asgi/http.rs index 121fe6c..f2509f0 100644 --- a/src/asgi/http.rs +++ b/src/asgi/http.rs @@ -1,4 +1,5 @@ use http_handler::{Request, RequestExt, Version}; +use pyo3::Borrowed; use pyo3::exceptions::PyValueError; use pyo3::prelude::*; use pyo3::types::{PyAny, PyDict}; @@ -295,9 +296,11 @@ pub enum HttpSendMessage { }, } -impl<'py> FromPyObject<'py> for HttpSendMessage { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { - let dict = ob.downcast::()?; +impl<'a, 'py> FromPyObject<'a, 'py> for HttpSendMessage { + type Error = PyErr; + + fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult { + let dict = ob.cast::()?; let message_type = dict .get_item("type")? .ok_or_else(|| PyValueError::new_err("Missing 'type' key in HTTP send message dictionary"))?; @@ -318,22 +321,22 @@ impl<'py> FromPyObject<'py> for HttpSendMessage { // Convert headers from list of lists to vec of tuples let mut headers: Vec<(String, String)> = Vec::new(); - if let Ok(headers_list) = headers_py.downcast::() { + if let Ok(headers_list) = headers_py.cast::() { for item in headers_list.iter() { - if let Ok(header_pair) = item.downcast::() + if let Ok(header_pair) = item.cast::() && header_pair.len() == 2 { let name = header_pair.get_item(0)?; let value = header_pair.get_item(1)?; // Convert bytes to string - let name_str = if let Ok(bytes) = name.downcast::() { + let name_str = if let Ok(bytes) = name.cast::() { String::from_utf8_lossy(bytes.as_bytes()).to_string() } else { name.extract::()? }; - let value_str = if let Ok(bytes) = value.downcast::() { + let value_str = if let Ok(bytes) = value.cast::() { String::from_utf8_lossy(bytes.as_bytes()).to_string() } else { value.extract::()? diff --git a/src/asgi/http_method.rs b/src/asgi/http_method.rs index 35166bb..7526c90 100644 --- a/src/asgi/http_method.rs +++ b/src/asgi/http_method.rs @@ -1,6 +1,7 @@ use std::convert::Infallible; use std::str::FromStr; +use pyo3::Borrowed; use pyo3::exceptions::PyValueError; use pyo3::prelude::*; use pyo3::types::PyString; @@ -20,8 +21,10 @@ pub enum HttpMethod { Connect, } -impl<'py> FromPyObject<'py> for HttpMethod { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { +impl<'a, 'py> FromPyObject<'a, 'py> for HttpMethod { + type Error = PyErr; + + fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult { let method: String = ob.extract()?; method .to_uppercase() diff --git a/src/asgi/http_version.rs b/src/asgi/http_version.rs index d5302a1..c3d0d4c 100644 --- a/src/asgi/http_version.rs +++ b/src/asgi/http_version.rs @@ -1,6 +1,7 @@ use std::convert::Infallible; use std::str::FromStr; +use pyo3::Borrowed; use pyo3::exceptions::PyValueError; use pyo3::prelude::*; use pyo3::types::PyString; @@ -14,8 +15,10 @@ pub enum HttpVersion { V2_0, } -impl<'py> FromPyObject<'py> for HttpVersion { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { +impl<'a, 'py> FromPyObject<'a, 'py> for HttpVersion { + type Error = PyErr; + + fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult { let version: String = ob.extract()?; match version.as_str() { "1" | "1.0" => Ok(HttpVersion::V1_0), diff --git a/src/asgi/info.rs b/src/asgi/info.rs index de07f6b..98d02c7 100644 --- a/src/asgi/info.rs +++ b/src/asgi/info.rs @@ -1,3 +1,4 @@ +use pyo3::Borrowed; use pyo3::exceptions::PyValueError; use pyo3::prelude::*; use pyo3::types::PyDict; @@ -35,9 +36,11 @@ impl<'py> IntoPyObject<'py> for AsgiInfo { } } -impl<'py> FromPyObject<'py> for AsgiInfo { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { - let dict = ob.downcast::()?; +impl<'a, 'py> FromPyObject<'a, 'py> for AsgiInfo { + type Error = PyErr; + + fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult { + let dict = ob.cast::()?; let version: String = dict .get_item("version")? .ok_or_else(|| PyValueError::new_err("Missing 'version' key in ASGI info dictionary"))? diff --git a/src/asgi/lifespan.rs b/src/asgi/lifespan.rs index fd61497..d8a1781 100644 --- a/src/asgi/lifespan.rs +++ b/src/asgi/lifespan.rs @@ -1,3 +1,4 @@ +use pyo3::Borrowed; use pyo3::exceptions::PyValueError; use pyo3::prelude::*; use pyo3::types::PyDict; @@ -67,9 +68,11 @@ pub enum LifespanSendMessage { } // Only ever converted from Python to Rust. -impl<'py> FromPyObject<'py> for LifespanSendMessage { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { - let dict = ob.downcast::()?; +impl<'a, 'py> FromPyObject<'a, 'py> for LifespanSendMessage { + type Error = PyErr; + + fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult { + let dict = ob.cast::()?; let message_type = dict.get_item("type")?.ok_or_else(|| { PyValueError::new_err("Missing 'type' key in Lifespan send message dictionary") })?; diff --git a/src/asgi/mod.rs b/src/asgi/mod.rs index 1c1699e..5a5af4e 100644 --- a/src/asgi/mod.rs +++ b/src/asgi/mod.rs @@ -134,12 +134,7 @@ async fn forward_http_request( { const BUFFER_SIZE: usize = 64 * 1024; // 64KB buffer let mut buffer = BytesMut::with_capacity(BUFFER_SIZE); - loop { - let n = match request_stream.read_buf(&mut buffer).await { - Ok(n) => n, - Err(_) => break, - }; - + while let Ok(n) = request_stream.read_buf(&mut buffer).await { if n == 0 { // EOF - send final message let _ = rx.send(HttpReceiveMessage::Request { @@ -271,11 +266,9 @@ where } // Write body data if not empty - if !body.is_empty() { - if response_stream.write_all(&body).await.is_err() { - // Client disconnected - return true; - } + if !body.is_empty() && response_stream.write_all(&body).await.is_err() { + // Client disconnected + return true; } // Check if this was the final chunk diff --git a/src/asgi/sender.rs b/src/asgi/sender.rs index 4be5946..bf3e2d4 100644 --- a/src/asgi/sender.rs +++ b/src/asgi/sender.rs @@ -61,7 +61,7 @@ impl Sender { let args_dict = args.bind(py); match &self.0 { SenderType::Http(tx) => { - let msg = HttpSendMessage::extract_bound(args_dict)?; + let msg: HttpSendMessage = args_dict.extract()?; tx.send(AcknowledgedMessage { message: msg, ack: ack_tx, @@ -70,7 +70,7 @@ impl Sender { Ok(()) } SenderType::WebSocket(tx) => { - let msg = WebSocketSendMessage::extract_bound(args_dict)?; + let msg: WebSocketSendMessage = args_dict.extract()?; tx.send(AcknowledgedMessage { message: msg, ack: ack_tx, @@ -79,7 +79,7 @@ impl Sender { Ok(()) } SenderType::Lifespan(tx) => { - let msg = LifespanSendMessage::extract_bound(args_dict)?; + let msg: LifespanSendMessage = args_dict.extract()?; tx.send(AcknowledgedMessage { message: msg, ack: ack_tx, diff --git a/src/asgi/websocket.rs b/src/asgi/websocket.rs index 3cc9db2..8dbd9a9 100644 --- a/src/asgi/websocket.rs +++ b/src/asgi/websocket.rs @@ -1,4 +1,5 @@ use http_handler::{Request, RequestExt, Version}; +use pyo3::Borrowed; use pyo3::exceptions::PyValueError; use pyo3::prelude::*; use pyo3::types::PyDict; @@ -313,9 +314,11 @@ pub enum WebSocketSendMessage { }, } -impl<'py> FromPyObject<'py> for WebSocketSendMessage { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { - let dict = ob.downcast::()?; +impl<'a, 'py> FromPyObject<'a, 'py> for WebSocketSendMessage { + type Error = PyErr; + + fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult { + let dict = ob.cast::()?; let message_type = dict.get_item("type")?.ok_or_else(|| { PyValueError::new_err("Missing 'type' key in WebSocket send message dictionary") })?;