diff --git a/Cargo.lock b/Cargo.lock index 9acc1db..4b19cfd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,6 +56,15 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "futures" version = "0.3.26" @@ -237,6 +246,16 @@ dependencies = [ "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]] name = "indexmap" version = "1.9.2" @@ -389,6 +408,12 @@ dependencies = [ "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]] name = "pin-project-lite" version = "0.2.9" @@ -524,8 +549,24 @@ dependencies = [ "mlua", "serde_json", "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]] name = "tokio" version = "1.25.0" @@ -603,12 +644,38 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +[[package]] +name = "unicode-bidi" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" + [[package]] name = "unicode-ident" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "want" version = "0.3.0" diff --git a/Cargo.toml b/Cargo.toml index b2e7402..b219c79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,3 +29,4 @@ mlua = { version = "0.8", features = [ ] } serde_json = { version = "1.0" } tokio = { version = "1.0", features = ["full"] } +url = { version = "2.3" } diff --git a/examples/html.lua b/examples/html.lua new file mode 100644 index 0000000..630dd4f --- /dev/null +++ b/examples/html.lua @@ -0,0 +1,22 @@ +local app = create_app() + +app:get("/", function(req, res) + res.body = [[ + + +
+ + +
+ + +]] + return res +end) + +app:post("/submit", function(req, res) + res.body = req.form.hello + return res +end) + +app:run(3000) diff --git a/src/internal/core/service.rs b/src/internal/core/service.rs index ef1cf9a..88eb87f 100644 --- a/src/internal/core/service.rs +++ b/src/internal/core/service.rs @@ -11,6 +11,7 @@ use std::pin::Pin; use std::rc::Rc; use std::task::{Context, Poll}; use tokio::task::spawn_local; +use url::form_urlencoded; use crate::internal::lua::req::Req; use crate::internal::lua::res::Res; @@ -34,7 +35,7 @@ impl Service> for Svc { let addr = self.1.clone(); let router = self.2.clone(); 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()) { Ok(res) => res, Err(_) => { @@ -52,30 +53,44 @@ impl Service> for Svc { .unwrap()); } - let body_bytes = to_bytes(body).await.unwrap(); - let body_str = String::from_utf8(body_bytes.to_vec()).unwrap(); + let mut params_map: HashMap = HashMap::new(); + for (k, v) in lua_handle_match.params.iter() { + params_map.insert(k.to_string(), v.to_string()); + } let mut headers_map: HashMap = HashMap::new(); for (k, v) in parts.headers { headers_map.insert(k.unwrap().to_string(), v.to_str().unwrap().to_string()); } - let mut params_map: HashMap = HashMap::new(); - for (k, v) in lua_handle_match.params.iter() { - params_map.insert(k.to_string(), v.to_string()); + let form_content_type = "application/x-www-form-urlencoded"; + + let body_bytes = to_bytes(body).await.unwrap(); + + let mut form_map: HashMap = 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::>(); + } } + let body_str = String::from_utf8(body_bytes.to_vec()).unwrap(); + let req = Req { body: body_str, headers: headers_map, + form: form_map, method: lua_handle_match.value.method.to_string(), params: params_map, socket_addr: addr.to_string(), }; let res = Res { - body: "".to_string(), status: StatusCode::OK, + body: "".to_string(), + json: "".to_string(), }; let handler: Function = lua @@ -168,6 +183,7 @@ mod tests { Ok(Res { status: StatusCode::IM_A_TEAPOT, body: "Hello, World!".to_string(), + json: "".to_string(), }) }) .unwrap(); diff --git a/src/internal/lua/req.rs b/src/internal/lua/req.rs index 174f1dd..c89ef46 100644 --- a/src/internal/lua/req.rs +++ b/src/internal/lua/req.rs @@ -5,6 +5,7 @@ use mlua::{LuaSerdeExt, UserData, UserDataFields}; #[derive(Clone)] pub struct Req { pub body: String, + pub form: HashMap, pub headers: HashMap, pub method: String, pub params: HashMap, @@ -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| { this.headers = value; Ok(()) diff --git a/src/internal/lua/res.rs b/src/internal/lua/res.rs index e6fbae0..99d97f5 100644 --- a/src/internal/lua/res.rs +++ b/src/internal/lua/res.rs @@ -20,16 +20,23 @@ pub fn json_encode(value: LuaValue, pretty: Option) -> Result>(fields: &mut F) { fields.add_field_method_set("body", |_, this, value| { - this.body = json_encode(value, None).unwrap().unwrap(); + this.body = value; Ok(()) }); 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| { this.status = StatusCode::from_str(value.as_str()).unwrap(); Ok(())