spring boot迁移计划 第Ⅰ章 --chapter 1. rust hyper 结合rust nacos-client开发nacos网关 part ② hyper网关

1. toml依赖

hyper = { version = "1", features = ["full"] }
tokio = { version = "1", features = ["full"] }
http-body-util = "0.1"
hyper-util = { version = "0.1", features = ["full"] }

2. 代码

2025-02-17更新:更新跨域设置;
2025-01-24更新:更新跨域设置,符合RFC标准,参考spring boot的处理方式;
2025-01-23更新:新增跨域设置;
2025-01-17更新:全局服务静态缓存添加,转发至实际请求微服务地址可用,由于nacos不广播服务权重变化,缓存的使用方法还在考虑,有人有好的想法可以留言告知,感谢;
题外话:在写代码的过程中发现每个函数的参数是否可变原来不具有继承性,比如下面的代码是可以运行的, retuen_new_user函数的参数user是不可变的,但是可以将它直接传递给一个要求可变参数的函数,还真是奇怪嘿XD

pub fn retuen_new_user(user: User) -> User {
handle(user)
}
pub fn handle(mut user: User)-> User {
user.id = "11111".to_string;
user
}

2025-01-02更新:部分功能代码未完成后续完成后更新
以下代码参考hyper的官方example文件夹下的内容编写

use std::{net::SocketAddr, str};
use http_body_util::{combinators::BoxBody, BodyExt};
use hyper::{
body::{Bytes, Incoming},
header::{
HeaderValue, ACCESS_CONTROL_ALLOW_CREDENTIALS, ACCESS_CONTROL_ALLOW_HEADERS,
ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_MAX_AGE,
ACCESS_CONTROL_REQUEST_HEADERS, ACCESS_CONTROL_REQUEST_METHOD, ORIGIN, REFERER,
},
server::conn::http1,
service::service_fn,
HeaderMap, Method, Request, Response, StatusCode,
};
use hyper_util::rt::TokioIo;
use log::{error, info};
use tokio::{
io,
net::{TcpListener, TcpStream},
};
//由于nacos不广播权重配置的更改,而且nacos内置了raft和负载均衡算法所以改为每次去注册中心请求一个服务地址
//use crate::init::nacos::NAMING_MAP;
use crate::{
init::{self, config::Config, constant::AUTHORIZATION, nacos},
util::jwt,
};
pub async fn init_hyper() -> io::Result<()> {
//Config::global()获取一个LazyLock全局静态变量,仅支持rust 1.8.0以上版本
let server_ip = Config::global().server_ip();
let server_port = Config::global().server_port();
//创建soket
let addr: SocketAddr = format!("{}:{}", server_ip, server_port).parse().unwrap();
//创建监听器
let listener = TcpListener::bind(addr).await?;
info!("service listening on {}", addr);
//创建http监听
let http = http1::Builder::new();
//调用hyper的关闭监听
let graceful = hyper_util::server::graceful::GracefulShutdown::new();
let mut signal = std::pin::pin!(shutdown_signal());
loop {
//接受链接请求或者关闭信号
tokio::select! {
Ok((stream, _addr)) = listener.accept() => {
let io = TokioIo::new(stream);
let conn = http.serve_connection(io, service_fn(handle_proxy));
let fut = graceful.watch(conn);
tokio::spawn(async move {
if let Err(err) = fut.await {
error!("Error input connection: {}", err);
}
});
},
_ = &mut signal => {
info!("graceful shutdown signal received");
// stop the accept loop
break;
}
}
}
//监控关闭是否成功
tokio::select! {
_ = graceful.shutdown() => {
info!("all connections gracefully closed");
},
_ = tokio::time::sleep(std::time::Duration::from_secs(10)) => {
error!("timed out wait for all connections to close");
}
}
Ok(())
}
//预处理请求,鉴权等
async fn handle_proxy(
req: Request<hyper::body::Incoming>,
) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
//跨域相关
if req.method() == Method::OPTIONS {
return return_options(req.headers());
}
if req.uri().path().contains("login") || req.uri().path().contains("app/auth") {
proxy_to_service(req).await
} else if req.headers().contains_key(AUTHORIZATION) {
if is_valid_token(req.headers()) {
proxy_to_service(req).await
} else {
return_forbidden()
}
} else {
//鉴权不通过统统403
return_forbidden()
}
}
fn is_valid_token(headers: &HeaderMap<HeaderValue>) -> bool {
let token = headers.get(AUTHORIZATION);
if token.is_none() {
return false;
}
let token = token.unwrap().to_str().unwrap();
is_valid_user_token(token) || is_valid_app_token(token)
}
fn is_valid_app_token(token: &str, ip: &str) -> bool {
ture
}
fn is_valid_user_token(token: &str) -> bool {
ture
}
//转发至微服务地址
async fn proxy_to_service(
mut req: Request<Incoming>,
) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
let req_server_name = req.uri().path().split("/").collect::<Vec<&str>>()[1];
let service_addr: String;
// let map = NAMING_MAP.load();
// if map.contains_key(req_server_name) {
// let instances_list = map.get(req_server_name).expect("get server addr error");
// service_addr = instances_list[0].ip.clone() + ":" + &instances_list[0].port.to_string();
// } else {
service_addr = nacos::select_service(req_server_name).await;
if service_addr.is_empty() {
return return_not_found(&req.headers());
}
// }
let req_header = req.headers().clone();
info!(
"request uri : {} with X-Custom-Version {:?}",
req.uri(),
req_header.get("X-Custom-Version")
);
*req.uri_mut() = req
.uri()
.to_string()
.split(req_server_name)
.collect::<Vec<&str>>()[1]
.parse()
.unwrap();
let uri = req.uri().clone();
match TcpStream::connect(service_addr).await {
Ok(stream) => {
let io = TokioIo::new(stream);
match hyper::client::conn::http1::handshake(io).await {
Ok((mut sender, conn)) => {
tokio::task::spawn(async move {
if let Err(err) = conn.await {
error!("Connection failed: {}", err);
}
});
match sender.send_request(req).await {
Ok(mut res) => {
insert_cors_header(&req_header, res.headers_mut());
Ok(res.map(|b| b.boxed()))
}
Err(err) => {
error!("Error proxy sending request: {} when request {}", err, uri);
return_bad_gateway(&req_header)
}
}
}
Err(err) => {
error!("Error proxy handshanke: {} when request {}", err, uri);
return_bad_gateway(&req_header)
}
}
}
Err(err) => {
error!("Error proxy connection :{} when request {}", err, uri);
return_bad_gateway(&req_header)
}
}
}
//跨域头设置
fn insert_cors_header(request_header: &HeaderMap, response_header: &mut HeaderMap) {
match request_header.get(ORIGIN) {
Some(origin) => {
response_header.insert(ACCESS_CONTROL_ALLOW_ORIGIN, origin.clone());
response_header.insert(
ACCESS_CONTROL_ALLOW_CREDENTIALS,
HeaderValue::from_str("true").unwrap(),
);
}
None => (),
};
}
fn return_options(
request_header: &HeaderMap,
) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
let mut response = Response::new(BoxBody::default());
*response.status_mut() = StatusCode::NO_CONTENT;
insert_cors_header(request_header, response.headers_mut());
match request_header.get(ACCESS_CONTROL_REQUEST_METHOD) {
None => (),
Some(method) => {
response
.headers_mut()
.insert(ACCESS_CONTROL_ALLOW_METHODS, method.clone());
}
};
match request_header.get(ACCESS_CONTROL_REQUEST_HEADERS) {
None => (),
Some(method) => {
response
.headers_mut()
.insert(ACCESS_CONTROL_ALLOW_HEADERS, method.clone());
}
};
response
.headers_mut()
.insert(ACCESS_CONTROL_MAX_AGE, HeaderValue::from(3600));
Ok(response)
}
fn return_forbidden(request_header: &HeaderMap,) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
let mut response = Response::new(BoxBody::default());
*response.status_mut() = StatusCode::FORBIDDEN;
insert_cors_header(request_header, response.headers_mut());
Ok(response)
}
fn return_bad_gateway(request_header: &HeaderMap,) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
let mut response = Response::new(BoxBody::default());
*response.status_mut() = StatusCode::BAD_GATEWAY;
insert_cors_header(request_header, response.headers_mut());
Ok(response)
}
fn return_not_found(request_header: &HeaderMap,) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
let mut response = Response::new(BoxBody::default());
*response.status_mut() = StatusCode::NOT_FOUND;
insert_cors_header(request_header, response.headers_mut());
Ok(response)
}
//优雅的关闭tokio运行时任务
async fn shutdown_signal() {
// Wait for the CTRL+C signal
tokio::signal::ctrl_c()
.await
.expect("failed to install CTRL+C signal handler");
}
posted @   Jiajie6591  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 易语言 —— 开山篇
点击右上角即可分享
微信分享提示