Digest Headers
A library to aid in the creation and verification of Digest Headers, Sha Digests of HTTP Request
Bodies
crates.io
documentation
Getting started
Add the following to your Cargo.toml
[dependencies.digest-headers]
version = "0.2"
Available Features
use_actix_web
use_hyper
use_reqwest
use_rocket
Usage
Here's some basic usage with all of the supported frameworks.
Building requests with Actix Web
#[derive(Debug, Fail)]
#[fail(display = "Could not build request")]
pub struct RequestBuildError;
fn main() -> Result<(), Error> {
let json = r#"{"library":"hyper"}"#;
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(())
}
Handling requests with Actix Web
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();
}
Building requests with Hyper
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(|body| {
if let Ok(body) = String::from_utf8(body.to_vec()) {
println!("Verified response: {}", body);
} else {
println!("Verified resposne");
}
})
.map_err(|_| ())
});
hyper::rt::run(post)
Handling requests with Hyper
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(|body| {
println!("Verified!");
Response::builder()
.with_digest(body, ShaSize::TwoFiftySix)
.map_err(|_| SomeError)
});
Box::new(fut)
}
Building requests with Reqwest
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");
}
Handling requests with Rocket
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];
if content_length > 1024 * 1024 * 2 {
return Outcome::Failure((Status::BadRequest, Error::RequestTooBig));
}
println!("Content Length: {}", 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(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()
}
What this library supports
- Creation of Digest Header strings
- Verification of Digest Header strings
- Adding Digest Headers to Requests and Responses for various libraries.
Examples
Notes
- The Actix Web Client and Server examples are configured to work with each other.
- The Hyper Client and Server examples are configured to work with each other.
- The Rocket and Reqwest examples are configured to work with each other.
Contributing
Please be aware that all code contributed to this project will be licensed under the GPL version 3.
License
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/.