2018-10-07 00:24:11 +00:00
|
|
|
extern crate serde;
|
2017-02-08 03:40:14 +00:00
|
|
|
extern crate rmp_serde;
|
|
|
|
|
|
|
|
use std::ops::{Deref, DerefMut};
|
|
|
|
use std::io::{Cursor, Read};
|
|
|
|
|
|
|
|
use rocket::request::Request;
|
2018-09-26 08:13:57 +00:00
|
|
|
use rocket::outcome::Outcome::*;
|
|
|
|
use rocket::data::{Outcome, Transform, Transform::*, Transformed, Data, FromData};
|
2017-02-08 03:40:14 +00:00
|
|
|
use rocket::response::{self, Responder, Response};
|
2018-09-26 08:13:57 +00:00
|
|
|
use rocket::http::Status;
|
2017-02-08 03:40:14 +00:00
|
|
|
|
2018-10-07 00:24:11 +00:00
|
|
|
use self::serde::Serialize;
|
|
|
|
use self::serde::de::Deserialize;
|
2017-02-08 03:40:14 +00:00
|
|
|
|
2018-10-07 00:24:11 +00:00
|
|
|
pub use self::rmp_serde::decode::Error;
|
2017-02-08 03:40:14 +00:00
|
|
|
|
2018-10-07 00:24:11 +00:00
|
|
|
/// The `MsgPack` type: implements [`FromData`] and [`Responder`], allowing you
|
|
|
|
/// to easily consume and respond with MessagePack data.
|
2017-02-08 03:40:14 +00:00
|
|
|
///
|
2017-04-18 07:36:39 +00:00
|
|
|
/// ## Receiving MessagePack
|
|
|
|
///
|
2017-03-24 05:41:42 +00:00
|
|
|
/// If you're receiving MessagePack data, simply add a `data` parameter to your
|
|
|
|
/// route arguments and ensure the type of the parameter is a `MsgPack<T>`,
|
|
|
|
/// where `T` is some type you'd like to parse from MessagePack. `T` must
|
2018-10-07 00:24:11 +00:00
|
|
|
/// implement [`Deserialize`] from [`serde`]. The data is parsed from the HTTP
|
|
|
|
/// request body.
|
2017-02-08 03:40:14 +00:00
|
|
|
///
|
2018-07-11 00:08:58 +00:00
|
|
|
/// ```rust
|
2018-10-06 04:56:46 +00:00
|
|
|
/// # #![feature(proc_macro_hygiene, decl_macro)]
|
2018-10-05 04:44:42 +00:00
|
|
|
/// # #[macro_use] extern crate rocket;
|
2018-07-11 00:08:58 +00:00
|
|
|
/// # extern crate rocket_contrib;
|
|
|
|
/// # type User = usize;
|
2018-10-07 00:24:11 +00:00
|
|
|
/// use rocket_contrib::msgpack::MsgPack;
|
2018-07-11 00:08:58 +00:00
|
|
|
///
|
|
|
|
/// #[post("/users", format = "msgpack", data = "<user>")]
|
2017-02-08 03:40:14 +00:00
|
|
|
/// fn new_user(user: MsgPack<User>) {
|
2018-07-11 00:08:58 +00:00
|
|
|
/// /* ... */
|
2017-02-08 03:40:14 +00:00
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
2018-07-11 00:08:58 +00:00
|
|
|
/// You don't _need_ to use `format = "msgpack"`, but it _may_ be what you want.
|
|
|
|
/// Using `format = msgpack` means that any request that doesn't specify
|
|
|
|
/// "application/msgpack" as its first `Content-Type:` header parameter will not
|
2018-09-26 08:13:57 +00:00
|
|
|
/// be routed to this handler.
|
2017-02-08 03:40:14 +00:00
|
|
|
///
|
2017-04-18 07:36:39 +00:00
|
|
|
/// ## Sending MessagePack
|
|
|
|
///
|
2017-03-24 05:41:42 +00:00
|
|
|
/// If you're responding with MessagePack data, return a `MsgPack<T>` type,
|
2018-10-07 00:24:11 +00:00
|
|
|
/// where `T` implements [`Serialize`] from [`serde`]. The content type of the
|
|
|
|
/// response is set to `application/msgpack` automatically.
|
2017-02-08 03:40:14 +00:00
|
|
|
///
|
2018-07-11 00:08:58 +00:00
|
|
|
/// ```rust
|
2018-10-06 04:56:46 +00:00
|
|
|
/// # #![feature(proc_macro_hygiene, decl_macro)]
|
2018-10-05 04:44:42 +00:00
|
|
|
/// # #[macro_use] extern crate rocket;
|
2018-07-11 00:08:58 +00:00
|
|
|
/// # extern crate rocket_contrib;
|
|
|
|
/// # type User = usize;
|
2018-10-07 00:24:11 +00:00
|
|
|
/// use rocket_contrib::msgpack::MsgPack;
|
2018-07-11 00:08:58 +00:00
|
|
|
///
|
2017-02-08 03:40:14 +00:00
|
|
|
/// #[get("/users/<id>")]
|
|
|
|
/// fn user(id: usize) -> MsgPack<User> {
|
|
|
|
/// let user_from_id = User::from(id);
|
2018-07-11 00:08:58 +00:00
|
|
|
/// /* ... */
|
2017-02-08 03:40:14 +00:00
|
|
|
/// MsgPack(user_from_id)
|
|
|
|
/// }
|
|
|
|
/// ```
|
2017-04-18 07:36:39 +00:00
|
|
|
///
|
|
|
|
/// ## Incoming Data Limits
|
|
|
|
///
|
|
|
|
/// The default size limit for incoming MessagePack data is 1MiB. Setting a
|
|
|
|
/// limit protects your application from denial of service (DOS) attacks and
|
|
|
|
/// from resource exhaustion through high memory consumption. The limit can be
|
|
|
|
/// increased by setting the `limits.msgpack` configuration parameter. For
|
|
|
|
/// instance, to increase the MessagePack limit to 5MiB for all environments,
|
|
|
|
/// you may add the following to your `Rocket.toml`:
|
|
|
|
///
|
|
|
|
/// ```toml
|
|
|
|
/// [global.limits]
|
|
|
|
/// msgpack = 5242880
|
|
|
|
/// ```
|
2017-02-08 03:40:14 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct MsgPack<T>(pub T);
|
|
|
|
|
|
|
|
impl<T> MsgPack<T> {
|
|
|
|
/// Consumes the `MsgPack` wrapper and returns the wrapped item.
|
|
|
|
///
|
|
|
|
/// # Example
|
2017-03-24 05:41:42 +00:00
|
|
|
///
|
2017-02-08 03:40:14 +00:00
|
|
|
/// ```rust
|
2018-10-07 00:24:11 +00:00
|
|
|
/// # use rocket_contrib::msgpack::MsgPack;
|
2017-02-08 03:40:14 +00:00
|
|
|
/// let string = "Hello".to_string();
|
|
|
|
/// let my_msgpack = MsgPack(string);
|
|
|
|
/// assert_eq!(my_msgpack.into_inner(), "Hello".to_string());
|
|
|
|
/// ```
|
2017-03-24 05:41:42 +00:00
|
|
|
#[inline(always)]
|
2017-02-08 03:40:14 +00:00
|
|
|
pub fn into_inner(self) -> T {
|
|
|
|
self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-18 07:25:13 +00:00
|
|
|
/// Default limit for MessagePack is 1MB.
|
|
|
|
const LIMIT: u64 = 1 << 20;
|
2017-02-08 03:40:14 +00:00
|
|
|
|
2018-09-26 08:13:57 +00:00
|
|
|
impl<'a, T: Deserialize<'a>> FromData<'a> for MsgPack<T> {
|
2018-10-07 00:24:11 +00:00
|
|
|
type Error = Error;
|
2018-09-26 08:13:57 +00:00
|
|
|
type Owned = Vec<u8>;
|
|
|
|
type Borrowed = [u8];
|
2017-02-08 03:40:14 +00:00
|
|
|
|
2018-09-26 08:13:57 +00:00
|
|
|
fn transform(r: &Request, d: Data) -> Transform<Outcome<Self::Owned, Self::Error>> {
|
2017-02-08 03:40:14 +00:00
|
|
|
let mut buf = Vec::new();
|
2018-09-26 08:13:57 +00:00
|
|
|
let size_limit = r.limits().get("msgpack").unwrap_or(LIMIT);
|
|
|
|
match d.open().take(size_limit).read_to_end(&mut buf) {
|
|
|
|
Ok(_) => Borrowed(Success(buf)),
|
2018-10-07 00:24:11 +00:00
|
|
|
Err(e) => Borrowed(Failure((Status::BadRequest, Error::InvalidDataRead(e))))
|
2018-09-26 08:13:57 +00:00
|
|
|
}
|
|
|
|
}
|
2017-02-08 03:40:14 +00:00
|
|
|
|
2018-09-26 08:13:57 +00:00
|
|
|
fn from_data(_: &Request, o: Transformed<'a, Self>) -> Outcome<Self, Self::Error> {
|
2018-10-07 00:24:11 +00:00
|
|
|
use self::Error::*;
|
2018-09-26 08:13:57 +00:00
|
|
|
|
|
|
|
let buf = o.borrowed()?;
|
|
|
|
match rmp_serde::from_slice(&buf) {
|
|
|
|
Ok(val) => Success(MsgPack(val)),
|
|
|
|
Err(e) => {
|
|
|
|
error_!("Couldn't parse MessagePack body: {:?}", e);
|
|
|
|
match e {
|
|
|
|
TypeMismatch(_) | OutOfRange | LengthMismatch(_) => {
|
|
|
|
Failure((Status::UnprocessableEntity, e))
|
|
|
|
}
|
|
|
|
_ => Failure((Status::BadRequest, e))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-02-08 03:40:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-24 05:41:42 +00:00
|
|
|
/// Serializes the wrapped value into MessagePack. Returns a response with
|
|
|
|
/// Content-Type `MsgPack` and a fixed-size body with the serialization. If
|
|
|
|
/// serialization fails, an `Err` of `Status::InternalServerError` is returned.
|
2017-02-08 03:40:14 +00:00
|
|
|
impl<T: Serialize> Responder<'static> for MsgPack<T> {
|
2017-05-19 10:29:08 +00:00
|
|
|
fn respond_to(self, _: &Request) -> response::Result<'static> {
|
2017-02-08 03:40:14 +00:00
|
|
|
rmp_serde::to_vec(&self.0).map_err(|e| {
|
|
|
|
error_!("MsgPack failed to serialize: {:?}", e);
|
|
|
|
Status::InternalServerError
|
|
|
|
}).and_then(|buf| {
|
|
|
|
Response::build()
|
|
|
|
.sized_body(Cursor::new(buf))
|
|
|
|
.ok()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> Deref for MsgPack<T> {
|
|
|
|
type Target = T;
|
|
|
|
|
2017-03-24 05:41:42 +00:00
|
|
|
#[inline(always)]
|
2018-07-28 16:58:10 +00:00
|
|
|
fn deref(&self) -> &T {
|
2017-02-08 03:40:14 +00:00
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> DerefMut for MsgPack<T> {
|
2017-03-24 05:41:42 +00:00
|
|
|
#[inline(always)]
|
2018-07-28 16:58:10 +00:00
|
|
|
fn deref_mut(&mut self) -> &mut T {
|
2017-02-08 03:40:14 +00:00
|
|
|
&mut self.0
|
|
|
|
}
|
|
|
|
}
|