From: Martin Pitt Date: Sun, 19 Sep 2021 08:38:31 +0000 (+0200) Subject: simple-http: Add scaffolding for thread pool implementation X-Git-Url: https://piware.de/gitweb/?p=learn-rust.git;a=commitdiff_plain;h=4d4ba2d5f703969851742798e79e16333fb9ca9a simple-http: Add scaffolding for thread pool implementation Turn into library crate. Add ThreadPool stub to src/lib. --- diff --git a/simple-http/src/bin/main.rs b/simple-http/src/bin/main.rs new file mode 100644 index 0000000..ff3af27 --- /dev/null +++ b/simple-http/src/bin/main.rs @@ -0,0 +1,72 @@ +use std::io::prelude::*; + +use std::net::TcpListener; +use std::net::TcpStream; +use std::time::Duration; +use std::{fs, str, thread}; + +use simple_http::ThreadPool; + +fn handle_connection(mut stream: TcpStream) { + let mut buffer = [0; 1024]; + + stream.read(&mut buffer).unwrap(); + + let buffer = match str::from_utf8(&buffer[..]) { + Ok(s) => s, + Err(e) => { + eprintln!("Invalid non-UTF8 request: {}\n{}", e, String::from_utf8_lossy(&buffer[..])); + return; + } + }; + println!("Request: {}", buffer); + + let path; + + // simple web server, just interested in first line + if let Some(line) = buffer.lines().next() { + let request: Vec<_> = line.split_whitespace().collect(); + if request.len() != 3 || request[0] != "GET" || request[2] != "HTTP/1.1" { + stream.write(b"HTTP/1.1 501 NOT IMPLEMENTED\r\n").unwrap(); + stream.flush().unwrap(); + return; + } + + path = request[1]; + } else { + eprintln!("Ignoring empty request: {}", buffer); + return; + } + + let (code, file) = if path == "/" || path == "/index.html" { + ("200 OK", "index.html") + } else if path == "/slow" { + thread::sleep(Duration::from_secs(5)); + ("200 OK", "index.html") + } else { + ("404 NOT FOUND", "404.html") + }; + + let text = fs::read_to_string(file).unwrap(); + + let response = format!( + "HTTP/1.1 {}\r\n\ + Content-Type: text/html\r\n\ + Content-Length: {}\r\n\r\n{}", + code, + text.len(), + text); + + stream.write(response.as_bytes()).unwrap(); + stream.flush().unwrap(); +} + +fn main() { + let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); + let pool = ThreadPool::new(4); + + for stream in listener.incoming() { + let stream = stream.unwrap(); + pool.execute(|| handle_connection(stream)); + } +} diff --git a/simple-http/src/lib.rs b/simple-http/src/lib.rs new file mode 100644 index 0000000..ff8bc4f --- /dev/null +++ b/simple-http/src/lib.rs @@ -0,0 +1,40 @@ +use std::thread; + +struct Worker { + id: usize, + thread: thread::JoinHandle<()>, +} + +impl Worker { + fn new(id: usize) -> Worker { + Worker { id, thread: thread::spawn(|| {}) } + } +} + +pub struct ThreadPool { + workers: Vec, +} + +impl ThreadPool { + /// Create a new thread pool. + /// + /// # Panics + /// + /// - if size is zero + pub fn new(size: usize) -> ThreadPool { + assert!(size > 0); + let mut workers = Vec::with_capacity(size); + + for id in 0..size { + workers.push(Worker::new(id)); + } + + ThreadPool { workers } + } + + pub fn execute(&self, f: F) + where F: FnOnce() + Send + 'static + { + thread::spawn(f); + } +} diff --git a/simple-http/src/main.rs b/simple-http/src/main.rs deleted file mode 100644 index 0993075..0000000 --- a/simple-http/src/main.rs +++ /dev/null @@ -1,69 +0,0 @@ -use std::io::prelude::*; - -use std::net::TcpListener; -use std::net::TcpStream; -use std::time::Duration; -use std::{fs, str, thread}; - -fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - - stream.read(&mut buffer).unwrap(); - - let buffer = match str::from_utf8(&buffer[..]) { - Ok(s) => s, - Err(e) => { - eprintln!("Invalid non-UTF8 request: {}\n{}", e, String::from_utf8_lossy(&buffer[..])); - return; - } - }; - println!("Request: {}", buffer); - - let path; - - // simple web server, just interested in first line - if let Some(line) = buffer.lines().next() { - let request: Vec<_> = line.split_whitespace().collect(); - if request.len() != 3 || request[0] != "GET" || request[2] != "HTTP/1.1" { - stream.write(b"HTTP/1.1 501 NOT IMPLEMENTED\r\n").unwrap(); - stream.flush().unwrap(); - return; - } - - path = request[1]; - } else { - eprintln!("Ignoring empty request: {}", buffer); - return; - } - - let (code, file) = if path == "/" || path == "/index.html" { - ("200 OK", "index.html") - } else if path == "/slow" { - thread::sleep(Duration::from_secs(5)); - ("200 OK", "index.html") - } else { - ("404 NOT FOUND", "404.html") - }; - - let text = fs::read_to_string(file).unwrap(); - - let response = format!( - "HTTP/1.1 {}\r\n\ - Content-Type: text/html\r\n\ - Content-Length: {}\r\n\r\n{}", - code, - text.len(), - text); - - stream.write(response.as_bytes()).unwrap(); - stream.flush().unwrap(); -} - -fn main() { - let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); - - for stream in listener.incoming() { - let stream = stream.unwrap(); - thread::spawn(|| handle_connection(stream)); - } -}