前言:在现代编程领域,Rust以其安全性、性能和并发性而闻名。它不仅适用于系统编程,还能用于构建高性能的Web服务器。本文将带你了解如何使用Rust来实现一个简单的Web服务器,包括请求处理、响应生成、路由设置、处理器编写以及服务器运行。
0、环境依赖
[dependencies] reqwest = { version = "0.12.5", features = ["gzip"] } tokio = { version = "1.39.3", features = ["rt", "rt-multi-thread", "macros"] }
1、Request(请求)
在Web服务器中,请求(Request)是客户端发送给服务器的HTTP请求。这里我们先创建request模块,将http报文解析为request
/**
请求方法
*/
#[derive(PartialEq, Debug)]
pub enum Method {
Get,
Post,
Put,
Delete,
Uninitialized,
}
impl From<&str> for Method {
fn from(value: &str) -> Self {
match value {
"GET" => Method::Get,
"POST" => Method::Post,
"PUT" => Method::Put,
"DELETE" => Method::Delete,
_ => Method::Uninitialized,
}
}
}
#[derive(Debug)]
pub enum Version {
Http1_0,
Http1_1,
Http2,
Uninitialized,
}
impl From<&str> for Version {
fn from(value: &str) -> Self {
match value {
"HTTP/1.0" => Version::Http1_0,
"HTTP/1.1" => Version::Http1_1,
"HTTP/2" => Version::Http2,
_ => Version::Uninitialized,
}
}
}
impl From<&Version> for String {
fn from(version: &Version) -> String {
match version {
Version::Http1_0 => "HTTP/1.0".to_string(),
Version::Http1_1 => "HTTP/1.1".to_string(),
Version::Http2 => "HTTP/2".to_string(),
Version::Uninitialized => {"".to_string()}
}
}
}
/**
请求
*/
#[derive(Debug)]
pub struct Request {
pub method: Method,
pub uri:String,
pub version: Version,
pub headers: Vec<(String, String)>,
pub body: Vec<u8>,
}
impl Request {
pub fn new(request_text:&str) -> Self{
let (method, uri, version) = process_req_line(request_text);
let mut headers = vec![];
let mut body = vec![];
for line in request_text.lines().skip(1) {
if line.contains(":") && !line.starts_with("{"){
let (key,value) = process_header(line);
headers.push((key,value))
}else {
body.extend(line.as_bytes())
}
}
Request {
method,
uri,
version,
headers,
body,
}
}
}
/**
解析请求行
*/
fn process_req_line(s: &str) -> (Method, String, Version) {
let mut words = s.split_whitespace();
let method = words.next().unwrap_or("");
let resource = words.next().unwrap_or("");
let version = words.next().unwrap_or("");
(
method.into(),
resource.to_string(),
version.into(),
)
}
/**
解析请求头
*/
fn process_header(s: &str) -> (String, String) {
let mut header_items = s.split(": ");
let key = header_items.next().unwrap_or("");
let value = header_items.next().unwrap_or("");
(key.to_string(), value.to_string())
}
2、Response(响应)
响应(Response)是服务器返回给客户端的HTTP响应。响应对象包含了状态码、响应头和响应体。当处理完逻辑后我们通过构建response将http响应报文通过输出流写出
/**
响应
*/
pub struct Response {
pub status_code: u16,
pub status_text: String,
pub headers: Vec<(String, String)>,
pub body: Vec<u8>,
pub version: Version,
}
impl Response {
pub fn new(status_code: u16, headers: Vec<(String, String)>, body: Vec<u8>) -> Self {
let status_text = match status_code {
200 => {"OK"}
404 => {"Not Found"}
500 => {"Internal Server Error"}
_ => {""}
};
Response {
status_code,
status_text: status_text.to_string(),
headers,
body,
version: Version::Http1_1,
}
}
fn status_line(&self) -> String {
let headers = self
.headers
.iter()
.filter(|(k, _)| k != "transfer-encoding")
.map(|(k, v)| format!("{}: {}\r\n", k, v))
.collect::<String>();
format!(
"{} {} {}\r\n{}Content-Length: {}\r\n\r\n",
String::from(&self.version),
&self.status_code,
&self.status_text,
headers,
&self.body.len(),
)
}
pub fn write(&self, stream: &mut TcpStream) -> Result<(), std::io::Error> {
let status_line = self.status_line();
stream.write(status_line.as_bytes())?;
stream.write(&self.body)?;
Ok(())
}
}
在这个例子中,我们创建了一个空的响应体,并设置了状态码为200(OK)。我们还添加了一个内容类型头,指示响应体是纯文本。
3、Route(路由)
路由(Route)是Web服务器用来将不同的URL路径映射到不同处理函数的机制。我们在这里创建route,读取完request将其分配到不同的处理,处理完成后将response写出。
pub struct Router;
impl Router {
pub async fn route(request: Request, stream: &mut TcpStream) {
if request.method == Method::Uninitialized {
return;
}
let response = if request.uri.starts_with("/prod-api") {
ProxyHandler::handle(&request).await
}else{
StaticHandler::handle(&request).await
};
response.write(stream).unwrap()
}
}
4、Handler(处理器)
处理器(Handler)是处理特定请求的函数。处理器接收一个请求对象,并返回一个响应对象。处理器可以根据请求的类型、路径或其他属性来决定如何响应。
pub trait Handler {
async fn handle(request: &Request) -> Response ;
}
/**
没有匹配时
*/
pub struct NotFoundHandler;
/**
服务内部错误时
*/
pub struct ErrorHandler;
/**
处理静态资源
*/
pub struct StaticHandler;
/**
处理反向代理
*/
pub struct ProxyHandler;
impl Handler for NotFoundHandler {
async fn handle(_request: &Request) -> Response {
Response::new(
404,
vec![],
"404 Not Found".as_bytes().to_vec(),
)
}
}
impl Handler for ErrorHandler {
async fn handle( _request: &Request) -> Response {
Response::new(
500,
vec![],
"500 Internal Server Error".as_bytes().to_vec(),
)
}
}
impl Handler for StaticHandler {
async fn handle(request: &Request) -> Response {
let uri = &request.uri;
let file_path = format!("D:/project/tt/tcp_server/src/static{}", uri);
if let Ok(body) = fs::read(file_path){
let content_type = match uri.split(".").last() {
Some("html") => "text/html",
Some("css") => "text/css",
Some("js") => "text/javascript",
Some("png") => "image/png",
Some("jpg") => "image/jpeg",
Some("jpeg") => "image/jpeg",
Some("gif") => "image/gif",
Some("ico") => "image/x-icon",
Some("svg") =>"image/svg+xml",
_ => {""}
};
Response::new(
200,
vec![("Content-Type".to_string(),content_type.to_string()),("Server".to_string(),"rust".to_string())],
body,
)
}else{
NotFoundHandler::handle(request).await
}
}
}
impl Handler for ProxyHandler {
async fn handle(request: &Request) -> Response {
let uri = &request.uri;
let replace_uri = uri.replace("/api", "http://localhost:8080");
let client = Client::new();
let mut request_builder = match request.method {
Method::Get => client.get(&replace_uri),
Method::Post => client.post(&replace_uri),
Method::Put => client.put(&replace_uri),
Method::Delete => client.delete(&replace_uri),
_ => {panic!("Invalid method")}
};
for (key, value) in &request.headers {
if key != "Host" {
request_builder = request_builder.header(key, value);
}
}
request_builder = request_builder.body(request.body.clone());
let response = request_builder.send().await.unwrap();
let mut headers = vec![];
for (key, value) in response.headers() {
headers.push((key.as_str().to_string(), value.to_str().unwrap().to_string()))
}
let status_code = response.status().as_u16();
let body = response.text().await.unwrap();
Response::new(
status_code,
headers,
body.as_bytes().to_vec(),
)
}
}
5、Server(服务器)
服务器(Server)是监听网络请求并调用相应处理器的组件。在这里sever负责截取tcp数据处理为request后叫给route处理。
pub struct Server;
impl Server {
pub fn run (addr: &str) {
let connection_listener = TcpListener::bind(addr).unwrap();
for stream in connection_listener.incoming(){
//move将代码块内变量所有权移交至新的异步任务中
tokio::spawn(async move {
let mut stream = stream.unwrap();
let mut buffer = [0; 10240];
if let Ok(size) = stream.read(&mut buffer){
if size == 0 {
return;
}
let request_text = String::from_utf8_lossy(&buffer).to_string();
let request = Request::new(&request_text);
Router::route(request, &mut stream).await;
}
});
}
}
}
6、运行
#[tokio::main]
async fn main() {
Server::run("127.0.0.1:8088")
}
在这个例子中,我们创建了一个监听在localhost
的8088端口的服务器。当服务器接收到请求时,它会调用我们定义的路由逻辑来处理请求。
通过上述步骤,你可以使用Rust构建一个简单的Web服务器。这只是一个起点,Rust的生态系统提供了许多库和工具,可以帮助你构建更复杂、更高效的Web应用程序。