我正在尝试将 HTTPS 强制添加到 GKE 上基于 Warp 的 Web 应用程序中。
GKE 平台大多是无关紧要的;最重要的细节是负载均衡器终止 SSL/TLS 连接,因此X-Forwarded-Proto
标题中提供了“真实”方案。Warp 解析的文字方案将始终为HTTP
.
逻辑如下:
- 如果方案是
HTTPS
,则正常处理请求。 - 如果方案是,则向等效URL
HTTP
发送 301 重定向。HTTPS
- 如果该方案是其他任何内容,请发送 421(错误定向请求)错误。
- 如果
X-Forwarded-Proto
标头丢失(或发生任何其他实际不可能的情况),则发送 400(错误请求)错误。
此示例中的错误响应没有正文内容,所有 HTTPS 请求都应以 text 响应Hello, world!
。
问题:
error[E0277]: the trait bound `std::result::Result<(), warp::reject::Rejection>: core::future::future::Future` is not satisfied
--> src/main.rs:23:10
|
23 | .and_then(|scheme_header: Option<String>, host: String, path: FullPath| {
| ^^^^^^^^ the trait `core::future::future::Future` is not implemented for `std::result::Result<(), warp::reject::Rejection>`
|
= note: required because of the requirements on the impl of `futures_core::future::TryFuture` for `std::result::Result<(), warp::reject::Rejection>`
error[E0599]: no method named `and` found for type `warp::filter::and_then::AndThen<warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, impl warp::filter::Filter+std::marker::Copy>, impl warp::filter::Filter+std::marker::Copy>, [closure@src/main.rs:23:19: 43:10]>` in the current scope
--> src/main.rs:44:10
|
44 | .and(filter)
| ^^^ method not found in `warp::filter::and_then::AndThen<warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, impl warp::filter::Filter+std::marker::Copy>, impl warp::filter::Filter+std::marker::Copy>, [closure@src/main.rs:23:19: 43:10]>`
|
= note: the method `and` exists but the following trait bounds were not satisfied:
`&mut warp::filter::and_then::AndThen<warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, impl warp::filter::Filter+std::marker::Copy>, impl warp::filter::Filter+std::marker::Copy>, [closure@src/main.rs:23:19: 43:10]> : warp::filter::Filter`
`&warp::filter::and_then::AndThen<warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, impl warp::filter::Filter+std::marker::Copy>, impl warp::filter::Filter+std::marker::Copy>, [closure@src/main.rs:23:19: 43:10]> : warp::filter::Filter`
`warp::filter::and_then::AndThen<warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, impl warp::filter::Filter+std::marker::Copy>, impl warp::filter::Filter+std::marker::Copy>, [closure@src/main.rs:23:19: 43:10]> : warp::filter::Filter`
显然我在这里遗漏了一些明显的东西,所以我希望有人能把我推向正确的方向!
use futures::{FutureExt, StreamExt};
use warp::{Filter, Rejection};
use warp::filters::path::{FullPath};
use warp::http::{StatusCode, Uri};
use warp::http::uri::{Parts, Scheme};
use warp::reply::Reply;
enum SchemeError {
InsecureScheme(Uri),
UnknownScheme,
MissingScheme,
}
impl warp::reject::Reject for SchemeError {}
async fn requires_https(filter: impl Filter<Extract = (Scheme,), Error = Rejection> + Copy) -> impl Filter<Extract = (), Error = Rejection> + Copy {
warp::header::optional("X-Forwarded-Proto")
.and(warp::header("Host"))
.and(warp::path::full())
.and_then(|scheme_header: Option<String>, host: String, path: FullPath| {
if let Some(scheme) = scheme_header {
match scheme.to_ascii_lowercase().as_str() {
"https" => Ok(()),
"http" => {
let mut uri_parts = Parts::default();
uri_parts.scheme = Some(Scheme::HTTPS);
uri_parts.authority = Some(host.parse().unwrap());
uri_parts.path_and_query = Some(path.as_str().parse().unwrap());
let uri_parts = uri_parts;
let new_uri = Uri::from_parts(uri_parts).unwrap();
println!("Redirecting to secure URL: {}", new_uri);
Err(warp::reject::custom(SchemeError::InsecureScheme(new_uri)))
},
_ => Err(warp::reject::custom(SchemeError::UnknownScheme)),
}
} else {
Err(warp::reject::custom(SchemeError::MissingScheme))
}
})
.and(filter)
.recover(|err: Rejection| {
if let Some(scheme_error) = err.find::<SchemeError>() {
match scheme_error {
SchemeError::InsecureScheme(new_uri) => Ok(warp::redirect(new_uri)),
SchemeError::UnknownScheme => Ok(StatusCode::MISDIRECTED_REQUEST),
SchemeError::MissingScheme => Ok(StatusCode::BAD_REQUEST),
}
} else {
Err(err)
}
})
}
#[tokio::main]
async fn main() {
let routes = requires_https(warp::any().map(|| "Hello, world!"));
warp::serve(routes)
.run(([0, 0, 0, 0], 8080))
.await;
}