26个版本
0.8.37 | 2024年4月22日 |
---|---|
0.8.36 | 2024年4月20日 |
0.8.35 | 2022年12月19日 |
0.8.33 | 2022年9月20日 |
0.8.13 | 2022年8月31日 |
#179 in HTTP服务器
每月下载量 184次
195KB
4K SLoC
rust-xfinal
这是一个由Rust编写的安全且高性能的Web服务器框架。
简介
这是用Rust编写一个安全Web服务器框架的开始。目前,这个仓库还没有提供完整和稳定的函数,因为xfinal已经完成了,由现代C++编写。目标是构建一个简单易用、性能好且安全的Web服务器框架。
优势
-
由于Rust的优势在于内存和多个线程上的安全,以及它是一种现代编程语言,几乎没有任何C++的累赘,基于Rust编写的框架无需担心内存和数据竞争的难题。Rust可以保证这些方面的安全性。
-
此外,Rust具有与C++类似的零成本抽象特性,因此框架的性能将符合您的期望,快速!
-
Rust有一个非常方便的包管理器:Cargo,它可以让您自由地使用任何第三方crate,而无需使用CMake来管理这些依赖项,甚至无需编写丑陋的规则。
已支持的功能
- 注册路由器。
- 注册中间件。
- 查询文本-plain正文、url-form数据和multipart-form数据(包括上传的文件)。
- 分块传输
- 接受范围请求,这意味着rust-xfinal支持断点续传下载文件。
- 基于tera的视图渲染
- 支持可恢复的cookie(即服务器重启后cookie仍然可以识别)
- 支持从中间件将用户数据传输到最终路由器
- 支持WebSocket
尚未支持的功能
- 似乎没有
用法
- HTTP "hello, world"
use http_server::{
end_point, EndPoint, HttpServer, Request, Response, GET,
};
fn main(){
let mut http_server = HttpServer::create(end_point!(0.0.0.0:8080), 10);
http_server.route(GET, "/").reg(|req: &Request, res: &mut Response| {
res.write_string("hello, world"); // default http status is 200, you can also specify it.
});
http_server.run();
}
- 使用中间件
use http_server::{
end_point, inject_middlewares, EndPoint, HttpServer, MiddleWare, Request, Response, GET,
};
fn main(){
let mut http_server = HttpServer::create(end_point!(0.0.0.0:8080), 10);
fn interrupt_second(req:& Request,res:&mut Response) ->bool{
println!("invoke middleware2");
match req.get_param("id") {
Some(v) => {
if v == "1"{
true
}else{
res.write_string("invalid request, invalid id value").status(400);
false
}
},
None => {
res.write_string("invalid request, no id").status(400);
false
},
}
}
let middlewares = inject_middlewares! {
|req:& Request,res:&mut Response|->bool{
println!("invoke middleware");
true
},interrupt_second //, middleware 3,..., so forth
};
http_server.route(GET, "/middle").reg_with_middlewares(
middlewares,
|req: &Request, res: &mut Response| {
println!("invoke router");
res.write_string("hello from router with middleware passed");
},
);
http_server.run();
}
- 从请求中查询信息
http_server.route(GET, "/query").reg(|req: &Request, res: &mut Response| {
let r:Option<&str> = req.get_query("id");
let host = req.get_header("Host");
let file = req.get_file("file");
let version = req.get_version();
let method = req.get_method();
let headers = req.get_headers();
let forms = req.get_queries();
let get_all_files = req.get_files();
let url = req.get_url();
let id = req.get_param("id"); // /a?id=0
res.write_string("ok").status(200);
});
- 分块传输和/或可范围
范围函数几乎需要与HEAD配对
http_server.route([GET,HEAD], "/query").reg(|_req: &Request, res: &mut Response| {
// file: res.write_file("./upload/test.mp4",200).chunked().enable_range();
//string: res.write_string("abcdefg",200).chunked();
// res.write_string("abcdefg",200).enable_range();
// file: res.write_file("./upload/test.mp4",200).enable_range().chunked();
// No sequence requirement, whatever combination as you go.
});
- 通配符路径
http_server.route(GET, "/wildcard/*").reg(|_req: &Request, res: &mut Response|{
let s = format!("hello from {}", req.get_url());
res.write_string(&s).status(200);
});
- 视图渲染
use rust_xfinal::{EndPoint,end_point,HttpServer,GET, Request,Response,tera};
fn render(ctx:&Request) -> tera::Result<String>{
let mut context = tera::Context::new();
context.insert("text",&ctx.get_url().to_string());
let t = tera::Tera::new("template/**/*")?; // customize your own tera whatever you want
return t.render("test.html",&context);
}
fn main() {
let mut server = HttpServer::create(end_point![0.0.0.0:8080], 10);
server.route(GET, "/").reg(|req:&Request,res:& mut Response|{
let mut context = tera::Context::new();
context.insert("text", "abcdefg");
res.render_view_once("./test.html", &context);
});
server.route(GET, "/custome").reg(|req:&Request,res:& mut Response|{
res.render_view(render,req);
});
server.run();
}
- Cookie
use rust_xfinal::{cookie, end_point, tera, EndPoint, HttpServer, Request, Response, GET};
use cookie::Period;
fn main() {
let mut server = HttpServer::create(end_point![0.0.0.0:8080], 10);
server.route(GET, "/cookie").reg(|req:&Request,res:& mut Response|{
let mut cookie = cookie::Cookie::new(String::from("token"),req);
cookie.insert(String::from("login"), true);
cookie.set_path("/".to_string());
cookie.set_max_age(2.days().from_now()); // the cookie will be expired after 2 days
res.write_string("ok").with_cookies(cookie);
});
server.route(GET, "/validate").reg(|req:&Request,res:& mut Response|{
let cookie = cookie::Cookie::new(String::from("token"),req);
let login:Option<bool> = cookie.get_data(String::from("login"));
let s = format!("{:?}",login);
res.write_string(&s);
});
server.route(GET, "/cookie").reg(|req:&Request,res:& mut Response|{
let mut cookie = cookie::Cookie::new(String::from("token"),req);
cookie.insert(String::from("login"), true);
cookie.set_path("/".to_string());
cookie.set_max_age(2.days().from_now());
cookie.set_http_only(true);
let mut cookie2 = cookie::Cookie::new(String::from("test"),req);
cookie2.set_path("/".to_string());
cookie2.insert(String::from("test"), "abc");
cookie2.insert(String::from("age"), 18);
cookie2.set_http_only(false);
res.write_string("ok").with_cookies([cookie,cookie2]);
});
server.route(GET, "/validate2").reg(|req:&Request,res:& mut Response|{
let cookie = cookie::Cookie::new(String::from("token"),req);
let login:Option<bool> = cookie.get_data(String::from("login"));
let cookie2 = cookie::Cookie::new(String::from("test"),req);
let test:Option<String> = cookie2.get_data("test".to_string());
let age:Option<i32> = cookie2.get_data("age".to_string());
let s = format!("\"{:?}\",{:?},{:?}",login,test,age);
res.add_header("content-type".to_string(), "text/plain; charset=UTF-8".to_string());
res.write_string(&s);
});
}
- 在中间件和最终路由器之间传输用户数据
use rust_xfinal::{JsonValue,inject_middlewares};
fn interrupter(req: &Request, res: &mut Response) -> bool {
match req.get_param("id") {
Some(v) => {
if v == "1" {
let mut ctx = req.context().borrow_mut();
ctx.insert("number".to_string(), JsonValue::Number(20.into()));
true
} else {
res.write_string("invalid request, invalid id value")
.status(400);
false
}
}
None => {
res.write_string("invalid request, no id").status(400);
false
}
}
}
server.route(GET, "/transfer").reg_with_middlewares(
inject_middlewares![interrupter],
|req: &Request, res: &mut Response| {
let ctx = req.context().borrow_mut();
let d = ctx.get(&"number".to_string());
let s = format!("abc,{:?}",d);
let method = req.get_method();
res.write_string(&s);
},
);
- WebSocket
fn interrupt(req: &Request, res: &mut Response) -> bool {
println!("invoke");
true
}
server.route_ws("/ws").reg_with_middlewares(inject_middlewares![interrupt],|ws:WebsocketEvent| {
//println!("message: {:?}", ws.message);
match &ws.message {
WsMessage::Open => {
println!("open!");
ws.write_string("hello");
},
WsMessage::Message(data, opcode) => {
let s = std::str::from_utf8(&data);
println!("{:?},len:{}",s,s.unwrap().len());
},
WsMessage::Close => {
println!("close");
},
};
});
server.route_ws("/ws2").reg(|ws:WebsocketEvent| {
//println!("message: {:?}", ws.message);
match &ws.message {
WsMessage::Open => {
println!("open!");
ws.write_string("hello");
},
WsMessage::Message(data, opcode) => {
let s = std::str::from_utf8(&data);
println!("{:?},len:{}",s,s.unwrap().len());
},
WsMessage::Close => {
println!("close");
},
};
});
依赖项
~10-20MB
~272K SLoC