mirror of
https://github.com/lune-org/lune.git
synced 2025-05-04 10:43:57 +01:00
Add back some old structs and utils for net requests + most tests now pass
This commit is contained in:
parent
07744d0079
commit
14197d9398
10 changed files with 426 additions and 129 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1869,6 +1869,7 @@ dependencies = [
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"rustls 0.23.26",
|
"rustls 0.23.26",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
|
"url",
|
||||||
"webpki",
|
"webpki",
|
||||||
"webpki-roots 0.26.8",
|
"webpki-roots 0.26.8",
|
||||||
]
|
]
|
||||||
|
|
|
@ -29,6 +29,7 @@ hyper = { version = "1.6", features = ["http1", "client", "server"] }
|
||||||
pin-project-lite = "0.2"
|
pin-project-lite = "0.2"
|
||||||
rustls = "0.23"
|
rustls = "0.23"
|
||||||
rustls-pki-types = "1.11"
|
rustls-pki-types = "1.11"
|
||||||
|
url = "2.5"
|
||||||
webpki = "0.22"
|
webpki = "0.22"
|
||||||
webpki-roots = "0.26"
|
webpki-roots = "0.26"
|
||||||
|
|
||||||
|
|
142
crates/lune-std-net/src/client/config.rs
Normal file
142
crates/lune-std-net/src/client/config.rs
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use bstr::{BString, ByteSlice};
|
||||||
|
use hyper::Method;
|
||||||
|
use mlua::prelude::*;
|
||||||
|
|
||||||
|
use crate::shared::headers::table_to_hash_map;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct RequestConfigOptions {
|
||||||
|
pub decompress: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RequestConfigOptions {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { decompress: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromLua for RequestConfigOptions {
|
||||||
|
fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
|
||||||
|
if let LuaValue::Nil = value {
|
||||||
|
// Nil means default options
|
||||||
|
Ok(Self::default())
|
||||||
|
} else if let LuaValue::Table(tab) = value {
|
||||||
|
// Table means custom options
|
||||||
|
let decompress = match tab.get::<Option<bool>>("decompress") {
|
||||||
|
Ok(decomp) => Ok(decomp.unwrap_or(true)),
|
||||||
|
Err(_) => Err(LuaError::RuntimeError(
|
||||||
|
"Invalid option value for 'decompress' in request config options".to_string(),
|
||||||
|
)),
|
||||||
|
}?;
|
||||||
|
Ok(Self { decompress })
|
||||||
|
} else {
|
||||||
|
// Anything else is invalid
|
||||||
|
Err(LuaError::FromLuaConversionError {
|
||||||
|
from: value.type_name(),
|
||||||
|
to: "RequestConfigOptions".to_string(),
|
||||||
|
message: Some(format!(
|
||||||
|
"Invalid request config options - expected table or nil, got {}",
|
||||||
|
value.type_name()
|
||||||
|
)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct RequestConfig {
|
||||||
|
pub url: String,
|
||||||
|
pub method: Method,
|
||||||
|
pub query: HashMap<String, Vec<String>>,
|
||||||
|
pub headers: HashMap<String, Vec<String>>,
|
||||||
|
pub body: Option<Vec<u8>>,
|
||||||
|
pub options: RequestConfigOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromLua for RequestConfig {
|
||||||
|
fn from_lua(value: LuaValue, lua: &Lua) -> LuaResult<Self> {
|
||||||
|
// If we just got a string we assume its a GET request to a given url
|
||||||
|
if let LuaValue::String(s) = value {
|
||||||
|
Ok(Self {
|
||||||
|
url: s.to_string_lossy().to_string(),
|
||||||
|
method: Method::GET,
|
||||||
|
query: HashMap::new(),
|
||||||
|
headers: HashMap::new(),
|
||||||
|
body: None,
|
||||||
|
options: RequestConfigOptions::default(),
|
||||||
|
})
|
||||||
|
} else if let LuaValue::Table(tab) = value {
|
||||||
|
// If we got a table we are able to configure the entire request
|
||||||
|
|
||||||
|
// Extract url
|
||||||
|
let url = match tab.get::<LuaString>("url") {
|
||||||
|
Ok(config_url) => Ok(config_url.to_string_lossy().to_string()),
|
||||||
|
Err(_) => Err(LuaError::runtime("Missing 'url' in request config")),
|
||||||
|
}?;
|
||||||
|
// Extract method
|
||||||
|
let method = match tab.get::<LuaString>("method") {
|
||||||
|
Ok(config_method) => config_method.to_string_lossy().trim().to_ascii_uppercase(),
|
||||||
|
Err(_) => "GET".to_string(),
|
||||||
|
};
|
||||||
|
// Extract query
|
||||||
|
let query = match tab.get::<LuaTable>("query") {
|
||||||
|
Ok(tab) => table_to_hash_map(tab, "query")?,
|
||||||
|
Err(_) => HashMap::new(),
|
||||||
|
};
|
||||||
|
// Extract headers
|
||||||
|
let headers = match tab.get::<LuaTable>("headers") {
|
||||||
|
Ok(tab) => table_to_hash_map(tab, "headers")?,
|
||||||
|
Err(_) => HashMap::new(),
|
||||||
|
};
|
||||||
|
// Extract body
|
||||||
|
let body = match tab.get::<BString>("body") {
|
||||||
|
Ok(config_body) => Some(config_body.as_bytes().to_owned()),
|
||||||
|
Err(_) => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert method string into proper enum
|
||||||
|
let method = method.trim().to_ascii_uppercase();
|
||||||
|
let method = match method.as_ref() {
|
||||||
|
"GET" => Ok(Method::GET),
|
||||||
|
"POST" => Ok(Method::POST),
|
||||||
|
"PUT" => Ok(Method::PUT),
|
||||||
|
"DELETE" => Ok(Method::DELETE),
|
||||||
|
"HEAD" => Ok(Method::HEAD),
|
||||||
|
"OPTIONS" => Ok(Method::OPTIONS),
|
||||||
|
"PATCH" => Ok(Method::PATCH),
|
||||||
|
_ => Err(LuaError::RuntimeError(format!(
|
||||||
|
"Invalid request config method '{}'",
|
||||||
|
&method
|
||||||
|
))),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
// Parse any extra options given
|
||||||
|
let options = match tab.get::<LuaValue>("options") {
|
||||||
|
Ok(opts) => RequestConfigOptions::from_lua(opts, lua)?,
|
||||||
|
Err(_) => RequestConfigOptions::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// All good, validated and we got what we need
|
||||||
|
Ok(Self {
|
||||||
|
url,
|
||||||
|
method,
|
||||||
|
query,
|
||||||
|
headers,
|
||||||
|
body,
|
||||||
|
options,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Anything else is invalid
|
||||||
|
Err(LuaError::FromLuaConversionError {
|
||||||
|
from: value.type_name(),
|
||||||
|
to: "RequestConfig".to_string(),
|
||||||
|
message: Some(format!(
|
||||||
|
"Invalid request config - expected string or table, got {}",
|
||||||
|
value.type_name()
|
||||||
|
)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,2 @@
|
||||||
mod request;
|
pub mod config;
|
||||||
mod stream;
|
pub mod stream;
|
||||||
|
|
||||||
pub use self::request::{Request, Response};
|
|
||||||
|
|
|
@ -1,115 +0,0 @@
|
||||||
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<Full<Bytes>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Request {
|
|
||||||
pub async fn send(self, lua: Lua) -> LuaResult<Response> {
|
|
||||||
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<Self> {
|
|
||||||
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::<String>("url")?;
|
|
||||||
let builder = HyperRequest::builder().uri(url);
|
|
||||||
|
|
||||||
// Add method, if provided
|
|
||||||
let builder = match t.get::<Option<String>>("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::<Option<BString>>("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<Full<Bytes>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Response {
|
|
||||||
pub async fn from_incoming(incoming: HyperResponse<Incoming>) -> LuaResult<Self> {
|
|
||||||
let (parts, body) = incoming.into_parts();
|
|
||||||
|
|
||||||
let body = BodyStream::new(body)
|
|
||||||
.try_fold(Vec::<u8>::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<F: LuaUserDataFields<Self>>(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()));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +1,15 @@
|
||||||
#![allow(clippy::cargo_common_metadata)]
|
#![allow(clippy::cargo_common_metadata)]
|
||||||
|
|
||||||
|
use lune_utils::TableBuilder;
|
||||||
use mlua::prelude::*;
|
use mlua::prelude::*;
|
||||||
|
|
||||||
use lune_utils::TableBuilder;
|
pub(crate) mod client;
|
||||||
|
pub(crate) mod server;
|
||||||
mod client;
|
|
||||||
mod server;
|
|
||||||
mod url;
|
|
||||||
|
|
||||||
use self::client::{Request, Response};
|
|
||||||
|
|
||||||
pub(crate) mod shared;
|
pub(crate) mod shared;
|
||||||
|
pub(crate) mod url;
|
||||||
|
|
||||||
|
use self::client::config::RequestConfig;
|
||||||
|
use self::shared::{request::Request, response::Response};
|
||||||
|
|
||||||
const TYPEDEFS: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/types.d.luau"));
|
const TYPEDEFS: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/types.d.luau"));
|
||||||
|
|
||||||
|
@ -39,6 +38,8 @@ pub fn module(lua: Lua) -> LuaResult<LuaTable> {
|
||||||
.build_readonly()
|
.build_readonly()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn net_request(lua: Lua, req: Request) -> LuaResult<Response> {
|
async fn net_request(lua: Lua, config: RequestConfig) -> LuaResult<Response> {
|
||||||
req.send(lua).await
|
Request::from_config(config, lua.clone())?
|
||||||
|
.send(lua.clone())
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
95
crates/lune-std-net/src/shared/headers.rs
Normal file
95
crates/lune-std-net/src/shared/headers.rs
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use hyper::{
|
||||||
|
header::{CONTENT_ENCODING, CONTENT_LENGTH},
|
||||||
|
HeaderMap,
|
||||||
|
};
|
||||||
|
|
||||||
|
use lune_utils::TableBuilder;
|
||||||
|
use mlua::prelude::*;
|
||||||
|
|
||||||
|
pub fn create_user_agent_header(lua: &Lua) -> LuaResult<String> {
|
||||||
|
let version_global = lua
|
||||||
|
.globals()
|
||||||
|
.get::<LuaString>("_VERSION")
|
||||||
|
.expect("Missing _VERSION global");
|
||||||
|
|
||||||
|
let version_global_str = version_global
|
||||||
|
.to_str()
|
||||||
|
.context("Invalid utf8 found in _VERSION global")?;
|
||||||
|
|
||||||
|
let (package_name, full_version) = version_global_str.split_once(' ').unwrap();
|
||||||
|
|
||||||
|
Ok(format!("{}/{}", package_name.to_lowercase(), full_version))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn header_map_to_table(
|
||||||
|
lua: &Lua,
|
||||||
|
headers: HeaderMap,
|
||||||
|
remove_content_headers: bool,
|
||||||
|
) -> LuaResult<LuaTable> {
|
||||||
|
let mut res_headers = HashMap::<String, Vec<String>>::new();
|
||||||
|
for (name, value) in &headers {
|
||||||
|
let name = name.as_str();
|
||||||
|
let value = value.to_str().unwrap().to_owned();
|
||||||
|
if let Some(existing) = res_headers.get_mut(name) {
|
||||||
|
existing.push(value);
|
||||||
|
} else {
|
||||||
|
res_headers.insert(name.to_owned(), vec![value]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if remove_content_headers {
|
||||||
|
let content_encoding_header_str = CONTENT_ENCODING.as_str();
|
||||||
|
let content_length_header_str = CONTENT_LENGTH.as_str();
|
||||||
|
res_headers.retain(|name, _| {
|
||||||
|
name != content_encoding_header_str && name != content_length_header_str
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut builder = TableBuilder::new(lua.clone())?;
|
||||||
|
for (name, mut values) in res_headers {
|
||||||
|
if values.len() == 1 {
|
||||||
|
let value = values.pop().unwrap().into_lua(lua)?;
|
||||||
|
builder = builder.with_value(name, value)?;
|
||||||
|
} else {
|
||||||
|
let values = TableBuilder::new(lua.clone())?
|
||||||
|
.with_sequential_values(values)?
|
||||||
|
.build_readonly()?
|
||||||
|
.into_lua(lua)?;
|
||||||
|
builder = builder.with_value(name, values)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.build_readonly()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn table_to_hash_map(
|
||||||
|
tab: LuaTable,
|
||||||
|
tab_origin_key: &'static str,
|
||||||
|
) -> LuaResult<HashMap<String, Vec<String>>> {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
|
for pair in tab.pairs::<String, LuaValue>() {
|
||||||
|
let (key, value) = pair?;
|
||||||
|
match value {
|
||||||
|
LuaValue::String(s) => {
|
||||||
|
map.insert(key, vec![s.to_str()?.to_owned()]);
|
||||||
|
}
|
||||||
|
LuaValue::Table(t) => {
|
||||||
|
let mut values = Vec::new();
|
||||||
|
for value in t.sequence_values::<LuaString>() {
|
||||||
|
values.push(value?.to_str()?.to_owned());
|
||||||
|
}
|
||||||
|
map.insert(key, values);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(LuaError::runtime(format!(
|
||||||
|
"Value for '{tab_origin_key}' must be a string or array of strings",
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(map)
|
||||||
|
}
|
|
@ -1 +1,4 @@
|
||||||
|
pub mod headers;
|
||||||
pub mod hyper;
|
pub mod hyper;
|
||||||
|
pub mod request;
|
||||||
|
pub mod response;
|
||||||
|
|
100
crates/lune-std-net/src/shared/request.rs
Normal file
100
crates/lune-std-net/src/shared/request.rs
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
use http_body_util::Full;
|
||||||
|
|
||||||
|
use hyper::{
|
||||||
|
body::Bytes,
|
||||||
|
client::conn::http1::handshake,
|
||||||
|
header::{HeaderName, HeaderValue, USER_AGENT},
|
||||||
|
HeaderMap, Request as HyperRequest,
|
||||||
|
};
|
||||||
|
|
||||||
|
use mlua::prelude::*;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
client::{config::RequestConfig, stream::HttpRequestStream},
|
||||||
|
shared::{
|
||||||
|
headers::create_user_agent_header,
|
||||||
|
hyper::{HyperExecutor, HyperIo},
|
||||||
|
response::Response,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Request {
|
||||||
|
inner: HyperRequest<Full<Bytes>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Request {
|
||||||
|
pub fn from_config(config: RequestConfig, lua: Lua) -> LuaResult<Self> {
|
||||||
|
// 1. Parse the URL and make sure it is valid
|
||||||
|
let mut url = Url::parse(&config.url).into_lua_err()?;
|
||||||
|
|
||||||
|
// 2. Append any query pairs passed as a table
|
||||||
|
{
|
||||||
|
let mut query = url.query_pairs_mut();
|
||||||
|
for (key, values) in config.query {
|
||||||
|
for value in values {
|
||||||
|
query.append_pair(&key, &value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Create the inner request builder
|
||||||
|
let mut builder = HyperRequest::builder()
|
||||||
|
.method(config.method)
|
||||||
|
.uri(url.as_str());
|
||||||
|
|
||||||
|
// 4. Append any headers passed as a table - builder
|
||||||
|
// headers may be None if builder is already invalid
|
||||||
|
if let Some(headers) = builder.headers_mut() {
|
||||||
|
for (key, values) in config.headers {
|
||||||
|
let key = HeaderName::from_bytes(key.as_bytes()).into_lua_err()?;
|
||||||
|
for value in values {
|
||||||
|
let value = HeaderValue::from_str(&value).into_lua_err()?;
|
||||||
|
headers.insert(key.clone(), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Convert request body bytes to the proper Body
|
||||||
|
// type that Hyper expects, if we got any bytes
|
||||||
|
let body = config
|
||||||
|
.body
|
||||||
|
.map(Bytes::from)
|
||||||
|
.map(Full::new)
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
// 6. Finally, attach the body, verifying that the request
|
||||||
|
// is valid, and attach a user agent if not already set
|
||||||
|
let mut inner = builder.body(body).into_lua_err()?;
|
||||||
|
add_default_headers(&lua, inner.headers_mut())?;
|
||||||
|
Ok(Self { inner })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send(self, lua: Lua) -> LuaResult<Response> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_default_headers(lua: &Lua, headers: &mut HeaderMap) -> LuaResult<()> {
|
||||||
|
if !headers.contains_key(USER_AGENT) {
|
||||||
|
let ua = create_user_agent_header(lua)?;
|
||||||
|
let ua = HeaderValue::from_str(&ua).into_lua_err()?;
|
||||||
|
headers.insert(USER_AGENT, ua);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
71
crates/lune-std-net/src/shared/response.rs
Normal file
71
crates/lune-std-net/src/shared/response.rs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
use futures_lite::prelude::*;
|
||||||
|
use http_body_util::BodyStream;
|
||||||
|
|
||||||
|
use hyper::{
|
||||||
|
body::{Bytes, Incoming},
|
||||||
|
HeaderMap, Response as HyperResponse,
|
||||||
|
};
|
||||||
|
|
||||||
|
use mlua::prelude::*;
|
||||||
|
|
||||||
|
use crate::shared::headers::header_map_to_table;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Response {
|
||||||
|
inner: HyperResponse<Bytes>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Response {
|
||||||
|
pub async fn from_incoming(incoming: HyperResponse<Incoming>) -> LuaResult<Self> {
|
||||||
|
let (parts, body) = incoming.into_parts();
|
||||||
|
|
||||||
|
let body = BodyStream::new(body)
|
||||||
|
.try_fold(Vec::<u8>::new(), |mut body, chunk| {
|
||||||
|
if let Some(chunk) = chunk.data_ref() {
|
||||||
|
body.extend_from_slice(chunk);
|
||||||
|
}
|
||||||
|
Ok(body)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.into_lua_err()?;
|
||||||
|
|
||||||
|
let bytes = Bytes::from(body);
|
||||||
|
let inner = HyperResponse::from_parts(parts, bytes);
|
||||||
|
|
||||||
|
Ok(Self { inner })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn status_ok(&self) -> bool {
|
||||||
|
self.inner.status().is_success()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn status_code(&self) -> u16 {
|
||||||
|
self.inner.status().as_u16()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn status_message(&self) -> &str {
|
||||||
|
self.inner.status().canonical_reason().unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn headers(&self) -> &HeaderMap {
|
||||||
|
self.inner.headers()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn body(&self) -> &[u8] {
|
||||||
|
self.inner.body()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LuaUserData for Response {
|
||||||
|
fn add_fields<F: LuaUserDataFields<Self>>(fields: &mut F) {
|
||||||
|
fields.add_field_method_get("ok", |_, this| Ok(this.status_ok()));
|
||||||
|
fields.add_field_method_get("statusCode", |_, this| Ok(this.status_code()));
|
||||||
|
fields.add_field_method_get("statusMessage", |lua, this| {
|
||||||
|
lua.create_string(this.status_message())
|
||||||
|
});
|
||||||
|
fields.add_field_method_get("headers", |lua, this| {
|
||||||
|
header_map_to_table(lua, this.headers().clone(), false)
|
||||||
|
});
|
||||||
|
fields.add_field_method_get("body", |lua, this| lua.create_string(this.body()));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue