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");
}

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

  1. spring boot 笔记--第三章

    spring boot 笔记 第三章,使用Spring boot 构建系统: 强烈建议支持依赖管理的构建系统,Maven或Gradle 依赖管理: Spring Boot的每版本都会提供它支持的依赖列 ...

  2. 携程Apollo(阿波罗)配置中心Spring Boot迁移日志组件,使用配置中心进行管理的思路

    说明: 1.Spring Boot项目默认使用logback进行日志管理 2.logback在启动时默认会自动检查是否有logback.xml文件,如果有时会有限加载这个文件. 3.那么如果是用配置中 ...

  3. 《spring boot》8.2章学习时无法正常启动,报“ORA-00942: 表或视图不存在 ”

    在学习<spring boot>一书的过程中,由于原书作者难免有一些遗漏的的地方,或者系统.软件版本不一致.框架更新等各种因素,完全安装书中源码页不能实现项目的正常启动 在8.2章节,演示 ...

  4. Spring Boot 2.0 迁移指南

    ![img](https://mmbiz.qpic.cn/mmbiz_jpg/1flHOHZw6Rs7yEJ6ItV43JZMS7AJWoMSZtxicnG0iaE0AvpUHI8oM7lxz1rRs ...

  5. 《深入实践Spring Boot》阅读笔记之三:核心技术源代码分析

    刚关注的朋友,可以回顾前两篇文章: 基础应用开发 分布式应用开发 上篇文章总结了<深入实践Spring Boot>的第二部分,本篇文章总结第三部分,也是最后一部分.这部分主要讲解核心技术的 ...

  6. 《Spring Boot实战》笔记(目录)

    目录 目 录第一部分 点睛Spring 4.x第1 章 Spring 基础 .............................................................. ...

  7. 【原】spring boot在整合项目依赖的问题

    最近要开发新的项目,就花了几天时间看了下spring boot的相关资料,然后做了一个demo,不得不说开发效率确实很快,几行注解就完成了事务,aop,数据库等相关配置:但由于先前习惯了spring ...

  8. 为什么是Spring Boot

    原文:https://dzone.com/articles/why-springboot 作者:Siva Prasad Reddy Katamreddy 译者:Oopsguy 本文介绍将各种Sprin ...

  9. Spring Boot快速入门(最新)

    本章通过完成Spring Boot基础项目的构建并实现一个简单的Http请求处理,让大家对Spring Boot有一个初步的了解,并体验其结构简单.开发快速的特性.预计阅读及演练过程将花费约5分钟. ...

  10. Spring Boot 2.0系列文章(五):Spring Boot 2.0 项目源码结构预览

    关注我 转载请务必注明原创地址为:http://www.54tianzhisheng.cn/2018/04/15/springboot2_code/ 项目结构 结构分析: Spring-boot-pr ...

随机推荐

  1. 【Java基础总结】集合框架

    集合和数组的区别 集合只存储对象,长度是可变的: 数组既可以存储基本数据类型,又可以存储对象,但长度是固定的. 1. Collection接口 代码演示 1 List<String> c1 ...

  2. 开源一款串口舵机驱动扩展板-FreakStudio多米诺系列

    原文链接: FreakStudio的博客 摘要 总线舵机扩展板通过UART接口控制多个舵机,支持堆叠级联,最多连接4个扩展板.具备小尺寸设计.供电保护.全双工转半双工通信.稳定供电等特点,适用于多舵机 ...

  3. 新版Edge 浏览器几种详细的卸载方法

    一.使用Windows系统自带卸载程序卸载 1.打开"控制面板"(可在Windows搜索栏中搜索): 2.选择"程序和功能": 3.找到Microsoft Ed ...

  4. @autowired注解报错原因及解决办法

    @autowired 注入dao层的时候,标红报错,但不影响编译使用 按照严格的spring注解方式在dao层加入@Repository注解

  5. 支付宝 IoT 设备入门宝典(下)设备经营篇

    上篇介绍了支付宝 IoT 设备管理,但除了这些基础功能外,商户还可以利用设备进行一些运营动作,让设备更好的帮助自己,本篇就会以设备经营为中心,介绍常见的设备相关能力和问题解决方案.如果对上篇感兴趣,可 ...

  6. 【Azure Storage Account】利用App Service作为反向代理, 并使用.NET Storage Account SDK实现上传/下载操作

    问题描述 在使用Azure上的存储服务 Storage Account 的时候,有时需要代替 它原本提供的域名进行访问,比如默认的域名为:mystorageaccount.blob.core.chin ...

  7. 【日常运维笔记】linux系统使用grep命令查找文件,并用vim编辑文件

    问题描述:linux系统中查找含有某个字符的文件,进行编辑修改 1.使用grep命令查找到符合条件的文件 命令格式:grep  '匹配内容'  文件路径  显示方式(-r  -n) -i:忽略大小写进 ...

  8. Appflowy cloud 部署测试避坑指南

    在进行 Appflowy cloud 部署测试时,我可谓是踩坑无数.下面,我想从几个关键方面来分享一下我的经验. 先给大家讲讲我的基础情况.Appflowy cloud 的部署是在 docker 环境 ...

  9. [评测/调研/AIGC/流媒体] 视频内容自动生成摘要工具

    概述:视频内容自动生成摘要工具 SolidPoint | 仅支持 简介 SolidPoint 是一款AI驱动的在线视频摘要工具,专注于自动生成YouTube视频的简洁摘要. 通过分析视频内容提取关键点 ...

  10. Ctfhub-SSRF部分做题记录

    Ctfhub-SSRF部分做题记录 上传文件 提示:这次需要上传一个文件到flag.php了.祝你好运 进入flag.php 发现没有提交按钮 修改源代码,加个提交按钮 抓包 修改host为127.0 ...