Rust实现简单的Web服务器

前言:在现代编程领域,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应用程序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值