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 服务器
49KB
818 行
摘要头部
一个库,帮助创建和验证摘要头部,HTTP 请求体的 Sha 摘要
入门
将以下内容添加到您的 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 客户端示例
- Actix 服务器示例
- Hyper 客户端示例
- Hyper 服务器示例
- 请求客户端示例
- Rocket 服务器示例。这个示例更为深入。它为
rocket::request::FromRequest
实现了两个自定义结构体,用于Digest
和ContentLength
头部,并为一个简单的包装器实现了FromData
,该包装器围绕一个Vec<u8>
。请参阅示例以获取完整实现。
注意
- Actix Web 客户端和服务器示例配置为相互工作。
- Hyper 客户端和服务器示例配置为相互工作。
- Rocket 和 Reqwest 示例配置为相互工作。
贡献
请注意,此项目所有贡献的代码都将根据 GPL 版本 3 许可。
许可证
摘要头是免费软件:您可以在自由软件基金会发布的GNU通用公共许可证的条款下重新分发和/或修改它,无论是许可证的第3版,还是(根据您的选择)任何后续版本。
摘要头是为了希望它将是有用的而分发的,但没有任何保证;甚至没有对适销性或特定用途适用性的暗示性保证。有关更多详细信息,请参阅GNU通用公共许可证。本文件是摘要头的一部分
您应该已收到GNU通用公共许可证的副本,与摘要头一起。如果没有,请参阅http://www.gnu.org/licenses/。
依赖项
~6–12MB
~326K SLoC