#header #request-headers #http-request #http-header #http #web #actix-web

digest-headers

一个简单的库,用于在头部哈希请求体

5 个版本

0.2.1 2019年1月12日
0.2.0 2019年1月12日
0.1.2 2018年1月15日
0.1.1 2018年1月14日
0.1.0 2018年1月13日

#1114 in HTTP 服务器

GPL-3.0 许可证

49KB
818

摘要头部

一个库,帮助创建和验证摘要头部,HTTP 请求体的 Sha 摘要

crates.io 文档

入门

将以下内容添加到您的 Cargo.toml

[dependencies.digest-headers]
version = "0.2"

可用特性

  • use_actix_web
  • use_hyper
  • use_reqwest
  • use_rocket

使用

以下是一些使用所有支持框架的基本用法。

使用 Actix Web 构建请求

#[derive(Debug, Fail)]
#[fail(display = "Could not build request")]
pub struct RequestBuildError;

fn main() -> Result<(), Error> {
    let json = r#"{"library":"actix"}"#;

    let req = post("https://127.0.0.1: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(())
}

使用 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();
}

使用 Hyper 构建请求

use digest_headers::{prelude::*, ShaSize};

let client = Client::new();

let uri = "https://127.0.0.1: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)

使用 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(|_req| {
        println!("Verified!");
        Response::builder()
            .with_digest("Verified", ShaSize::TwoFiftySix)
            .map_err(|_| SomeError)
    });

    Box::new(fut)
}

使用 Reqwest 构建请求

use digest_headers::{prelude::*, ShaSize};
use reqwest::Client;

let payload = r#"{"Library":"Reqwest"}"#;
let client = Client::new();
let req = client
    .post("https://127.0.0.1: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");
}

使用 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];
                // 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()
}

此库支持的内容

  • 创建摘要头部字符串
  • 验证摘要头部字符串
  • 为各种库添加摘要头部到请求和响应。

示例

注意

  • Actix Web 客户端和服务器示例配置为相互工作。
  • Hyper 客户端和服务器示例配置为相互工作。
  • Rocket 和 Reqwest 示例配置为相互工作。

贡献

请注意,此项目所有贡献的代码都将根据 GPL 版本 3 许可。

许可证

摘要头是免费软件:您可以在自由软件基金会发布的GNU通用公共许可证的条款下重新分发和/或修改它,无论是许可证的第3版,还是(根据您的选择)任何后续版本。

摘要头是为了希望它将是有用的而分发的,但没有任何保证;甚至没有对适销性或特定用途适用性的暗示性保证。有关更多详细信息,请参阅GNU通用公共许可证。本文件是摘要头的一部分

您应该已收到GNU通用公共许可证的副本,与摘要头一起。如果没有,请参阅http://www.gnu.org/licenses/

依赖项

~6–12MB
~326K SLoC