当前位置: 首页 > news >正文

Rust Web 全栈开发(二):构建 HTTP Server

Rust Web 全栈开发(二):构建 HTTP Server

  • Rust Web 全栈开发(二):构建 HTTP Server
    • 解析 HTTP 请求
      • HTTP 请求的构成
      • 创建成员包/库:httpserver、http
      • 构建 http 成员库
        • httprequest
        • httpresponse
        • lib

Rust Web 全栈开发(二):构建 HTTP Server

参考视频:https://www.bilibili.com/video/BV1RP4y1G7KF

Web Server 的消息流动图:

在这里插入图片描述

Server:监听 TCP 字节流

Router:接收 HTTP 请求,并决定调用哪个 Handler

Handler:处理 HTTP 请求,构建 HTTP 响应

HTTP Library:

  • 解释字节流,把它转换为 HTTP 请求
  • 把 HTTP 响应转换回字节流

构建步骤:

  1. 解析 HTTP 请求消息
  2. 构建 HTTP 响应消息
  3. 路由与 Handler
  4. 测试 Web Server

解析 HTTP 请求

3 个数据结构:

名称类型描述
HttpRequeststruct表示 HTTP 请求
Methodenum指定所允许的 HTTP 方法
Versionenum指定所允许的 HTTP 版本

以上 3 个数据结构都需要实现的 3 个 trait:

名称描述
From<&str>用于把传进来的字符串切片转换为 HttpRequest
Debug打印调试信息
PartialEq用于解析和自动化测试脚本里做比较

HTTP 请求的构成

HTTP 请求报文由 3 部分组成:请求行、请求头、请求体。

在这里插入图片描述

创建成员包/库:httpserver、http

在原项目下新建成员包 httpserver、成员库 http:

cargo new httpserver
cargo new --lib http

在这里插入图片描述

在工作区内运行 cargo new 会自动将新创建的包添加到工作区内 Cargo.toml 的 [workspace] 定义中的 members 键中,如下所示:

在这里插入图片描述

此时,我们可以通过运行 cargo build 来构建工作区。项目目录下的文件应该是这样的:

├── Cargo.lock
├── Cargo.toml
├── httpserver
│   ├── Cargo.toml
│   └── src
│       └── main.rs
├── http
│   ├── Cargo.toml
│   └── src
│       └── lib.rs
├── tcpclient
│   ├── Cargo.toml
│   └── src
│       └── main.rs
├── tcpserver
│   ├── Cargo.toml
│   └── src
│       └── main.rs
└── target

构建 http 成员库

在 http 成员库的 src 目录下新建两个文件:httprequest.rs、httpresponse.rs。

httprequest

打开 http 成员库中的 httprequest.rs,编写代码:

use std::collections::HashMap;#[derive(Debug, PartialEq)]
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,}}
}#[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,}}
}#[derive(Debug, PartialEq)]
pub enum Resource {Path(String),
}#[derive(Debug)]
pub struct HttpRequest {pub method: Method,pub resource: Resource,pub version: Version,pub headers: HashMap<String, String>,pub body: String,
}impl From<String> for HttpRequest {fn from(request: String) -> HttpRequest {let mut parsed_method = Method::Uninitialized;let mut parsed_resource = Resource::Path("".to_string());let mut parsed_version =  Version::V1_1;let mut parsed_headers = HashMap::new();let mut parsed_body = "";for line in request.lines() {if line.contains("HTTP") {let (method, resource, version) = process_request_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_body = line;}}HttpRequest {method: parsed_method,resource: parsed_resource,version: parsed_version,headers: parsed_headers,body: parsed_body.to_string(),}}
}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)
}fn process_request_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())
}#[cfg(test)]
mod test {use super::*;#[test]fn test_method_into() {let method: Method = "GET".into();assert_eq!(method, Method::Get);}#[test]fn test_version_into() {let version: Version = "HTTP/1.1".into();assert_eq!(version, Version::V1_1);}#[test]fn test_read_http() {let s = 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_excepted = HashMap::new();headers_excepted.insert("Host".into(), " localhost".into());headers_excepted.insert("Accept".into(), " */*".into());headers_excepted.insert("User-Agent".into(), " curl/7.71.1".into());let request: HttpRequest = s.into();assert_eq!(request.method, Method::Get);assert_eq!(request.resource, Resource::Path("/greeting".to_string()));assert_eq!(request.version, Version::V1_1);assert_eq!(request.headers, headers_excepted);}
}

运行命令 cargo test -p http,测试 http 成员库。

3 个测试都通过了:

在这里插入图片描述

httpresponse

打开 http 成员库中的 httpresponse.rs,编写代码:

在这里插入代码片
lib

打开 http 成员库中的 lib.rs,编写代码:

pub mod httprequest;
pub mod httpresponse;
http://www.dtcms.com/a/265464.html

相关文章:

  • 主流分布式中间件及其选型
  • locate命令的原理是啥
  • OpenCV CUDA模块设备层-----在GPU 上高效地执行两个 uint 类型值的最大值比较函数vmax2()
  • Frida:配置自动补全 in VSCode
  • 搭建VirtualBox-6+vagrant_2+docker+mysql5.7的步骤
  • 客户案例 | 某新能源车企依托Atlassian工具链+龙智定制开发服务,打造符合ASPICE标准的研发管理体系
  • 云原生系统DOCKER中容器系统搭建
  • Python字符与ASCII转换方法
  • Ubuntu Gnome 安装和卸载 WhiteSur-gtk-theme 类 Mac 主题的正确方法
  • vue2+elementui使用compressorjs压缩上传的图片
  • Euler2203安装.NetCore6.0环境操作步骤
  • python安装虚拟环境
  • Python 物联网(IoT)与边缘计算开发实战(1)
  • 优雅草蜻蜓R实时音视频会议系统云原生私有化部署方案深度解析-优雅草卓伊凡|贝贝|clam|麻子|夜辰
  • Docker 容器资源限制
  • 9.Docker的容器数据卷使用(挂载)
  • ATE FT ChangeKit学习总结-20250630
  • 网络的封包与拆包
  • 基于Java的企业项目管理与协作系统设计与实现
  • Dataset Distillation by Matching Training Trajectories(2203.11932)
  • Eclipse主题拓展
  • mysql索引的底层原理是什么?如何回答?
  • Go语言的sync.Once和sync.Cond
  • Redis 源码 tar 包安装 Redis 哨兵模式(Sentinel)
  • Go调度器的抢占机制:从协作式到异步抢占的演进之路|Go语言进阶(7)
  • 价值实证:数字化转型标杆案例深度解析
  • 网络地址与子网划分:一次性搞清 CIDR、VLSM 和子网掩码
  • 分类树查询性能优化:从 2 秒到 0.1 秒的技术蜕变之路
  • 如何在 IDEA 中设置类路径
  • 探索具身智能新高度——机器人在数据收集与学习策略中的优势和机会