前言
本章节将带你实现一个简单的HTTP服务器,它能够响应基本的HTTP请求。我们将从零开始构建,并逐步完善功能,最终实现一个能够响应静态文件和处理简单动态路由的HTTP服务器。
项目目标
HTTP服务器功能:
- 监听特定端口,接受HTTP请求。
- 能够解析请求头并生成响应。
- 提供静态文件服务,例如HTML文件。
- 支持简单的动态路由,例如返回JSON响应。
创建项目
首先我们创建一个名为simple_http的项目,项目目录结构如下所示:
? simple_http git:(master) ? tree .
.
├── Cargo.toml
├── src
│ ├── http
│ │ ├── mod.rs
│ │ ├── request.rs
│ │ └── response.rs
│ ├── main.rs
│ └── server.rs
└── static
├── about.html
└── index.html
4 directories, 8 files
添加所需的项目依赖,具体操作如下所示:
cargo add serde --features derive
cargo add serde_json
cargo add log
cargo add env_logger
cargo add tokio --features full
HTTP模块实现
HTTP请求解析
编辑文件:src/http/request.rs
use std::collections::HashMap;
// 定义http 请求结构
#[derive(Debug)]
pub struct Request {
pub method: String,
pub path: String,
pub headers: HashMap,
pub body: Option,
}
impl Request {
pub fn parse(request: &str) -> Option {
let mut lines = request.lines();
// 解析请求行
let request_line = lines.next()?;
let mut parts = request_line.split_whitespace();
let method = parts.next()?.to_string();
let path = parts.next()?.to_string();
// 解析http header
let mut headers = HashMap::new();
for line in lines {
if line.is_empty() {
break;
}
if let Some((name, value)) = line.split_once(':') {
headers.insert(name.to_string(), value.to_string());
}
}
// 解析body
let body = parts.next().map(|s| s.to_string());
Some(Request{
method,
path,
headers,
body,
})
}
}
HTTP响应构建
编辑文件:src/http/response.rs
use std::collections::HashMap;
#[derive(Debug)]
pub struct Response {
pub status_code: u16,
pub headers: HashMap,
pub body: Option,
}
impl Response {
pub fn new(status_code: u16, body: Option) -> Response {
let mut headers = HashMap::new();
if let Some(ref body) = body {
headers.insert("Content-Length".to_string(), body.len().to_string());
headers.insert("Content-Type".to_string(), "text/html".to_string());
}
Response{
status_code,
headers,
body,
}
}
pub fn to_string(&self) -> String {
let mut response = format!("HTTP/1.1 {} OK\r\n\r\n", self.status_code);
for (key, value) in &self.headers {
response.push_str(&format!("{}: {}\r\n", key, value));
}
response.push_str("\r\n");
if let Some(body) = &self.body {
response.push_str(body);
}
response
}
}
HTTP模块入口
编辑文件:src/http/mod.rs
pub mod request;
pub mod response;
HTTP服务器逻辑
编辑文件:src/server.rs
use std::fs;
use log::info;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpListener;
use crate::http::request::Request;
use crate::http::response::Response;
use std::sync::Arc;
pub struct Server {
pub address: String,
}
impl Server {
pub fn new(address: &str) -> Server {
Server{address: address.to_string()}
}
pub async fn run(self) {
let listener = TcpListener::bind(&self.address).await.unwrap();
info!("Server listening on {}", &self.address);
let value = Arc::new(self);
loop {
let (mut socket, _) = listener.accept().await.unwrap();
let handler = value.clone();
tokio::spawn(async move {
let mut buf = vec![0; 1024];
let n = socket.read(&mut buf).await.unwrap();
let request = String::from_utf8_lossy(&buf[..n]);
if let Some(http_request) = Request::parse(&request) {
let response = handler.handle_request(http_request).await;
socket.write_all(response.to_string().as_bytes()).await.unwrap();
}
});
}
}
async fn handle_request(&self, request: Request) -> Response {
match request.path.as_str() {
"/" => {
let content = fs::read_to_string("static/index.html").unwrap_or_else(|_| {
"welcome to rust http server
".to_string()
});
Response::new(200, Some(content))
}
"/about" => {
let content = fs::read_to_string("static/about.html").unwrap_or_else(|_| {
"about page
".to_string()
});
Response::new(200, Some(content))
}
"/api" => {
let content = serde_json::to_string(&serde_json::json!({
"msg": "success",
"status": "ok"
})).unwrap();
Response::new(200, Some(content))
}
_ => {
Response::new(404, Some(format!("404 not found {}", request.path)))
}
}
}
}
主程序入口
编辑文件:src/main.rs
mod http;
mod server;
#[tokio::main]
async fn main() {
env_logger::init_from_env(env_logger::Env::new().filter_or("RUST_LOG", "INFO"));
let server = server::Server::new("0.0.0.0:9527");
server.run().await;
}
运行项目
确保static/目录下包含index.html和about.html。
运行服务器:
cargo run
在浏览器访问:
- http://127.0.0.1:9527/ 查看主页。
- http://127.0.0.1:9527/about 查看关于页面。
- http://127.0.0.1:9527/api 查看JSON API。
总结
- 本章节实现了一个简单的HTTP服务器,包含静态文件服务和动态路由支持。
- 学习了Rust中使用tokio实现异步网络操作。
- 掌握了基本的HTTP请求解析和响应构建技术,为构建更复杂的Web服务器打下基础。
如果你觉得本文对你有所帮助,欢迎点赞,收藏,关注,或者分享给更多的 Rust 开发者!感谢你的收看,我们明年见!!!
本文暂时没有评论,来添加一个吧(●'◡'●)