2020-12-27 21:03:10 +00:00
|
|
|
use crate::errors::Error;
|
|
|
|
use rocket::data::{Data, FromData, Outcome, ToByteUnit};
|
|
|
|
use rocket::request::Request;
|
|
|
|
use std::default::Default;
|
|
|
|
use std::ops::Deref;
|
|
|
|
|
2020-12-31 22:21:05 +00:00
|
|
|
pub mod cofd;
|
2020-12-27 21:03:10 +00:00
|
|
|
|
2021-01-02 14:51:24 +00:00
|
|
|
const CRATE_NAME: &'static str = env!("CARGO_BIN_NAME");
|
|
|
|
|
|
|
|
/// Convert an incoming protobuf content-type to the equivalent type
|
|
|
|
/// name produced by std::any::type_name(). Currently does NOT work
|
|
|
|
/// with nested types due to how prost generates the module names.
|
|
|
|
fn convert_to_rust_name(proto_type: &str) -> String {
|
|
|
|
format!("{}::{}", CRATE_NAME, proto_type.replace(".", "::"))
|
|
|
|
}
|
|
|
|
|
2020-12-27 21:03:10 +00:00
|
|
|
/// A struct wrapping a protobuf that allows it to be used as binary
|
|
|
|
/// data submitted via POST using fetch API. Can automatically be
|
|
|
|
/// dereferenced into its wrapped type.
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub(crate) struct Proto<T>(T)
|
|
|
|
where
|
|
|
|
T: prost::Message + Default;
|
|
|
|
|
|
|
|
/// Converts the body of a POST request containing encoded protobuf
|
|
|
|
/// data into the wrapped type.
|
|
|
|
#[rocket::async_trait]
|
|
|
|
impl<T> FromData for Proto<T>
|
|
|
|
where
|
|
|
|
T: prost::Message + Default,
|
|
|
|
{
|
|
|
|
type Error = crate::errors::Error;
|
|
|
|
|
2021-01-02 14:51:24 +00:00
|
|
|
async fn from_data(req: &Request<'_>, data: Data) -> Outcome<Self, Error> {
|
2020-12-27 21:03:10 +00:00
|
|
|
use rocket::http::Status;
|
2021-01-02 14:51:24 +00:00
|
|
|
let content_type = req.content_type();
|
|
|
|
|
|
|
|
let is_protobuf = content_type
|
|
|
|
.map(|ct| ct.top() == "application" && ct.sub() == "x-protobuf")
|
|
|
|
.unwrap_or(false);
|
|
|
|
|
|
|
|
let message_type: Option<String> = content_type.and_then(|ct| {
|
|
|
|
ct.params()
|
|
|
|
.find(|&(name, _)| name == "messageType")
|
|
|
|
.map(|(_, message_type)| convert_to_rust_name(message_type))
|
|
|
|
});
|
|
|
|
|
|
|
|
if !is_protobuf {
|
|
|
|
return Outcome::Failure((Status::new(422, "invalid protobuf"), Error::InvalidInput));
|
|
|
|
}
|
|
|
|
|
|
|
|
if message_type.as_ref().map(String::as_str) != Some(std::any::type_name::<T>()) {
|
|
|
|
return Outcome::Forward(data);
|
|
|
|
}
|
2020-12-27 21:03:10 +00:00
|
|
|
|
|
|
|
let bytes: Vec<u8> = match data.open(2.mebibytes()).stream_to_vec().await {
|
|
|
|
Ok(read_bytes) => read_bytes,
|
|
|
|
Err(e) => return Outcome::Failure((Status::new(422, "invalid protobuf"), e.into())),
|
|
|
|
};
|
|
|
|
|
|
|
|
match T::decode(bytes.as_ref()) {
|
|
|
|
Ok(decoded) => Outcome::Success(Proto(decoded)),
|
|
|
|
Err(e) => Outcome::Failure((Status::new(422, "invalid protobuf"), e.into())),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-31 22:21:05 +00:00
|
|
|
/// Enable automatically calling methods on a decoded Proto instance.
|
2020-12-27 21:03:10 +00:00
|
|
|
impl<T> Deref for Proto<T>
|
|
|
|
where
|
|
|
|
T: prost::Message + Default,
|
|
|
|
{
|
|
|
|
type Target = T;
|
|
|
|
|
|
|
|
fn deref(&self) -> &T {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|