forked from abonander/multipart
-
Notifications
You must be signed in to change notification settings - Fork 1
/
rocket.rs
93 lines (79 loc) · 2.95 KB
/
rocket.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// Example usage with Rocket (https://rocket.rs)
//
// Direct integration is not provided at this time as it appears the Rocket folks would prefer
// to handle multipart requests behind the scenes.
#![feature(proc_macro_hygiene, decl_macro)]
#![feature(plugin, custom_attribute)]
extern crate qiniu_multipart;
#[macro_use]
extern crate rocket;
use qiniu_multipart::mock::StdoutTee;
use qiniu_multipart::server::save::Entries;
use qiniu_multipart::server::save::SaveResult::*;
use qiniu_multipart::server::Multipart;
use rocket::http::{ContentType, Status};
use rocket::response::status::Custom;
use rocket::response::Stream;
use rocket::Data;
use std::io::{self, Cursor, Write};
#[post("/upload", data = "<data>")]
// signature requires the request to have a `Content-Type`
fn multipart_upload(
cont_type: &ContentType,
data: Data,
) -> Result<Stream<Cursor<Vec<u8>>>, Custom<String>> {
// this and the next check can be implemented as a request guard but it seems like just
// more boilerplate than necessary
if !cont_type.is_form_data() {
return Err(Custom(
Status::BadRequest,
"Content-Type not multipart/form-data".into(),
));
}
let (_, boundary) = cont_type
.params()
.find(|&(k, _)| k == "boundary")
.ok_or_else(|| {
Custom(
Status::BadRequest,
"`Content-Type: multipart/form-data` boundary param not provided".into(),
)
})?;
match process_upload(boundary, data) {
Ok(resp) => Ok(Stream::from(Cursor::new(resp))),
Err(err) => Err(Custom(Status::InternalServerError, err.to_string())),
}
}
fn process_upload(boundary: &str, data: Data) -> io::Result<Vec<u8>> {
let mut out = Vec::new();
// saves all fields, any field longer than 10kB goes to a temporary directory
// Entries could implement FromData though that would give zero control over
// how the files are saved; Multipart would be a good impl candidate though
match Multipart::with_body(data.open(), boundary).save().temp() {
Full(entries) => process_entries(entries, &mut out)?,
Partial(partial, reason) => {
writeln!(out, "Request partially processed: {:?}", reason)?;
if let Some(field) = partial.partial {
writeln!(out, "Stopped on field: {:?}", field.source.headers)?;
}
process_entries(partial.entries, &mut out)?
}
Error(e) => return Err(e),
}
Ok(out)
}
// having a streaming output would be nice; there's one for returning a `Read` impl
// but not one that you can `write()` to
fn process_entries(entries: Entries, mut out: &mut Vec<u8>) -> io::Result<()> {
{
let stdout = io::stdout();
let tee = StdoutTee::new(&mut out, &stdout);
entries.write_debug(tee)?;
}
writeln!(out, "Entries processed")
}
fn main() {
rocket::ignite()
.mount("/", routes![multipart_upload])
.launch();
}