2017-02-08 03:40:14 +00:00
|
|
|
extern crate rmp_serde;
|
|
|
|
|
|
|
|
use std::ops::{Deref, DerefMut};
|
|
|
|
use std::io::{Cursor, Read};
|
|
|
|
|
2017-04-18 07:25:13 +00:00
|
|
|
use rocket::config;
|
2017-02-08 03:40:14 +00:00
|
|
|
use rocket::outcome::Outcome;
|
|
|
|
use rocket::request::Request;
|
|
|
|
use rocket::data::{self, Data, FromData};
|
|
|
|
use rocket::response::{self, Responder, Response};
|
|
|
|
use rocket::http::{ContentType, Status};
|
|
|
|
|
|
|
|
use serde::{Serialize, Deserialize};
|
|
|
|
|
|
|
|
pub use self::rmp_serde::decode::Error as MsgPackError;
|
|
|
|
|
2017-03-24 05:41:42 +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
|
|
|
|
/// implement `Deserialize` from [Serde](https://github.com/serde-rs/serde). The
|
|
|
|
/// data is parsed from the HTTP request body.
|
2017-02-08 03:40:14 +00:00
|
|
|
///
|
|
|
|
/// ```rust,ignore
|
|
|
|
/// #[post("/users/", format = "application/msgpack", data = "<user>")]
|
|
|
|
/// fn new_user(user: MsgPack<User>) {
|
|
|
|
/// ...
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
2017-03-24 05:41:42 +00:00
|
|
|
/// You don't _need_ to use `format = "application/msgpack"`, but it _may_ be
|
|
|
|
/// what you want. Using `format = application/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. By default, Rocket will
|
|
|
|
/// accept a Content Type of any of the following for MessagePack data:
|
|
|
|
/// `application/msgpack`, `application/x-msgpack`, `bin/msgpack`, or
|
|
|
|
/// `bin/x-msgpack`.
|
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,
|
|
|
|
/// where `T` implements `Serialize` from
|
|
|
|
/// [Serde](https://github.com/serde-rs/serde). The content type of the response
|
|
|
|
/// is set to `application/msgpack` automatically.
|
2017-02-08 03:40:14 +00:00
|
|
|
///
|
|
|
|
/// ```rust,ignore
|
|
|
|
/// #[get("/users/<id>")]
|
|
|
|
/// fn user(id: usize) -> MsgPack<User> {
|
|
|
|
/// let user_from_id = User::from(id);
|
|
|
|
/// ...
|
|
|
|
/// 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
|
|
|
|
/// # use rocket_contrib::MsgPack;
|
|
|
|
/// 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
|
|
|
|
2017-03-24 05:41:42 +00:00
|
|
|
/// Accepted content types are: `application/msgpack`, `application/x-msgpack`,
|
|
|
|
/// `bin/msgpack`, and `bin/x-msgpack`.
|
|
|
|
#[inline(always)]
|
2017-02-08 03:40:14 +00:00
|
|
|
fn is_msgpack_content_type(ct: &ContentType) -> bool {
|
2017-03-24 05:41:42 +00:00
|
|
|
(ct.top() == "application" || ct.top() == "bin")
|
|
|
|
&& (ct.sub() == "msgpack" || ct.sub() == "x-msgpack")
|
2017-02-08 03:40:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Deserialize> FromData for MsgPack<T> {
|
|
|
|
type Error = MsgPackError;
|
|
|
|
|
|
|
|
fn from_data(request: &Request, data: Data) -> data::Outcome<Self, Self::Error> {
|
|
|
|
if !request.content_type().map_or(false, |ct| is_msgpack_content_type(&ct)) {
|
|
|
|
error_!("Content-Type is not MessagePack.");
|
|
|
|
return Outcome::Forward(data);
|
|
|
|
}
|
|
|
|
|
2017-04-18 07:25:13 +00:00
|
|
|
let size_limit = config::active()
|
|
|
|
.and_then(|c| c.limits.get("msgpack"))
|
|
|
|
.unwrap_or(LIMIT);
|
|
|
|
|
2017-02-08 03:40:14 +00:00
|
|
|
let mut buf = Vec::new();
|
2017-04-18 07:25:13 +00:00
|
|
|
if let Err(e) = data.open().take(size_limit).read_to_end(&mut buf) {
|
2017-02-08 03:40:14 +00:00
|
|
|
let e = MsgPackError::InvalidDataRead(e);
|
|
|
|
error_!("Couldn't read request data: {:?}", e);
|
|
|
|
return Outcome::Failure((Status::BadRequest, e));
|
|
|
|
};
|
|
|
|
|
|
|
|
match rmp_serde::from_slice(&buf).map(|val| MsgPack(val)) {
|
|
|
|
Ok(value) => Outcome::Success(value),
|
|
|
|
Err(e) => {
|
|
|
|
error_!("Couldn't parse MessagePack body: {:?}", e);
|
|
|
|
Outcome::Failure((Status::BadRequest, e))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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> {
|
|
|
|
fn respond(self) -> response::Result<'static> {
|
|
|
|
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)]
|
2017-02-08 03:40:14 +00:00
|
|
|
fn deref<'a>(&'a self) -> &'a T {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> DerefMut for MsgPack<T> {
|
2017-03-24 05:41:42 +00:00
|
|
|
#[inline(always)]
|
2017-02-08 03:40:14 +00:00
|
|
|
fn deref_mut<'a>(&'a mut self) -> &'a mut T {
|
|
|
|
&mut self.0
|
|
|
|
}
|
|
|
|
}
|