#web-framework #web-server #performance #server-framework #safe #data #request-response

rust-xfinal

一个安全且简单易用的Web服务器框架

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服务器

Download history 97/week @ 2024-04-14 190/week @ 2024-04-21 5/week @ 2024-04-28 1/week @ 2024-05-26 1/week @ 2024-06-30 81/week @ 2024-07-07 103/week @ 2024-07-28

每月下载量 184次

MIT/Apache

195KB
4K SLoC

rust-xfinal

这是一个由Rust编写的安全且高性能的Web服务器框架。

简介

这是用Rust编写一个安全Web服务器框架的开始。目前,这个仓库还没有提供完整和稳定的函数,因为xfinal已经完成了,由现代C++编写。目标是构建一个简单易用、性能好且安全的Web服务器框架。

优势

  1. 由于Rust的优势在于内存和多个线程上的安全,以及它是一种现代编程语言,几乎没有任何C++的累赘,基于Rust编写的框架无需担心内存和数据竞争的难题。Rust可以保证这些方面的安全性。

  2. 此外,Rust具有与C++类似的零成本抽象特性,因此框架的性能将符合您的期望,快速!

  3. Rust有一个非常方便的包管理器:Cargo,它可以让您自由地使用任何第三方crate,而无需使用CMake来管理这些依赖项,甚至无需编写丑陋的规则。

已支持的功能

  1. 注册路由器。
  2. 注册中间件。
  3. 查询文本-plain正文、url-form数据和multipart-form数据(包括上传的文件)。
  4. 分块传输
  5. 接受范围请求,这意味着rust-xfinal支持断点续传下载文件。
  6. 基于tera的视图渲染
  7. 支持可恢复的cookie(即服务器重启后cookie仍然可以识别)
  8. 支持从中间件将用户数据传输到最终路由器
  9. 支持WebSocket

尚未支持的功能

  1. 似乎没有

用法

  1. 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();
}
  1. 使用中间件
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();
}
  1. 从请求中查询信息
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);
});
  1. 分块传输和/或可范围

范围函数几乎需要与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.
});
  1. 通配符路径
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);
});
  1. 视图渲染
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();
}
  1. 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);
    });

}
  1. 在中间件和最终路由器之间传输用户数据
    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);
        },
    );
  1. 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