feat: basic handle of multipart form-data
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
e8cdc93ebd
commit
84d7e20baa
170
Cargo.lock
generated
170
Cargo.lock
generated
@ -23,6 +23,16 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "buf_redux"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"safemem",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.4.0"
|
||||
@ -50,6 +60,15 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
|
||||
dependencies = [
|
||||
"instant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
@ -154,6 +173,17 @@ dependencies = [
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.15"
|
||||
@ -266,6 +296,15 @@ dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.5"
|
||||
@ -327,6 +366,22 @@ version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
||||
|
||||
[[package]]
|
||||
name = "mime_guess"
|
||||
version = "2.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
|
||||
dependencies = [
|
||||
"mime",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.5"
|
||||
@ -360,6 +415,24 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multipart"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182"
|
||||
dependencies = [
|
||||
"buf_redux",
|
||||
"httparse",
|
||||
"log",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"quick-error",
|
||||
"rand",
|
||||
"safemem",
|
||||
"tempfile",
|
||||
"twoway",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
@ -432,6 +505,12 @@ version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.51"
|
||||
@ -441,6 +520,12 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.23"
|
||||
@ -450,6 +535,36 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.16"
|
||||
@ -459,6 +574,15 @@ dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
@ -471,6 +595,12 @@ version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
|
||||
|
||||
[[package]]
|
||||
name = "safemem"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
@ -546,12 +676,28 @@ dependencies = [
|
||||
"futures",
|
||||
"hyper",
|
||||
"matchit",
|
||||
"mime",
|
||||
"mlua",
|
||||
"multipart",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"remove_dir_all",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
@ -644,6 +790,24 @@ version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
|
||||
|
||||
[[package]]
|
||||
name = "twoway"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.10"
|
||||
@ -676,6 +840,12 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.0"
|
||||
|
||||
@ -27,6 +27,10 @@ mlua = { version = "0.8", features = [
|
||||
"async",
|
||||
"serialize",
|
||||
] }
|
||||
mime = { version = "0.3" }
|
||||
multipart = { version = "0.18", default-features = false, features = [
|
||||
"server",
|
||||
], optional = false }
|
||||
serde_json = { version = "1.0" }
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
url = { version = "2.3" }
|
||||
|
||||
@ -2,7 +2,7 @@ local app = create_app()
|
||||
|
||||
app:get("/", function(req, res)
|
||||
res.body = [[
|
||||
<html>
|
||||
<html style="background-color:black;">
|
||||
<body>
|
||||
<form action="/submit" method="post">
|
||||
<input name="hello" value="Hello, World!" />
|
||||
|
||||
12
examples/multipart.lua
Normal file
12
examples/multipart.lua
Normal file
@ -0,0 +1,12 @@
|
||||
local app = create_app()
|
||||
|
||||
app:post("/", function(req,res)
|
||||
for k,v in pairs(req.parts) do
|
||||
print("name", k)
|
||||
print("filename", v.filename)
|
||||
print("content", v.content)
|
||||
end
|
||||
return res
|
||||
end)
|
||||
|
||||
app:run(3000)
|
||||
@ -15,12 +15,14 @@ end)
|
||||
local app = create_app()
|
||||
|
||||
app:get("/:segment", function(req, res)
|
||||
res.body = { data = req.params.segment }
|
||||
res.json = { data = req.params.segment }
|
||||
return res
|
||||
end)
|
||||
|
||||
app:post("/submit", function(req, res)
|
||||
print(req.body)
|
||||
for k,v in pairs(req.form) do
|
||||
print(k,v)
|
||||
end
|
||||
return res
|
||||
end)
|
||||
|
||||
|
||||
@ -3,9 +3,12 @@ use hyper::server::conn::AddrStream;
|
||||
use hyper::service::Service;
|
||||
use hyper::{Body, Request, Response, StatusCode};
|
||||
use matchit::Router;
|
||||
use mime::Mime;
|
||||
use mlua::{Error as LuaError, Function, Lua};
|
||||
use multipart::server::Multipart;
|
||||
use std::collections::HashMap;
|
||||
use std::future::Future;
|
||||
use std::io::{Cursor, Read};
|
||||
use std::net::SocketAddr;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
@ -13,7 +16,7 @@ use std::task::{Context, Poll};
|
||||
use tokio::task::spawn_local;
|
||||
use url::form_urlencoded;
|
||||
|
||||
use crate::internal::lua::req::Req;
|
||||
use crate::internal::lua::req::{Part, Req};
|
||||
use crate::internal::lua::res::Res;
|
||||
|
||||
use super::routers::LuaHandle;
|
||||
@ -64,26 +67,57 @@ impl Service<Request<Body>> for Svc {
|
||||
}
|
||||
|
||||
let form_content_type = "application/x-www-form-urlencoded";
|
||||
let multipart_content_type = "multipart/form-data";
|
||||
|
||||
let body_bytes = to_bytes(body).await.unwrap();
|
||||
let mut body_str = String::new();
|
||||
|
||||
let mut form_map: HashMap<String, String> = HashMap::new();
|
||||
let mut parts_map: HashMap<String, Part> = 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>>();
|
||||
} else if content_type.starts_with(multipart_content_type) {
|
||||
let mime = content_type.parse::<Mime>().unwrap();
|
||||
if let Some(boundary) = mime.get_param("boundary").map(|v| v.to_string()) {
|
||||
let mut multipart = Multipart::with_body(Cursor::new(body_bytes), boundary);
|
||||
multipart
|
||||
.foreach_entry(|mut arg| {
|
||||
let mut buf = String::new();
|
||||
let name = arg.headers.name;
|
||||
arg.data.read_to_string(&mut buf).unwrap();
|
||||
match arg.headers.filename {
|
||||
Some(filename) => {
|
||||
parts_map.insert(
|
||||
name.to_string(),
|
||||
Part {
|
||||
filename,
|
||||
content: buf,
|
||||
},
|
||||
);
|
||||
}
|
||||
None => {
|
||||
form_map.insert(name.to_string(), buf);
|
||||
}
|
||||
};
|
||||
()
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
} else {
|
||||
body_str = String::from_utf8(body_bytes.to_vec()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
parts: parts_map,
|
||||
socket_addr: addr.to_string(),
|
||||
};
|
||||
|
||||
|
||||
@ -2,6 +2,27 @@ use std::collections::HashMap;
|
||||
|
||||
use mlua::{LuaSerdeExt, UserData, UserDataFields};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Part {
|
||||
pub filename: String,
|
||||
pub content: String,
|
||||
}
|
||||
impl UserData for Part {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_method_set("filename", |_, this, value| {
|
||||
this.filename = value;
|
||||
Ok(())
|
||||
});
|
||||
fields.add_field_method_get("filename", |_, this| Ok(this.filename.to_owned()));
|
||||
|
||||
fields.add_field_method_set("content", |_, this, value| {
|
||||
this.content = value;
|
||||
Ok(())
|
||||
});
|
||||
fields.add_field_method_get("content", |_, this| Ok(this.content.to_owned()));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Req {
|
||||
pub body: String,
|
||||
@ -9,6 +30,7 @@ pub struct Req {
|
||||
pub headers: HashMap<String, String>,
|
||||
pub method: String,
|
||||
pub params: HashMap<String, String>,
|
||||
pub parts: HashMap<String, Part>,
|
||||
pub socket_addr: String,
|
||||
}
|
||||
|
||||
@ -48,5 +70,11 @@ impl UserData for Req {
|
||||
Ok(())
|
||||
});
|
||||
fields.add_field_method_get("params", |_, this| Ok(this.params.to_owned()));
|
||||
|
||||
fields.add_field_method_set("parts", |_, this, value| {
|
||||
this.parts = value;
|
||||
Ok(())
|
||||
});
|
||||
fields.add_field_method_get("parts", |_, this| Ok(this.parts.to_owned()));
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user