digest-headers 0.2.1

A simple library to hash a request's body in the headers
Documentation
# Digest Headers
_A library to aid in the creation and verification of Digest Headers, Sha Digests of HTTP Request
Bodies_

[crates.io](https://crates.io/crates/digest-headers)
[documentation](https://docs.rs/digest-headers)

## Getting started
Add the following to your `Cargo.toml`
```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
```rust
#[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(())
}
```
### Handling requests with Actix Web
```rust
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
```rust
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)
```
### Handling requests with Hyper
```rust
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)
}
```
### Building requests with Reqwest
```rust
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
```rust
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()
}
```

## 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
 - [Actix Client example]https://git.asonix.dog/asonix/digest-headers/src/branch/master/examples/actix-web-client/src/main.rs
 - [Actix Server example]https://git.asonix.dog/asonix/digest-headers/src/branch/master/examples/actix-web-server/src/main.rs
 - [Hyper Client example]https://git.asonix.dog/asonix/digest-headers/src/branch/master/examples/hyper-client/src/main.rs
 - [Hyper Server example]https://git.asonix.dog/asonix/digest-headers/src/branch/master/examples/hyper-server/src/main.rs
 - [Request Client example]https://git.asonix.dog/asonix/digest-headers/src/branch/master/examples/reqwest-client/src/main.rs
 - [Rocket Server Example]https://git.asonix.dog/asonix/digest-headers/src/branch/master/examples/rocket-server/src/main.rs.
    This one is more in-depth. It implements `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.

### 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/.