Rocket/contrib/lib/src/msgpack.rs

171 lines
5.3 KiB
Rust
Raw Normal View History

2018-10-13 01:52:38 +00:00
//! Automatic MessagePack (de)serialization support.
//!
//! See the [`MsgPack`](msgpack::MessagePack) type for further details.
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;
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};
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
///
/// 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
///
/// ```rust
2018-10-06 04:56:46 +00:00
/// # #![feature(proc_macro_hygiene, decl_macro)]
/// # #[macro_use] extern crate rocket;
/// # extern crate rocket_contrib;
/// # type User = usize;
2018-10-07 00:24:11 +00:00
/// use rocket_contrib::msgpack::MsgPack;
///
/// #[post("/users", format = "msgpack", data = "<user>")]
2017-02-08 03:40:14 +00:00
/// fn new_user(user: MsgPack<User>) {
/// /* ... */
2017-02-08 03:40:14 +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
/// be routed to this handler.
2017-02-08 03:40:14 +00:00
///
2017-04-18 07:36:39 +00:00
/// ## Sending MessagePack
///
/// 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
///
/// ```rust
2018-10-06 04:56:46 +00:00
/// # #![feature(proc_macro_hygiene, decl_macro)]
/// # #[macro_use] extern crate rocket;
/// # extern crate rocket_contrib;
/// # type User = usize;
2018-10-07 00:24:11 +00:00
/// use rocket_contrib::msgpack::MsgPack;
///
2017-02-08 03:40:14 +00:00
/// #[get("/users/<id>")]
/// fn user(id: usize) -> MsgPack<User> {
/// let user_from_id = User::from(id);
/// /* ... */
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-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());
/// ```
#[inline(always)]
2017-02-08 03:40:14 +00:00
pub fn into_inner(self) -> T {
self.0
}
}
/// Default limit for MessagePack is 1MB.
const LIMIT: u64 = 1 << 20;
2017-02-08 03:40:14 +00:00
impl<'a, T: Deserialize<'a>> FromData<'a> for MsgPack<T> {
2018-10-07 00:24:11 +00:00
type Error = Error;
type Owned = Vec<u8>;
type Borrowed = [u8];
2017-02-08 03:40:14 +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();
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))))
}
}
2017-02-08 03:40:14 +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::*;
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
}
}
/// 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> {
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;
#[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> {
#[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
}
}