2. 解析HTTP请求
需要定义三个数据结构
构建两个package,并且在根目录下的toml文件中将这两个package加进去
cargo new httpserver
cargo new --lib http
声明一个模块
/Users/qinjianquan/s1/http/src/lib.rs
pub mod httprequest;
再在如下文件夹中创建两个文件
/Users/qinjianquan/s1/http/src
httprequest.rs
httpresponse.rs
然后在继续编写代码
/Users/qinjianquan/s1/http/src/httprequest.rs
use std::collections::HashMap;
#[derive(Debug,PartialEq)]
//1.method
pub enum Method {
Get,
Post,
Uninitialized,
}
//为枚举实现方法
impl From<&str> for Method {
fn from(s:&str) -> Method {
match s {
"GET" =>Method::Get,
"POST"=>Method::Post,
_ => Method::Uninitialized,
}
}
}
//2.version
#[derive(Debug,PartialEq)]
pub enum Version {
V1_1,
V2_0,
Uninitialized,
}
impl From<&str> for Version {
fn from(s:&str) ->Version{
match s {
"HTTP/1.1" => Version::V1_1,
_ => Version::Uninitialized,
}
}
}
//3.resource
#[derive(Debug,PartialEq)]
pub enum Resource {
Path(String),
}
#[derive(Debug)]
pub struct HttpRequest {
pub method: Method,
pub version: Version,
pub resource: Resource,
pub headers: HashMap<String,String>,
pub msg_body: String,
}
impl From<String> for HttpRequest {
fn from(req:String) -> Self {
let mut parsed_method = Method::Uninitialized;
let mut parsed_version = Version::V1_1;
let mut parsed_resource = Resource::Path("".to_string());
let mut parsed_headers = HashMap::new();
let mut parsed_msg_body = "";
for line in req.lines() {
if line.contains("HTTP") {
let (method,resource,version) = process_req_line(line);
parsed_method =method;
parsed_resource = resource;
parsed_version= version;
}else if line.contains(":"){
let(key,value) = process_header_line(line);
parsed_headers.insert(key,value);
}else if line.len() == 0 {
}else {
parsed_msg_body = line;
}
}
HttpRequest {
method: parsed_method,
version: parsed_version,
resource: parsed_resource,
headers: parsed_headers,
msg_body: parsed_msg_body.to_string(),
}
}
}
fn process_req_line(s: &str) -> (Method,Resource,Version) {
let mut words = s.split_whitespace();
let method = words.next().unwrap();
let resource = words.next().unwrap();
let version = words.next().unwrap();
(
method.into(),
Resource::Path(resource.to_string()),
version.into(),
)
}
fn process_header_line(s:&str) -> (String,String){
let mut header_items = s.split(":");
let mut key = String::from("");
let mut value = String::from("");
if let Some(k) = header_items.next(){
key = k.to_string();
}
if let Some(v) = header_items.next(){
value = v.to_string();
}
(key,value)
}
//测试方法
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_method_into() {
let m: Method = "GET".into();
assert_eq!(m,Method::Get);
}
#[test]
fn test_version_into() {
let v: Version = "HTTP/1.1".into();
assert_eq!(v,Version::V1_1);
}
#[test]
fn test_read_http () {
let s:String = String::from("GET /greeting HTTP/1.1\r\nHost:localhost:3000\r\nUser-Agent:curl/7.71.1\r\nAccept:*/*\r\n\r\n");
let mut headers_expexted = HashMap::new();
headers_expexted.insert("Host".into(), "localhost".into());
headers_expexted.insert("Accept".into(), "*/*".into());
headers_expexted.insert("User-Agent".into(), "curl/7.71.1".into());
let req: HttpRequest = s.into();
assert_eq!(Method::Get,req.method);
assert_eq!(Version::V1_1,req.version);
assert_eq!(Resource::Path("/greeting".to_string()),req.resource);
assert_eq!(headers_expexted,req.headers);
}
}
|