I'm extending on chapter 20 of the book, where I created a multi-threaded web server. ThreadPool
implementation is independent of the kind of work our web server is doing, so we naturally separated them.
- The web server was written in the binary crate
src/bin/main.rs
- The
ThreadPool
implementation went intosrc/lib.rs
. Doing so makes the library crate the primary crate in the hello directory
Before I begin explaining the issue, here is the code located inside src/bin/main.rs
:
Hint: See handle_connection
use std::net::{TcpStream, TcpListener};
use std::io::prelude::*;
use std::fs;
use hello::ThreadPool;
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);
});
}
println!("Shutting down.");
}
fn handle_connection(mut stream: TcpStream) {
let mut buffer = [0; 512];
stream.read(&mut buffer).unwrap();
let get = b"GET / HTTP/1.1\r\n";
let (status_line, filename) = if buffer.starts_with(get) {
("HTTP/1.1 200 OK\r\n\r\n", "view/hello.html")
} else {
("HTTP/1.1 404 NOT FOUND\r\n\r\n", "view/404.html")
};
let contents = fs::read_to_string(filename).unwrap();
let response = format!("{}{}", status_line, contents);
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
Note: The ThreadPool
implementation is entirely written in src/lib.rs
(no problems here)
Writing an integration test
One of the challenges at the end of the book is to write integration tests. I created a new directory called tests
and made a file named tests/integration_test.rs
(The tests
directory is at the same level as src
).
Inside integration_test.rs
, I wanted to simulate a graceful shutdown by only accepting two requests before shutting down the server. (I was later planning on using the reqwest
crate to simulate a client).
This is where I ran into an issue...
use hello::ThreadPool;
// Can't bring `handle_connection()` into scope
use reqwest;
#[test]
fn graceful_shutdown() {
// use reqwest crate to simulate a client
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
let pool = ThreadPool::new(4);
for stream in listener.incoming().take(2) {
let stream = stream.unwrap();
pool.execute(|| {
handle_connection(stream);
});
}
println!("Shutting down.");
}
I discovered that I am unable to bring the handle_connection()
function into scope because it only exists in the binary (src/bin/main.rs
)
I was thinking of ways around this, like moving handle_connection
into lib.rs
, but I feel that it doesn't belong with the ThreadPool
implementation.
Any suggestions?
Thanks,
Connor