使用Rust构建HTTP Server May 26 2024 笔记Rust基础 0 comment ### 前言 要完成一次客户端和服务端之间的网络通信,需要一种通信协议。客服端通过该协议发起请求,服务端接受请求并响应,这种协议就是Http/Https。此项目使用纯Rust来构建简易的Http服务体系,将主要分别对客户端、服务端的请求和响应的模式、服务模块等进行分析。 ### 1.客户端 客户端要实现对服务地址以及端口的监听,并写入buff数据。 use std::net::TcpStream; use std::io::{Read,Write}; use std::str; fn main() { let mut stream = TcpStream::connect("localhost:3000").unwrap(); stream.write("Hello".as_bytes()).unwrap(); let mut buffer = [0;5]; stream.read(&mut buffer).unwrap(); println!("Received from server:{}", str::from_utf8(&buffer).unwrap()); } ### 2.服务端 服务端要完成对请求体的解析、返回响应体格式数据、模块化路由等工作 > 1. server.rs module 服务的主体部分,管理和分配route和对应的handler > 2. router.rs module 实现对路由的响应和分配 > 3. handler.rs module 路由的处理函数 #### 2.1 解析请求体 #[derive(Debug)] pub struct HttpRequest{ pub method : Methods, pub version: Version, pub headers: HashMap<String,String>, pub resource: Resource, pub body:String } impl From<String> for HttpRequest{ fn from(value: String) -> Self { let mut parsed_method = Methods::Uninitialized; let mut parsed_version = Version::Uninitialized; let mut parsed_header = HashMap::new(); let mut parsed_resource = Resource::Path("".to_string()); let mut parsed_body = ""; for line in value.lines() { if line.contains("HTTP"){ let (method,version,resource) = process_req_line(line); parsed_method = method; parsed_version = version; parsed_resource = resource; }else if line.contains(":") { let (key,value) = process_header_line(line); parsed_header.insert(key,value); }else { parsed_body = line; } } HttpRequest{ method:parsed_method, version:parsed_version, headers:parsed_header, resource:parsed_resource, body:parsed_body.to_string() } } } fn process_req_line(line: &str) -> (Methods,Version,Resource) { let mut line = line.split_whitespace(); //这的顺序非常重要!! let method = line.next().unwrap(); let resource = line.next().unwrap(); let version = line.next().unwrap(); ( method.into(), version.into(), Resource::Path(resource.to_string()) ) } fn process_header_line (line:&str) -> (String,String) { let mut line = line.split(":"); let mut key = String::from(""); let mut value = String::from(""); if let Some(k) = line.next(){ key = k.to_string(); } if let Some(v) = line.next(){ value = v.to_string(); } (key,value) } #### 2.2 返回响应体 #[derive(Debug,PartialEq,Clone)] pub struct HttpResponse<'a> { version: &'a str, status_code: u16, reason_phrase: &'a str, headers: Option<HashMap<&'a str,&'a str>>, body: Option<String>, } //实现default trait impl <'a> Default for HttpResponse<'a> { fn default() -> Self { HttpResponse { version: "HTTP/1.1", status_code: 200, reason_phrase: "OK", headers: None, body: None, } } } impl <'a> From<HttpResponse<'a>> for String { fn from(response: HttpResponse<'a>) -> String { let result = response.clone(); format!( "{} {} {}\r\n{}\r\n{}", result.version(), result.status_code(), result.reason_phrase(), result.headers(), result.body(), ) } } //实现new 方法 impl <'a> HttpResponse<'a> { //实现new放方法 pub fn new( status_code: u16, headers: Option<HashMap<&'a str,&'a str>>, body: Option<String>, ) -> HttpResponse { let mut response = HttpResponse::default(); if status_code != 200 { response.status_code = status_code }; response.reason_phrase = match response.status_code { 200 => "OK", 404 => "Not Found", 500 => "Internal Server Error", 501 => "Not Implemented", 303 => "See Other", _ => "Unknown", }; response.headers = match &headers { Some(_h) => headers, None => { let mut h = HashMap::new(); h.insert("Content-Type", "text/html"); Some(h) } }; response.body = body; response } //实现send方法 pub fn send(&self, stream: &mut impl Write) -> Result<(), ()>{ let response = self.clone(); let res_string = String::from(response); println!("response:\r\n{}",res_string); let _ = write!(stream, "{}", res_string); Ok(()) } //返回响应体各个部分的方法 fn version(&self) -> &str { self.version } fn status_code(&self) -> u16 { self.status_code } fn reason_phrase(&self) -> &str { self.reason_phrase } pub fn headers(&self) -> String { let map = self.headers.clone().unwrap(); let mut header_string = String::from("").into(); for (key, value) in map.iter() { header_string = format!("{}{}: {}\r\n", header_string, key, value); }; header_string } pub fn body(&self) -> &str { match &self.body { Some(b) => b.as_str(), None => "", } } } #### 2.3 处理返回的静态页面和json数据等 服务端是通过匹配路由的状态码(由Request所携带),通过内置的read方法读取文件并返回返回静态页面,以及Json数据等。下面是部分处理代码: fn handle(req: &HttpRequest) -> HttpResponse { let httplib::httprequest::Resource::Path(s) = &req.resource; let route:Vec<&str> = s.split("/").collect(); match route[1] { "" => HttpResponse::new(200, None, Self::load_file("index.html")), "about" => HttpResponse::new(200, None, Self::load_file("about.html")), path => match Self::load_file(path) { Some(contents) => { let mut map = HashMap::new(); if path.ends_with(".js") { map.insert("Content-type", "text/javascript"); } else if path.ends_with(".css") { map.insert( "Content-type", "text/css"); } else { map.insert("Content-type", "text/html"); } HttpResponse::new(200, Some(map), Some(contents)) }, None => HttpResponse::new(404, None, Self::load_file("404.html")) } } } ### 3.写在最后 核心要点: 1. 客户端监听本地端口,写入buffer。 2. 请求体解析、响应体的返回。 3. handler处理返回数据、错误情况等。 如果你对此感到有兴趣,可参考[Rust Client-Server](https://github.com/Pancocy/client-server) 本文由 yuin 创作,本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。