Video Walkthrough
Initialize Project
cargo new blog
Listen on Port 8000 and Respond With 404
// main.rs
use std::{
io::{prelude::*},
net::{TcpListener, TcpStream},
};
fn main() {
let listener = TcpListener::bind("[::]:8000").unwrap();
for stream in listener.incoming() {
let stream = stream.unwrap();
handle_connection(stream);
}
}
fn handle_connection(mut stream: TcpStream) {
let status_line = "HTTP/1.1 404 NOT FOUND";
let contents = String::new();
let length = contents.len();
let response = format!(
"{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}"
);
stream.write_all(response.as_bytes()).unwrap();
}
Run
cargo run
Test
curl localhost:8000
Alternatively, you can open http://localhost:8000 in your browser.
Parse Path From Request
Add Regex to Cargo.toml
[package]
name = "http-server"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+ regex = "1"
// main.rs
use std::{
- io::{prelude::*},
+ io::{prelude::*, BufReader},
net::{TcpListener, TcpStream},
};
+ use regex::Regex;
fn main() {
let listener = TcpListener::bind("[::]:8000").unwrap();
for stream in listener.incoming() {
let stream = stream.unwrap();
handle_connection(stream);
}
}
fn handle_connection(mut stream: TcpStream) {
+ let buf_reader = BufReader::new(&mut stream);
+ let request_line = buf_reader.lines().next().unwrap().unwrap();
+ let re = Regex::new(r"^(.*) (.*) (.*)$").unwrap();
+ let caps = re.captures(&request_line).unwrap();
+ let pathname = caps.get(2).map_or("", |m| m.as_str());
+
+ println!("{pathname}");
let status_line = "HTTP/1.1 404 NOT FOUND";
let contents = String::new();
let length = contents.len();
let response = format!(
"{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}"
);
stream.write_all(response.as_bytes()).unwrap();
}
Check
Restart the program with cargo run
. Now from a browser you can try different URL variations http://localhost:8000 and confirm they are printed correctly in the console.
Read File to String and Return in Response
// main.rs
use std::{
io::{prelude::*, BufReader},
net::{TcpListener, TcpStream},
};
+use std::path::Path;
use regex::Regex;
fn main() {
let listener = TcpListener::bind("[::]:8000").unwrap();
for stream in listener.incoming() {
let stream = stream.unwrap();
handle_connection(stream);
}
}
fn handle_connection(mut stream: TcpStream) {
let buf_reader = BufReader::new(&mut stream);
let request_line = buf_reader.lines().next().unwrap().unwrap();
let re = Regex::new(r"^(.*) (.*) (.*)$").unwrap();
let caps = re.captures(&request_line).unwrap();
let pathname = caps.get(2).map_or("", |m| m.as_str());
- println!("{pathname}");
+ let mut filename = "index.html";
+ if pathname != "/" {
+ filename = &pathname[1..];
+ }
- let status_line = "HTTP/1.1 404 NOT FOUND";
- let contents = String::new();
+ let mut status_line = "HTTP/1.1 404 NOT FOUND";
+ let mut contents = String::new();
+ let full_file_path = format!("{}{}", "static/", filename);
+ if Path::new(&full_file_path).exists() {
+ status_line = "HTTP/1.1 200 OK";
+ contents = fs::read_to_string(&full_file_path).unwrap();
+ }
let length = contents.len();
let response = format!(
"{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}"
);
stream.write_all(response.as_bytes()).unwrap();
}
Add Some HTML Pages
Create static/index.html
<!-- static/index.html -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Home</title>
</head>
<body>
<a href="/hello-world.html">Hello World</a>
</body>
</html>
Create static/hello-world.html
<!-- static/hello-world.html -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hello World</title>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
Wrap-Up
In this post, we’ve built a simple static file server using Rust. Already this server is functional enough to be used for a blog. All you would need to do is add new html pages with posts and update the index.html to allow people to discover them from the front page.
In a future post, we will build a simple markdown parser to allow us to write out posts in markdown.