前言
要完成一次客户端和服务端之间的网络通信,需要一种通信协议。客服端通过该协议发起请求,服务端接受请求并响应,这种协议就是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.服务端
服务端要完成对请求体的解析、返回响应体格式数据、模块化路由等工作
- server.rs module 服务的主体部分,管理和分配route和对应的handler
- router.rs module 实现对路由的响应和分配
- 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处理返回数据、错误情况等。
:star2:如果你对此感到有兴趣,可参考Rust Client-Server
本文由 yuin 创作,
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。