Crates.io | digest-headers |
lib.rs | digest-headers |
version | 0.2.1 |
source | src |
created_at | 2018-01-13 23:03:46.307329 |
updated_at | 2019-01-12 18:19:59.80106 |
description | A simple library to hash a request's body in the headers |
homepage | |
repository | https://git.asonix.dog/asonix/digest-headers |
max_upload_size | |
id | 46714 |
size | 84,876 |
A library to aid in the creation and verification of Digest Headers, Sha Digests of HTTP Request Bodies
Add the following to your Cargo.toml
[dependencies.digest-headers]
version = "0.2"
use_actix_web
use_hyper
use_reqwest
use_rocket
Here's some basic usage with all of the supported frameworks.
#[derive(Debug, Fail)]
#[fail(display = "Could not build request")]
pub struct RequestBuildError;
fn main() -> Result<(), Error> {
let json = r#"{"library":"actix"}"#;
let req = post("http://localhost:5000")
.content_type("application/json")
.with_digest(json, ShaSize::TwoFiftySix)
.map_err(|_| RequestBuildError)?;
actix_web::actix::run(move || {
req.send()
.map_err(|_| ())
.and_then(|res| {
println!("POST: {}", res.status());
res.verify_digest()
.map(|ActixWebVerifiedDigest| println!("Verified response!"))
.map_err(|_| ())
})
.map(|_| {
actix_web::actix::System::current().stop();
})
});
Ok(())
}
use actix_web::{server, App, HttpResponse};
use digest_headers::{prelude::*, ShaSize};
const PHRASE: &str = "Hewwo, Mr. Obama???";
fn index(_: ActixWebVerifiedDigest) -> HttpResponse {
println!("Verified request!");
HttpResponse::Ok()
.content_type("text/plain")
.force_close()
.with_digest(PHRASE, ShaSize::TwoFiftySix)
}
fn main() {
server::new(move || App::new().resource("/", |r| r.with(index)))
.bind("127.0.0.1:5000")
.expect("Can not bind to port 5000")
.run();
}
use digest_headers::{prelude::*, ShaSize};
let client = Client::new();
let uri = "http://localhost:8000";
let json = r#"{"Library":"Hyper"}"#;
let req = Request::post(uri)
.header(CONTENT_TYPE, "application/json")
.header(CONNECTION, "close")
.with_digest(json, ShaSize::TwoFiftySix)
.unwrap();
let post = client.request(req).map_err(|_| ()).and_then(|res| {
println!("POST: {}", res.status());
res.verify_digest()
.map(|_req| {
println!("Verified resposne");
})
.map_err(|_| ())
});
hyper::rt::run(post)
use digest_headers::{prelude::*, ShaSize};
use futures::Future;
use hyper::{service::service_fn, Body, Request, Response, Server};
type BoxResponse = Box<Future<Item = Response<Body>, Error = SomeError> + Send>;
fn verify_digest(req: Request<Body>) -> BoxResponse {
let fut = req.verify_digest().map_err(|_| SomeError).and_then(|_req| {
println!("Verified!");
Response::builder()
.with_digest("Verified", ShaSize::TwoFiftySix)
.map_err(|_| SomeError)
});
Box::new(fut)
}
use digest_headers::{prelude::*, ShaSize};
use reqwest::Client;
let payload = r#"{"Library":"Reqwest"}"#;
let client = Client::new();
let req = client
.post("http://localhost:8000")
.with_digest(payload, ShaSize::TwoFiftySix)
.build()
.unwrap();
let mut res = client.execute(req).unwrap();
println!("GET: {}", res.status());
let body = res.verify_digest().unwrap();
if let Ok(body) = std::str::from_utf8(&body) {
println!("Verified, {}", body);
} else {
println!("Verified");
}
use digest_headers::{
use_rocket::{ContentLengthHeader, DigestHeader, Error as DigestError, WithDigest},
ShaSize,
};
use rocket::{
config::{Config, Environment},
data::{self, Data, FromData},
http::Status,
request::Request,
response::Response,
Outcome,
};
struct DigestVerifiedBody<T>(pub T);
impl<'a> FromData<'a> for DigestVerifiedBody<Vec<u8>> {
type Owned = Vec<u8>;
type Borrowed = Vec<u8>;
type Error = Error;
fn transform(
req: &Request,
data: Data,
) -> data::Transform<data::Outcome<Self::Owned, Self::Error>> {
let outcome = req
.guard::<DigestHeader>()
.map(|digest_header| digest_header.0)
.and_then(move |digest| {
req.guard::<ContentLengthHeader>()
.map(|content_length_header| (digest, content_length_header.0))
})
.map_failure(|(s, e)| (s, e.into()))
.and_then(move |(digest, content_length)| {
println!("Provided Digest: {:?}", digest);
let mut body = vec![0u8; content_length];
// Ensure request is less than 2 MB. This is still likely way too large
if content_length > 1024 * 1024 * 2 {
return Outcome::Failure((Status::BadRequest, Error::RequestTooBig));
}
println!("Content Length: {}", content_length);
// Only read as much data as we expect to avoid DOS
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(body)
});
let outcome = match outcome {
Outcome::Success(s) => Outcome::Success(s),
Outcome::Forward(_) => Outcome::Failure((Status::BadRequest, Error::ReadFailed)),
Outcome::Failure(f) => Outcome::Failure(f),
};
data::Transform::Borrowed(outcome)
}
fn from_data(
_: &Request,
outcome: data::Transformed<'a, Self>,
) -> data::Outcome<Self, Self::Error> {
let body = outcome.borrowed()?;
Outcome::Success(DigestVerifiedBody(body.to_vec()))
}
}
#[post("/", data = "<data>")]
fn index(data: DigestVerifiedBody<Vec<u8>>) -> Response<'static> {
let inner = data.0;
if let Ok(data) = std::str::from_utf8(&inner) {
println!("Verified {}", data);
} else {
println!("Verified");
}
Response::build()
.with_digest(Cursor::new("woah"), ShaSize::TwoFiftySix)
.unwrap()
.finalize()
}
rocket::request::FromRequest
for two custom structs for the Digest
and ContentLength
headers, and implements FromData
for a simple wrapper around a Vec<u8>
. See the example for the full implementation.Please be aware that all code contributed to this project will be licensed under the GPL version 3.
Digest Headers is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
Digest Headers is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. This file is part of Digest Headers
You should have received a copy of the GNU General Public License along with Digest Headers If not, see http://www.gnu.org/licenses/.