feat: handle content-type x-www-form-urlencoded
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
ato 2023-02-14 23:10:39 +01:00
parent c091528f9f
commit e8cdc93ebd
6 changed files with 128 additions and 8 deletions

67
Cargo.lock generated
View File

@ -56,6 +56,15 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
dependencies = [
"percent-encoding",
]
[[package]] [[package]]
name = "futures" name = "futures"
version = "0.3.26" version = "0.3.26"
@ -237,6 +246,16 @@ dependencies = [
"want", "want",
] ]
[[package]]
name = "idna"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
dependencies = [
"unicode-bidi",
"unicode-normalization",
]
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "1.9.2" version = "1.9.2"
@ -389,6 +408,12 @@ dependencies = [
"windows-sys 0.45.0", "windows-sys 0.45.0",
] ]
[[package]]
name = "percent-encoding"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
[[package]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.9" version = "0.2.9"
@ -524,8 +549,24 @@ dependencies = [
"mlua", "mlua",
"serde_json", "serde_json",
"tokio", "tokio",
"url",
] ]
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.25.0" version = "1.25.0"
@ -603,12 +644,38 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
[[package]]
name = "unicode-bidi"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.6" version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "unicode-normalization"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"tinyvec",
]
[[package]]
name = "url"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
]
[[package]] [[package]]
name = "want" name = "want"
version = "0.3.0" version = "0.3.0"

View File

@ -29,3 +29,4 @@ mlua = { version = "0.8", features = [
] } ] }
serde_json = { version = "1.0" } serde_json = { version = "1.0" }
tokio = { version = "1.0", features = ["full"] } tokio = { version = "1.0", features = ["full"] }
url = { version = "2.3" }

22
examples/html.lua Normal file
View File

@ -0,0 +1,22 @@
local app = create_app()
app:get("/", function(req, res)
res.body = [[
<html>
<body>
<form action="/submit" method="post">
<input name="hello" value="Hello, World!" />
<button type="submit">submit</button>
</form>
</body>
</html>
]]
return res
end)
app:post("/submit", function(req, res)
res.body = req.form.hello
return res
end)
app:run(3000)

View File

@ -11,6 +11,7 @@ use std::pin::Pin;
use std::rc::Rc; use std::rc::Rc;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use tokio::task::spawn_local; use tokio::task::spawn_local;
use url::form_urlencoded;
use crate::internal::lua::req::Req; use crate::internal::lua::req::Req;
use crate::internal::lua::res::Res; use crate::internal::lua::res::Res;
@ -34,7 +35,7 @@ impl Service<Request<Body>> for Svc {
let addr = self.1.clone(); let addr = self.1.clone();
let router = self.2.clone(); let router = self.2.clone();
Box::pin(async move { Box::pin(async move {
let uri = parts.uri.to_string(); let uri = parts.uri.path().to_string();
let lua_handle_match = match router.at(uri.as_str()) { let lua_handle_match = match router.at(uri.as_str()) {
Ok(res) => res, Ok(res) => res,
Err(_) => { Err(_) => {
@ -52,30 +53,44 @@ impl Service<Request<Body>> for Svc {
.unwrap()); .unwrap());
} }
let body_bytes = to_bytes(body).await.unwrap(); let mut params_map: HashMap<String, String> = HashMap::new();
let body_str = String::from_utf8(body_bytes.to_vec()).unwrap(); for (k, v) in lua_handle_match.params.iter() {
params_map.insert(k.to_string(), v.to_string());
}
let mut headers_map: HashMap<String, String> = HashMap::new(); let mut headers_map: HashMap<String, String> = HashMap::new();
for (k, v) in parts.headers { for (k, v) in parts.headers {
headers_map.insert(k.unwrap().to_string(), v.to_str().unwrap().to_string()); headers_map.insert(k.unwrap().to_string(), v.to_str().unwrap().to_string());
} }
let mut params_map: HashMap<String, String> = HashMap::new(); let form_content_type = "application/x-www-form-urlencoded";
for (k, v) in lua_handle_match.params.iter() {
params_map.insert(k.to_string(), v.to_string()); let body_bytes = to_bytes(body).await.unwrap();
let mut form_map: HashMap<String, String> = HashMap::new();
if let Some(content_type) = headers_map.get("content-type") {
if content_type.eq_ignore_ascii_case(form_content_type) {
form_map = form_urlencoded::parse(&body_bytes.as_ref())
.into_owned()
.collect::<HashMap<String, String>>();
}
} }
let body_str = String::from_utf8(body_bytes.to_vec()).unwrap();
let req = Req { let req = Req {
body: body_str, body: body_str,
headers: headers_map, headers: headers_map,
form: form_map,
method: lua_handle_match.value.method.to_string(), method: lua_handle_match.value.method.to_string(),
params: params_map, params: params_map,
socket_addr: addr.to_string(), socket_addr: addr.to_string(),
}; };
let res = Res { let res = Res {
body: "".to_string(),
status: StatusCode::OK, status: StatusCode::OK,
body: "".to_string(),
json: "".to_string(),
}; };
let handler: Function = lua let handler: Function = lua
@ -168,6 +183,7 @@ mod tests {
Ok(Res { Ok(Res {
status: StatusCode::IM_A_TEAPOT, status: StatusCode::IM_A_TEAPOT,
body: "Hello, World!".to_string(), body: "Hello, World!".to_string(),
json: "".to_string(),
}) })
}) })
.unwrap(); .unwrap();

View File

@ -5,6 +5,7 @@ use mlua::{LuaSerdeExt, UserData, UserDataFields};
#[derive(Clone)] #[derive(Clone)]
pub struct Req { pub struct Req {
pub body: String, pub body: String,
pub form: HashMap<String, String>,
pub headers: HashMap<String, String>, pub headers: HashMap<String, String>,
pub method: String, pub method: String,
pub params: HashMap<String, String>, pub params: HashMap<String, String>,
@ -24,6 +25,12 @@ impl UserData for Req {
} }
}); });
fields.add_field_method_set("form", |_, this, value| {
this.form = value;
Ok(())
});
fields.add_field_method_get("form", |_, this| Ok(this.form.to_owned()));
fields.add_field_method_set("headers", |_, this, value| { fields.add_field_method_set("headers", |_, this, value| {
this.headers = value; this.headers = value;
Ok(()) Ok(())

View File

@ -20,16 +20,23 @@ pub fn json_encode(value: LuaValue, pretty: Option<bool>) -> Result<Option<Strin
pub struct Res { pub struct Res {
pub status: StatusCode, pub status: StatusCode,
pub body: String, pub body: String,
pub json: String,
} }
impl UserData for Res { impl UserData for Res {
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) { fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
fields.add_field_method_set("body", |_, this, value| { fields.add_field_method_set("body", |_, this, value| {
this.body = json_encode(value, None).unwrap().unwrap(); this.body = value;
Ok(()) Ok(())
}); });
fields.add_field_method_get("body", |_, this| Ok(this.body.to_owned())); fields.add_field_method_get("body", |_, this| Ok(this.body.to_owned()));
fields.add_field_method_set("json", |_, this, value| {
this.json = json_encode(value, None).unwrap().unwrap();
Ok(())
});
fields.add_field_method_get("json", |_, this| Ok(this.json.to_owned()));
fields.add_field_method_set("status", |_, this, value: String| { fields.add_field_method_set("status", |_, this, value: String| {
this.status = StatusCode::from_str(value.as_str()).unwrap(); this.status = StatusCode::from_str(value.as_str()).unwrap();
Ok(()) Ok(())