本文将详细介绍如何使用 Rust 开发功能完善的单线程 Web 服务器。
1. 创建项目
cargo new single-threaded-web
cd single-threaded-web
2. 项目结构
single-threaded-web/
├── Cargo.toml
├── src/
│ ├── main.rs # 服务器入口
│ ├── lib.rs # 核心实现
│ └── http/ # HTTP协议相关
│ ├── mod.rs # 模块定义
│ ├── request.rs # 请求解析
│ └── response.rs # 响应构建
├── static/ # 静态文件
│ ├── index.html # 示例页面
│ └── 404.html # 404页面
3. 编辑Cargo.toml
[package]
name = "single-threaded-web"
version = "0.1.0"
edition = "2024"
[dependencies]
thiserror = "1.0" # 错误处理
bytes = "1.0" # 字节处理
httparse = "1.8" # HTTP解析
log = "0.4" # 日志
env_logger = "0.10" # 日志实现
4. 实现HTTP模块 (src/http/mod.rs)
mod request;
mod response;
// 导出所有公共类型
pub use request::{Request, Method};
pub use response::{Response, StatusCode};
/// HTTP相关错误
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Invalid HTTP request")]
InvalidRequest,
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("Unsupported HTTP method")]
UnsupportedMethod,
#[error("Invalid HTTP header")]
InvalidHeader,
#[error("HTTP parse error: {0}")]
Parse(#[from] httparse::Error),
}
5. 实现请求解析 (src/http/request.rs)
use std::collections::HashMap;
use std::str::FromStr;
use crate::http::Error;
/// HTTP请求方法
#[derive(Debug, PartialEq)]
pub enum Method {
GET,
POST,
}
impl std::fmt::Display for Method {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Method::GET => write!(f, "GET"),
Method::POST => write!(f, "POST"),
}
}
}
impl FromStr for Method {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"GET" => Ok(Method::GET),
"POST" => Ok(Method::POST),
_ => Err(Error::UnsupportedMethod),
}
}
}
/// HTTP请求
#[derive(Debug)]
pub struct Request {
pub method: Method,
pub path: String,
pub headers: HashMap<String, String>,
pub body: Vec<u8>,
}
impl Request {
/// 从字节流解析HTTP请求
pub fn parse(buffer: &[u8]) -> Result<Self, Error> {
let mut headers = [httparse::EMPTY_HEADER; 16];
let mut req = httparse::Request::new(&mut headers);
let status = req.parse(buffer).map_err(Error::from)?;
if status.is_partial() {
return Err(Error::InvalidRequest);
}