How to Build a Multithreaded Web Server in Rust Without External Crates
Rust’s powerful concurrency model and ownership system make it an excellent choice for building fast, safe web servers. In this article, you’ll learn how to implement a multithreaded web server from scratch using only the Rust standard library.
1. Project Setup
Start with a new Rust project:
cargo new rust_web_server
cd rust_web_server
2. Reading and Responding to HTTP Requests
Edit src/main.rs
to include basic TCP listening and response logic:
use std::io::prelude::*;
use std::net::{TcpListener, TcpStream};
use std::thread;
use std::fs;
fn handle_connection(mut stream: TcpStream) {
let mut buffer = [0; 1024];
stream.read(&mut buffer).unwrap();
let contents = fs::read_to_string("hello.html").unwrap();
let response = format!(
"HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}",
contents.len(),
contents
);
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
3. Adding a Thread Pool
To handle requests concurrently, we'll build a basic thread pool without any external dependencies:
use std::sync::{mpsc, Arc, Mutex};
struct ThreadPool {
workers: Vec,
sender: mpsc::Sender,
}
type Job = Box;
impl ThreadPool {
fn new(size: usize) -> ThreadPool {
let (sender, receiver) = mpsc::channel();
let receiver = Arc::new(Mutex::new(receiver));
let mut workers = Vec::with_capacity(size);
for id in 0..size {
workers.push(Worker::new(id, Arc::clone(&receiver)));
}
ThreadPool { workers, sender }
}
fn execute(&self, f: F)
where
F: FnOnce() + Send + 'static,
{
self.sender.send(Box::new(f)).unwrap();
}
}
struct Worker {
id: usize,
thread: Option<:joinhandle>>,
}
impl Worker {
fn new(id: usize, receiver: Arc>>) -> Worker {
let thread = thread::spawn(move || loop {
let job = receiver.lock().unwrap().recv().unwrap();
println!("Worker {} got a job; executing.", id);
job();
});
Worker {
id,
thread: Some(thread),
}
}
}
4. Wiring It All Together
fn main() {
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
let pool = ThreadPool::new(4);
for stream in listener.incoming().take(10) {
let stream = stream.unwrap();
pool.execute(|| {
handle_connection(stream);
});
}
println!("Shutting down server.");
}
5. Testing
Create a file named hello.html
in your project root:
Rust Web Server
Hello from Rust!
Conclusion
This is a barebones but fully functioning multithreaded web server written entirely with the Rust standard library. From here, you can explore features like routing, middleware, and async IO using more advanced tools like tokio
or hyper
.
If this post helped you, consider supporting me: buymeacoffee.com/hexshift