#![feature(plugin)]
#![plugin(rocket_codegen)]
extern crate digest_headers;
extern crate rocket;
use std::error::Error as StdError;
use std::fmt;
use std::io::Read;
use digest_headers::use_rocket::{ContentLengthHeader, DigestHeader, Error as DigestError};
use rocket::data::{self, Data, FromData};
use rocket::request::Request;
use rocket::Outcome;
use rocket::http::Status;
#[derive(Clone, Debug)]
enum Error {
Digest(DigestError),
RequestTooBig,
ReadFailed,
DigestMismatch,
}
impl From<DigestError> for Error {
fn from(e: DigestError) -> Self {
Error::Digest(e)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Digest(ref de) => write!(f, "DigestError {}", de),
_ => write!(f, "{}", self.description()),
}
}
}
impl StdError for Error {
fn description(&self) -> &str {
match *self {
Error::Digest(ref e) => e.description(),
Error::RequestTooBig => "Request too big to process",
Error::ReadFailed => "Unable to read request body",
Error::DigestMismatch => "The provided digest does not match the body",
}
}
fn cause(&self) -> Option<&StdError> {
match *self {
Error::Digest(ref e) => Some(e),
_ => None,
}
}
}
struct DigestVerifiedBody<T>(pub T);
impl FromData for DigestVerifiedBody<Vec<u8>> {
type Error = Error;
fn from_data(req: &Request, data: Data) -> data::Outcome<Self, Self::Error> {
let digest = match req.guard::<DigestHeader>() {
Outcome::Success(dh) => dh.0,
Outcome::Failure((status, error)) => return Outcome::Failure((status, error.into())),
Outcome::Forward(_) => return Outcome::Forward(data),
};
println!("Provided Digest: {:?}", digest);
let content_length = match req.guard::<ContentLengthHeader>() {
Outcome::Success(clh) => clh.0,
Outcome::Failure((status, error)) => return Outcome::Failure((status, error.into())),
Outcome::Forward(_) => return Outcome::Forward(data),
};
println!("Content Length: {}", content_length);
if content_length > 1024 * 1024 * 2 {
return Outcome::Failure((Status::BadRequest, Error::RequestTooBig));
}
let mut body = vec![0u8; content_length];
if data.open().read_exact(&mut body).is_err() {
return Outcome::Failure((Status::InternalServerError, Error::ReadFailed));
}
if digest.verify(&body).is_err() {
return Outcome::Failure((Status::BadRequest, Error::DigestMismatch));
}
Outcome::Success(DigestVerifiedBody(body))
}
}
#[post("/", data = "<data>")]
fn index(data: DigestVerifiedBody<Vec<u8>>) -> String {
let inner = data.0;
if let Ok(data) = std::str::from_utf8(&inner) {
println!("Received {}", data);
format!("Verified!: {}", data)
} else {
"Failed to verify data".to_owned()
}
}
fn main() {
rocket::ignite().mount("/", routes![index]).launch();
}