From 07744d00796a8ab9598059a83158b62ec18607ca Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sat, 26 Apr 2025 15:56:10 +0200 Subject: [PATCH] Initial working request function in net --- Cargo.lock | 22 ++++ crates/lune-std-net/Cargo.toml | 3 +- crates/lune-std-net/src/client/mod.rs | 4 +- crates/lune-std-net/src/client/request.rs | 115 ++++++++++++++++++ crates/lune-std-net/src/client/stream.rs | 2 +- crates/lune-std-net/src/lib.rs | 10 +- .../src/{client => shared}/hyper.rs | 20 ++- crates/lune-std-net/src/shared/mod.rs | 1 + 8 files changed, 169 insertions(+), 8 deletions(-) create mode 100644 crates/lune-std-net/src/client/request.rs rename crates/lune-std-net/src/{client => shared}/hyper.rs (88%) create mode 100644 crates/lune-std-net/src/shared/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 3deaa64..4ca0162 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1336,6 +1336,19 @@ dependencies = [ "http 1.3.1", ] +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http 1.3.1", + "http-body 1.0.1", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.10.1" @@ -1379,9 +1392,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", + "futures-channel", + "futures-util", "http 1.3.1", "http-body 1.0.1", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", "tokio", + "want", ] [[package]] @@ -1839,6 +1860,7 @@ dependencies = [ "bstr", "futures-lite", "futures-rustls", + "http-body-util", "hyper 1.6.0", "lune-std-serde", "lune-utils", diff --git a/crates/lune-std-net/Cargo.toml b/crates/lune-std-net/Cargo.toml index de8b421..65d74ae 100644 --- a/crates/lune-std-net/Cargo.toml +++ b/crates/lune-std-net/Cargo.toml @@ -24,7 +24,8 @@ blocking = "1.6" bstr = "1.9" futures-lite = "2.6" futures-rustls = "0.26" -hyper = "1.6" +http-body-util = "0.1" +hyper = { version = "1.6", features = ["http1", "client", "server"] } pin-project-lite = "0.2" rustls = "0.23" rustls-pki-types = "1.11" diff --git a/crates/lune-std-net/src/client/mod.rs b/crates/lune-std-net/src/client/mod.rs index 5ae1a78..5b79ed0 100644 --- a/crates/lune-std-net/src/client/mod.rs +++ b/crates/lune-std-net/src/client/mod.rs @@ -1,2 +1,4 @@ -mod hyper; +mod request; mod stream; + +pub use self::request::{Request, Response}; diff --git a/crates/lune-std-net/src/client/request.rs b/crates/lune-std-net/src/client/request.rs new file mode 100644 index 0000000..39597b8 --- /dev/null +++ b/crates/lune-std-net/src/client/request.rs @@ -0,0 +1,115 @@ +use bstr::BString; +use futures_lite::prelude::*; +use http_body_util::{BodyStream, Full}; +use hyper::{ + body::{Bytes, Incoming}, + client::conn::http1::handshake, + Method, Request as HyperRequest, Response as HyperResponse, +}; + +use mlua::prelude::*; + +use crate::{ + client::stream::HttpRequestStream, + shared::hyper::{HyperExecutor, HyperIo}, +}; + +#[derive(Debug, Clone)] +pub struct Request { + inner: HyperRequest>, +} + +impl Request { + pub async fn send(self, lua: Lua) -> LuaResult { + let stream = HttpRequestStream::connect(self.inner.uri()).await?; + + let (mut sender, conn) = handshake(HyperIo::from(stream)) + .await + .map_err(LuaError::external)?; + + HyperExecutor::execute(lua, conn); + + let incoming = sender + .send_request(self.inner) + .await + .map_err(LuaError::external)?; + + Response::from_incoming(incoming).await + } +} + +impl FromLua for Request { + fn from_lua(value: LuaValue, _lua: &Lua) -> LuaResult { + if let LuaValue::String(s) = value { + // We got a string, assume it's a URL + GET method + let uri = s.to_str()?; + Ok(Self { + inner: HyperRequest::builder() + .uri(uri.as_ref()) + .body(Full::new(Bytes::new())) + .into_lua_err()?, + }) + } else if let LuaValue::Table(t) = value { + // URL is always required with table options + let url = t.get::("url")?; + let builder = HyperRequest::builder().uri(url); + + // Add method, if provided + let builder = match t.get::>("method") { + Ok(Some(method)) => builder.method(method.as_str()), + Ok(None) => builder.method(Method::GET), + Err(e) => return Err(e), + }; + + // Add body, if provided + let builder = match t.get::>("body") { + Ok(Some(body)) => builder.body(Full::new(body.to_vec().into())), + Ok(None) => builder.body(Full::new(Bytes::new())), + Err(e) => return Err(e), + }; + + Ok(Self { + inner: builder.into_lua_err()?, + }) + } else { + Err(LuaError::FromLuaConversionError { + from: value.type_name(), + to: String::from("HttpRequest"), + message: Some(String::from("HttpRequest must be a string or table")), + }) + } + } +} + +#[derive(Debug, Clone)] +pub struct Response { + inner: HyperResponse>, +} + +impl Response { + pub async fn from_incoming(incoming: HyperResponse) -> LuaResult { + let (parts, body) = incoming.into_parts(); + + let body = BodyStream::new(body) + .try_fold(Vec::::new(), |mut body, chunk| { + if let Some(chunk) = chunk.data_ref() { + body.extend_from_slice(chunk); + } + Ok(body) + }) + .await + .into_lua_err()?; + + let bytes = Full::new(Bytes::from(body)); + let inner = HyperResponse::from_parts(parts, bytes); + + Ok(Self { inner }) + } +} + +impl LuaUserData for Response { + fn add_fields>(fields: &mut F) { + fields.add_field_method_get("ok", |_, this| Ok(this.inner.status().is_success())); + fields.add_field_method_get("status", |_, this| Ok(this.inner.status().as_u16())); + } +} diff --git a/crates/lune-std-net/src/client/stream.rs b/crates/lune-std-net/src/client/stream.rs index 79ff928..2d392cf 100644 --- a/crates/lune-std-net/src/client/stream.rs +++ b/crates/lune-std-net/src/client/stream.rs @@ -27,7 +27,7 @@ pub enum HttpRequestStream { } impl HttpRequestStream { - pub async fn connect(url: Uri) -> Result { + pub async fn connect(url: &Uri) -> Result { let Some(host) = url.host() else { return Err(make_err("unknown or missing host")); }; diff --git a/crates/lune-std-net/src/lib.rs b/crates/lune-std-net/src/lib.rs index 809e543..f39765f 100644 --- a/crates/lune-std-net/src/lib.rs +++ b/crates/lune-std-net/src/lib.rs @@ -8,6 +8,10 @@ mod client; mod server; mod url; +use self::client::{Request, Response}; + +pub(crate) mod shared; + const TYPEDEFS: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/types.d.luau")); /** @@ -27,10 +31,14 @@ pub fn typedefs() -> String { */ pub fn module(lua: Lua) -> LuaResult { TableBuilder::new(lua)? - // .with_async_function("request", net_request)? + .with_async_function("request", net_request)? // .with_async_function("socket", net_socket)? // .with_async_function("serve", net_serve)? // .with_function("urlEncode", net_url_encode)? // .with_function("urlDecode", net_url_decode)? .build_readonly() } + +async fn net_request(lua: Lua, req: Request) -> LuaResult { + req.send(lua).await +} diff --git a/crates/lune-std-net/src/client/hyper.rs b/crates/lune-std-net/src/shared/hyper.rs similarity index 88% rename from crates/lune-std-net/src/client/hyper.rs rename to crates/lune-std-net/src/shared/hyper.rs index 9088f5a..2dc1ea8 100644 --- a/crates/lune-std-net/src/client/hyper.rs +++ b/crates/lune-std-net/src/shared/hyper.rs @@ -9,7 +9,7 @@ use std::{ use async_io::Timer; use futures_lite::prelude::*; -use hyper::rt::{self, ReadBufCursor}; +use hyper::rt::{self, Executor, ReadBufCursor}; use mlua::prelude::*; use mlua_luau_scheduler::LuaSpawnExt; @@ -20,9 +20,21 @@ pub struct HyperExecutor { lua: Lua, } -impl From for HyperExecutor { - fn from(lua: Lua) -> Self { - Self { lua } +#[allow(dead_code)] +impl HyperExecutor { + pub fn execute(lua: Lua, fut: Fut) + where + Fut: Future + Send + 'static, + Fut::Output: Send + 'static, + { + let exec = if let Some(exec) = lua.app_data_ref::() { + exec + } else { + lua.set_app_data(Self { lua: lua.clone() }); + lua.app_data_ref::().unwrap() + }; + + exec.execute(fut); } } diff --git a/crates/lune-std-net/src/shared/mod.rs b/crates/lune-std-net/src/shared/mod.rs new file mode 100644 index 0000000..4342b3c --- /dev/null +++ b/crates/lune-std-net/src/shared/mod.rs @@ -0,0 +1 @@ +pub mod hyper;