Simple Web Server using Rust
Table of Contents
Introduction #
This course is to implement simple static web server using Rust
Modules #
use std::io::prelude::*;
use std::net::TcpListener;
use std::net::TcpStream;
use std::fs::File;
prelude
→ https://doc.rust-lang.org/std/prelude/index.html
TcpListener
→ https://doc.rust-lang.org/std/net/struct.TcpListener.html
After creating a TcpListener
by bind
ing it to a socket address, it listens for incoming TCP connections. These can be accepted by calling accept
or by iterating over the Incoming
iterator returned by incoming
.
TcpStream → https://doc.rust-lang.org/std/net/struct.TcpStream.html
Stream between a local and a remote socket. After creating a TcpStream
by either connect
ing to a remote host or accept
ing a connection on a TcpListener
, data can be transmitted by reading and writing to it.
File → https://doc.rust-lang.org/std/fs/struct.File.html
Opening files like index.html
main #
fn main() {
let listener = TcpListener::bind("127.0.0.1:8000").unwrap();
for stream in listener.incoming() {
let stream = stream.unwrap();
handle_connection(stream).unwrap();
}
}
listener.incoming()
returns an iterator over the connections being received on this listener. Each stream
in iterator becomes Result<TcpStream>
.
handle_connection #
fn handle_connection(mut stream: TcpStream) {
let mut buffer: [u8; 512] = [0; 512];
stream.read(&mut buffer).unwrap();
const GET: &[u8; 16] = b"GET / HTTP/1.1\r\n";
// If client sends GET request
if buffer.starts_with(GET) {
let mut file = File::open("index.html").unwrap();
let mut contents = String::new();
// open index.html and put its content to string
file.read_to_string(&mut contents).unwrap();
// create response as HTTP protocol
let response = format!("HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}",
contents.len(),
contents
);
// write response as bytes
stream.write(response.as_bytes()).unwrap();
// To ensure all buffered content reached its destination, flush this stream
stream.flush().unwrap();
} else {
const NOT_FOUND: &str = "HTTP/1.1 404 NOT FOUND";
let contents = "Cannot Found its Method or Resources";
let response = format!("{}\r\nContent-Length: {}\r\n\r\n{}",
NOT_FOUND,
contents.len(),
contents.to_string()
);
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
}
Multi threading Web Server #
Simple Web server using rust which mentioned above can’t be used in production. Because, it can’t handle multiple request concurrently. The server above could only handle one request at a time. For example, when one request takes more than 5 seconds, the next request must wait until the previous request is processed.
fn handle_slow_connection(mut stream: TcpStream) {
// --snip--
thread::sleep(Duration::from_millis(5000));
stream.write(response.as_bytes()).unwrap();
// --snip--
}
To prevent this, there are several ways to handle requests simultaneously. For this section, we’ll implement by using multi-threading method.
Thread for reqeust #
use std::thread;
fn main() {
let listener = TcpListener::bind("127.0.0.1:8000").unwrap();
for stream in listener.incoming() {
let stream = stream.unwrap();
thread::spawn(|| {
handle_connection(stream).unwrap();
});
}
}
Every time when listener gets a new request, thread::spawn()
creates a new thread.
This makes server possible to accept multiple requests in same time.
But, the problem with this version is that it would create new thread every single time when there is a new request.
When there are 1000 requests, thousands of new thread will be created.
This, can be prevented using a term ThreadPool
.
I’ll deal with ThreadPool
when I have a chance later.