diff --git a/listings/ch20-web-server/listing-20-01/Cargo.toml b/listings/ch20-web-server/listing-20-01/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/listing-20-01/Cargo.toml +++ b/listings/ch20-web-server/listing-20-01/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/listing-20-01/src/main.rs b/listings/ch20-web-server/listing-20-01/src/main.rs index d868c3ec1..5279e73f3 100644 --- a/listings/ch20-web-server/listing-20-01/src/main.rs +++ b/listings/ch20-web-server/listing-20-01/src/main.rs @@ -6,6 +6,7 @@ fn main() { for stream in listener.incoming() { let stream = stream.unwrap(); + // 接続が確立しました! println!("Connection established!"); } } diff --git a/listings/ch20-web-server/listing-20-02/Cargo.toml b/listings/ch20-web-server/listing-20-02/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/listing-20-02/Cargo.toml +++ b/listings/ch20-web-server/listing-20-02/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/listing-20-02/src/main.rs b/listings/ch20-web-server/listing-20-02/src/main.rs index 2e68f2f74..7240c73c7 100644 --- a/listings/ch20-web-server/listing-20-02/src/main.rs +++ b/listings/ch20-web-server/listing-20-02/src/main.rs @@ -1,6 +1,7 @@ -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; +use std::{ + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, +}; fn main() { let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); @@ -13,9 +14,12 @@ fn main() { } fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; + let buf_reader = BufReader::new(&mut stream); + let http_request: Vec<_> = buf_reader + .lines() + .map(|result| result.unwrap()) + .take_while(|line| !line.is_empty()) + .collect(); - stream.read(&mut buffer).unwrap(); - - println!("Request: {}", String::from_utf8_lossy(&buffer[..])); + println!("Request: {:#?}", http_request); } diff --git a/listings/ch20-web-server/listing-20-03/Cargo.toml b/listings/ch20-web-server/listing-20-03/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/listing-20-03/Cargo.toml +++ b/listings/ch20-web-server/listing-20-03/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/listing-20-03/src/main.rs b/listings/ch20-web-server/listing-20-03/src/main.rs index afa579a45..c72d4a9c6 100644 --- a/listings/ch20-web-server/listing-20-03/src/main.rs +++ b/listings/ch20-web-server/listing-20-03/src/main.rs @@ -1,6 +1,7 @@ -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; +use std::{ + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, +}; fn main() { let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); @@ -14,13 +15,15 @@ fn main() { // ANCHOR: here fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - - stream.read(&mut buffer).unwrap(); + let buf_reader = BufReader::new(&mut stream); + let http_request: Vec<_> = buf_reader + .lines() + .map(|result| result.unwrap()) + .take_while(|line| !line.is_empty()) + .collect(); let response = "HTTP/1.1 200 OK\r\n\r\n"; - stream.write(response.as_bytes()).unwrap(); - stream.flush().unwrap(); + stream.write_all(response.as_bytes()).unwrap(); } // ANCHOR_END: here diff --git a/listings/ch20-web-server/listing-20-04/src/main.rs b/listings/ch20-web-server/listing-20-04/src/main.rs deleted file mode 100644 index 818eac97e..000000000 --- a/listings/ch20-web-server/listing-20-04/src/main.rs +++ /dev/null @@ -1,24 +0,0 @@ -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; - -fn main() { - let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); - - for stream in listener.incoming() { - let stream = stream.unwrap(); - - handle_connection(stream); - } -} - -fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - - stream.read(&mut buffer).unwrap(); - - let response = "HTTP/1.1 200 OK\r\n\r\n"; - - stream.write(response.as_bytes()).unwrap(); - stream.flush().unwrap(); -} diff --git a/listings/ch20-web-server/listing-20-05/Cargo.toml b/listings/ch20-web-server/listing-20-05/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/listing-20-05/Cargo.toml +++ b/listings/ch20-web-server/listing-20-05/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/listing-20-05/hello.html b/listings/ch20-web-server/listing-20-05/hello.html index fe442d6b9..a80a862cf 100644 --- a/listings/ch20-web-server/listing-20-05/hello.html +++ b/listings/ch20-web-server/listing-20-05/hello.html @@ -5,7 +5,9 @@ Hello! +

Hello!

+

Hi from Rust

diff --git a/listings/ch20-web-server/listing-20-05/src/main.rs b/listings/ch20-web-server/listing-20-05/src/main.rs index d20417c44..d4b78b640 100644 --- a/listings/ch20-web-server/listing-20-05/src/main.rs +++ b/listings/ch20-web-server/listing-20-05/src/main.rs @@ -1,12 +1,12 @@ // ANCHOR: here -use std::fs; +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, +}; // --snip-- // ANCHOR_END: here -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; - fn main() { let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); @@ -19,18 +19,20 @@ fn main() { // ANCHOR: here fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - + let buf_reader = BufReader::new(&mut stream); + let http_request: Vec<_> = buf_reader + .lines() + .map(|result| result.unwrap()) + .take_while(|line| !line.is_empty()) + .collect(); + + let status_line = "HTTP/1.1 200 OK"; let contents = fs::read_to_string("hello.html").unwrap(); + let length = contents.len(); - let response = format!( - "HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}", - contents.len(), - contents - ); + let response = + format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}"); - stream.write(response.as_bytes()).unwrap(); - stream.flush().unwrap(); + stream.write_all(response.as_bytes()).unwrap(); } // ANCHOR_END: here diff --git a/listings/ch20-web-server/listing-20-06/Cargo.toml b/listings/ch20-web-server/listing-20-06/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/listing-20-06/Cargo.toml +++ b/listings/ch20-web-server/listing-20-06/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/listing-20-06/src/main.rs b/listings/ch20-web-server/listing-20-06/src/main.rs index 835324422..fa5f0d502 100644 --- a/listings/ch20-web-server/listing-20-06/src/main.rs +++ b/listings/ch20-web-server/listing-20-06/src/main.rs @@ -1,7 +1,8 @@ -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, +}; fn main() { let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); @@ -12,28 +13,25 @@ fn main() { handle_connection(stream); } } - // ANCHOR: here // --snip-- fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - - let get = b"GET / HTTP/1.1\r\n"; + let buf_reader = BufReader::new(&mut stream); + let request_line = buf_reader.lines().next().unwrap().unwrap(); - if buffer.starts_with(get) { + if request_line == "GET / HTTP/1.1" { + let status_line = "HTTP/1.1 200 OK"; let contents = fs::read_to_string("hello.html").unwrap(); + let length = contents.len(); let response = format!( - "HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}", - contents.len(), - contents + "{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}" ); - stream.write(response.as_bytes()).unwrap(); - stream.flush().unwrap(); + stream.write_all(response.as_bytes()).unwrap(); } else { + // 何か別の要求 // some other request } } diff --git a/listings/ch20-web-server/no-listing-07-define-message-enum/404.html b/listings/ch20-web-server/listing-20-07/404.html similarity index 61% rename from listings/ch20-web-server/no-listing-07-define-message-enum/404.html rename to listings/ch20-web-server/listing-20-07/404.html index 88d8e9152..7feea11b8 100644 --- a/listings/ch20-web-server/no-listing-07-define-message-enum/404.html +++ b/listings/ch20-web-server/listing-20-07/404.html @@ -5,7 +5,9 @@ Hello! +

Oops!

+

Sorry, I don't know what you're asking for.

diff --git a/listings/ch20-web-server/listing-20-07/Cargo.toml b/listings/ch20-web-server/listing-20-07/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/listing-20-07/Cargo.toml +++ b/listings/ch20-web-server/listing-20-07/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/listing-20-07/src/main.rs b/listings/ch20-web-server/listing-20-07/src/main.rs index 4d8e51286..a14b7d538 100644 --- a/listings/ch20-web-server/listing-20-07/src/main.rs +++ b/listings/ch20-web-server/listing-20-07/src/main.rs @@ -1,7 +1,8 @@ -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, +}; fn main() { let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); @@ -14,32 +15,31 @@ fn main() { } fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); + let buf_reader = BufReader::new(&mut stream); + let request_line = buf_reader.lines().next().unwrap().unwrap(); - let get = b"GET / HTTP/1.1\r\n"; - - if buffer.starts_with(get) { + if request_line == "GET / HTTP/1.1" { + let status_line = "HTTP/1.1 200 OK"; let contents = fs::read_to_string("hello.html").unwrap(); + let length = contents.len(); let response = format!( - "HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}", - contents.len(), - contents + "{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}" ); - stream.write(response.as_bytes()).unwrap(); - stream.flush().unwrap(); + stream.write_all(response.as_bytes()).unwrap(); // ANCHOR: here // --snip-- } else { - let status_line = "HTTP/1.1 404 NOT FOUND\r\n\r\n"; + let status_line = "HTTP/1.1 404 NOT FOUND"; let contents = fs::read_to_string("404.html").unwrap(); + let length = contents.len(); - let response = format!("{}{}", status_line, contents); + let response = format!( + "{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}" + ); - stream.write(response.as_bytes()).unwrap(); - stream.flush().unwrap(); + stream.write_all(response.as_bytes()).unwrap(); } // ANCHOR_END: here } diff --git a/listings/ch20-web-server/listing-20-08/Cargo.lock b/listings/ch20-web-server/listing-20-08/Cargo.lock deleted file mode 100644 index f2d069f46..000000000 --- a/listings/ch20-web-server/listing-20-08/Cargo.lock +++ /dev/null @@ -1,6 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "hello" -version = "0.1.0" - diff --git a/listings/ch20-web-server/listing-20-08/Cargo.toml b/listings/ch20-web-server/listing-20-08/Cargo.toml deleted file mode 100644 index 78dfe6ebc..000000000 --- a/listings/ch20-web-server/listing-20-08/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "hello" -version = "0.1.0" -authors = ["Your Name "] -edition = "2018" - -[dependencies] diff --git a/listings/ch20-web-server/listing-20-08/hello.html b/listings/ch20-web-server/listing-20-08/hello.html deleted file mode 100644 index fe442d6b9..000000000 --- a/listings/ch20-web-server/listing-20-08/hello.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Hello! - - -

Hello!

-

Hi from Rust

- - diff --git a/listings/ch20-web-server/listing-20-08/src/main.rs b/listings/ch20-web-server/listing-20-08/src/main.rs deleted file mode 100644 index ef905fa27..000000000 --- a/listings/ch20-web-server/listing-20-08/src/main.rs +++ /dev/null @@ -1,42 +0,0 @@ -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; - -fn main() { - let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); - - for stream in listener.incoming() { - let stream = stream.unwrap(); - - handle_connection(stream); - } -} - -fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - - let get = b"GET / HTTP/1.1\r\n"; - - if buffer.starts_with(get) { - 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(); - } else { - let status_line = "HTTP/1.1 404 NOT FOUND\r\n\r\n"; - let contents = fs::read_to_string("404.html").unwrap(); - - let response = format!("{}{}", status_line, contents); - - stream.write(response.as_bytes()).unwrap(); - stream.flush().unwrap(); - } -} diff --git a/listings/ch20-web-server/listing-20-09/Cargo.toml b/listings/ch20-web-server/listing-20-09/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/listing-20-09/Cargo.toml +++ b/listings/ch20-web-server/listing-20-09/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/listing-20-09/src/main.rs b/listings/ch20-web-server/listing-20-09/src/main.rs index 8fdfd076a..ffc51e803 100644 --- a/listings/ch20-web-server/listing-20-09/src/main.rs +++ b/listings/ch20-web-server/listing-20-09/src/main.rs @@ -1,7 +1,8 @@ -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, +}; fn main() { let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); @@ -12,31 +13,28 @@ fn main() { handle_connection(stream); } } - // ANCHOR: here // --snip-- fn handle_connection(mut stream: TcpStream) { // --snip-- - // ANCHOR_END: here - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - - let get = b"GET / HTTP/1.1\r\n"; - + let buf_reader = BufReader::new(&mut stream); + let request_line = buf_reader.lines().next().unwrap().unwrap(); // ANCHOR: here - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") + + let (status_line, filename) = if request_line == "GET / HTTP/1.1" { + ("HTTP/1.1 200 OK", "hello.html") } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html") + ("HTTP/1.1 404 NOT FOUND", "404.html") }; let contents = fs::read_to_string(filename).unwrap(); + let length = contents.len(); - let response = format!("{}{}", status_line, contents); + let response = + format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}"); - stream.write(response.as_bytes()).unwrap(); - stream.flush().unwrap(); + stream.write_all(response.as_bytes()).unwrap(); } // ANCHOR_END: here diff --git a/listings/ch20-web-server/listing-20-10/Cargo.toml b/listings/ch20-web-server/listing-20-10/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/listing-20-10/Cargo.toml +++ b/listings/ch20-web-server/listing-20-10/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/listing-20-10/src/main.rs b/listings/ch20-web-server/listing-20-10/src/main.rs index 257d47a79..5a18b45c0 100644 --- a/listings/ch20-web-server/listing-20-10/src/main.rs +++ b/listings/ch20-web-server/listing-20-10/src/main.rs @@ -1,10 +1,11 @@ -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; // ANCHOR: here -use std::thread; -use std::time::Duration; +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, + thread, + time::Duration, +}; // --snip-- // ANCHOR_END: here @@ -23,31 +24,29 @@ fn handle_connection(mut stream: TcpStream) { // --snip-- // ANCHOR_END: here - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); + let buf_reader = BufReader::new(&mut stream); + let request_line = buf_reader.lines().next().unwrap().unwrap(); // ANCHOR: here - let get = b"GET / HTTP/1.1\r\n"; - let sleep = b"GET /sleep HTTP/1.1\r\n"; - - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else if buffer.starts_with(sleep) { - thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html") + let (status_line, filename) = match &request_line[..] { + "GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), + "GET /sleep HTTP/1.1" => { + thread::sleep(Duration::from_secs(5)); + ("HTTP/1.1 200 OK", "hello.html") + } + _ => ("HTTP/1.1 404 NOT FOUND", "404.html"), }; // --snip-- // ANCHOR_END: here let contents = fs::read_to_string(filename).unwrap(); + let length = contents.len(); - let response = format!("{}{}", status_line, contents); + let response = + format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}"); - stream.write(response.as_bytes()).unwrap(); - stream.flush().unwrap(); + stream.write_all(response.as_bytes()).unwrap(); // ANCHOR: here } // ANCHOR_END: here diff --git a/listings/ch20-web-server/listing-20-11/Cargo.toml b/listings/ch20-web-server/listing-20-11/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/listing-20-11/Cargo.toml +++ b/listings/ch20-web-server/listing-20-11/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/listing-20-11/src/main.rs b/listings/ch20-web-server/listing-20-11/src/main.rs index d1087e212..1181357b0 100644 --- a/listings/ch20-web-server/listing-20-11/src/main.rs +++ b/listings/ch20-web-server/listing-20-11/src/main.rs @@ -1,9 +1,10 @@ -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; -use std::thread; -use std::time::Duration; +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, + thread, + time::Duration, +}; // ANCHOR: here fn main() { @@ -20,25 +21,23 @@ fn main() { // ANCHOR_END: here fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - - let get = b"GET / HTTP/1.1\r\n"; - let sleep = b"GET /sleep HTTP/1.1\r\n"; - - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else if buffer.starts_with(sleep) { - thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html") + let buf_reader = BufReader::new(&mut stream); + let request_line = buf_reader.lines().next().unwrap().unwrap(); + + let (status_line, filename) = match &request_line[..] { + "GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), + "GET /sleep HTTP/1.1" => { + thread::sleep(Duration::from_secs(5)); + ("HTTP/1.1 200 OK", "hello.html") + } + _ => ("HTTP/1.1 404 NOT FOUND", "404.html"), }; let contents = fs::read_to_string(filename).unwrap(); + let length = contents.len(); - let response = format!("{}{}", status_line, contents); + let response = + format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}"); - stream.write(response.as_bytes()).unwrap(); - stream.flush().unwrap(); + stream.write_all(response.as_bytes()).unwrap(); } diff --git a/listings/ch20-web-server/listing-20-12/Cargo.toml b/listings/ch20-web-server/listing-20-12/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/listing-20-12/Cargo.toml +++ b/listings/ch20-web-server/listing-20-12/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/listing-20-12/output.txt b/listings/ch20-web-server/listing-20-12/output.txt index 3a798ef12..ca6a7bb93 100644 --- a/listings/ch20-web-server/listing-20-12/output.txt +++ b/listings/ch20-web-server/listing-20-12/output.txt @@ -1,14 +1,11 @@ $ cargo check Checking hello v0.1.0 (file:///projects/hello) -error[E0433]: failed to resolve: use of undeclared type or module `ThreadPool` - --> src/main.rs:10:16 +error[E0433]: failed to resolve: use of undeclared type `ThreadPool` +(エラー: 解決に失敗しました。未定義の型`ThreadPool`を使用しています) + --> src/main.rs:11:16 | -10 | let pool = ThreadPool::new(4); - | ^^^^^^^^^^ use of undeclared type or module `ThreadPool` - -error: aborting due to previous error +11 | let pool = ThreadPool::new(4); + | ^^^^^^^^^^ use of undeclared type `ThreadPool` For more information about this error, try `rustc --explain E0433`. -error: could not compile `hello`. - -To learn more, run the command again with --verbose. +error: could not compile `hello` (bin "hello") due to 1 previous error diff --git a/listings/ch20-web-server/listing-20-12/src/main.rs b/listings/ch20-web-server/listing-20-12/src/main.rs index 72c5b9a83..21b9a80f1 100644 --- a/listings/ch20-web-server/listing-20-12/src/main.rs +++ b/listings/ch20-web-server/listing-20-12/src/main.rs @@ -1,9 +1,10 @@ -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; -use std::thread; -use std::time::Duration; +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, + thread, + time::Duration, +}; // ANCHOR: here fn main() { @@ -21,25 +22,23 @@ fn main() { // ANCHOR_END: here fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - - let get = b"GET / HTTP/1.1\r\n"; - let sleep = b"GET /sleep HTTP/1.1\r\n"; - - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else if buffer.starts_with(sleep) { - thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html") + let buf_reader = BufReader::new(&mut stream); + let request_line = buf_reader.lines().next().unwrap().unwrap(); + + let (status_line, filename) = match &request_line[..] { + "GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), + "GET /sleep HTTP/1.1" => { + thread::sleep(Duration::from_secs(5)); + ("HTTP/1.1 200 OK", "hello.html") + } + _ => ("HTTP/1.1 404 NOT FOUND", "404.html"), }; let contents = fs::read_to_string(filename).unwrap(); + let length = contents.len(); - let response = format!("{}{}", status_line, contents); + let response = + format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}"); - stream.write(response.as_bytes()).unwrap(); - stream.flush().unwrap(); + stream.write_all(response.as_bytes()).unwrap(); } diff --git a/listings/ch20-web-server/listing-20-13/Cargo.toml b/listings/ch20-web-server/listing-20-13/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/listing-20-13/Cargo.toml +++ b/listings/ch20-web-server/listing-20-13/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/listing-20-13/src/bin/main.rs b/listings/ch20-web-server/listing-20-13/src/bin/main.rs deleted file mode 100644 index 9d6a9c154..000000000 --- a/listings/ch20-web-server/listing-20-13/src/bin/main.rs +++ /dev/null @@ -1,44 +0,0 @@ -use hello::ThreadPool; -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; -use std::thread; -use std::time::Duration; - -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); - }); - } -} - -fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - - let get = b"GET / HTTP/1.1\r\n"; - let sleep = b"GET /sleep HTTP/1.1\r\n"; - - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else if buffer.starts_with(sleep) { - thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "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(); -} diff --git a/listings/ch20-web-server/listing-20-13/src/lib.rs b/listings/ch20-web-server/listing-20-13/src/lib.rs index ffa3c6a25..5e34a8c7d 100644 --- a/listings/ch20-web-server/listing-20-13/src/lib.rs +++ b/listings/ch20-web-server/listing-20-13/src/lib.rs @@ -2,6 +2,14 @@ pub struct ThreadPool; // ANCHOR: here impl ThreadPool { + /// 新しいThreadPoolを生成する。 + /// + /// sizeがプールのスレッド数です。 + /// + /// # パニック + /// + /// sizeが0なら、`new`関数はパニックします。 + /// /// Create a new ThreadPool. /// /// The size is the number of threads in the pool. @@ -17,7 +25,6 @@ impl ThreadPool { // --snip-- // ANCHOR_END: here - pub fn execute(&self, f: F) where F: FnOnce() + Send + 'static, @@ -26,5 +33,3 @@ impl ThreadPool { // ANCHOR: here } // ANCHOR_END: here - -fn main() {} diff --git a/listings/ch20-web-server/listing-20-13/src/main.rs b/listings/ch20-web-server/listing-20-13/src/main.rs new file mode 100644 index 000000000..79efb28a2 --- /dev/null +++ b/listings/ch20-web-server/listing-20-13/src/main.rs @@ -0,0 +1,43 @@ +use hello::ThreadPool; +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, + thread, + time::Duration, +}; + +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); + }); + } +} + +fn handle_connection(mut stream: TcpStream) { + let buf_reader = BufReader::new(&mut stream); + let request_line = buf_reader.lines().next().unwrap().unwrap(); + + let (status_line, filename) = match &request_line[..] { + "GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), + "GET /sleep HTTP/1.1" => { + thread::sleep(Duration::from_secs(5)); + ("HTTP/1.1 200 OK", "hello.html") + } + _ => ("HTTP/1.1 404 NOT FOUND", "404.html"), + }; + + let contents = fs::read_to_string(filename).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(); +} diff --git a/listings/ch20-web-server/listing-20-14/Cargo.toml b/listings/ch20-web-server/listing-20-14/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/listing-20-14/Cargo.toml +++ b/listings/ch20-web-server/listing-20-14/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/listing-20-14/src/bin/main.rs b/listings/ch20-web-server/listing-20-14/src/bin/main.rs deleted file mode 100644 index 9d6a9c154..000000000 --- a/listings/ch20-web-server/listing-20-14/src/bin/main.rs +++ /dev/null @@ -1,44 +0,0 @@ -use hello::ThreadPool; -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; -use std::thread; -use std::time::Duration; - -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); - }); - } -} - -fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - - let get = b"GET / HTTP/1.1\r\n"; - let sleep = b"GET /sleep HTTP/1.1\r\n"; - - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else if buffer.starts_with(sleep) { - thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "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(); -} diff --git a/listings/ch20-web-server/listing-20-14/src/lib.rs b/listings/ch20-web-server/listing-20-14/src/lib.rs index 509a62e1f..400ff8f79 100644 --- a/listings/ch20-web-server/listing-20-14/src/lib.rs +++ b/listings/ch20-web-server/listing-20-14/src/lib.rs @@ -22,12 +22,12 @@ impl ThreadPool { let mut threads = Vec::with_capacity(size); for _ in 0..size { + // スレッドを作成してベクタに格納する // create some threads and store them in the vector } ThreadPool { threads } } - // --snip-- // ANCHOR_END: here diff --git a/listings/ch20-web-server/listing-20-14/src/main.rs b/listings/ch20-web-server/listing-20-14/src/main.rs new file mode 100644 index 000000000..79efb28a2 --- /dev/null +++ b/listings/ch20-web-server/listing-20-14/src/main.rs @@ -0,0 +1,43 @@ +use hello::ThreadPool; +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, + thread, + time::Duration, +}; + +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); + }); + } +} + +fn handle_connection(mut stream: TcpStream) { + let buf_reader = BufReader::new(&mut stream); + let request_line = buf_reader.lines().next().unwrap().unwrap(); + + let (status_line, filename) = match &request_line[..] { + "GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), + "GET /sleep HTTP/1.1" => { + thread::sleep(Duration::from_secs(5)); + ("HTTP/1.1 200 OK", "hello.html") + } + _ => ("HTTP/1.1 404 NOT FOUND", "404.html"), + }; + + let contents = fs::read_to_string(filename).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(); +} diff --git a/listings/ch20-web-server/listing-20-15/Cargo.toml b/listings/ch20-web-server/listing-20-15/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/listing-20-15/Cargo.toml +++ b/listings/ch20-web-server/listing-20-15/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/listing-20-15/src/lib.rs b/listings/ch20-web-server/listing-20-15/src/lib.rs index d2ad3ca17..80a6eeeb3 100644 --- a/listings/ch20-web-server/listing-20-15/src/lib.rs +++ b/listings/ch20-web-server/listing-20-15/src/lib.rs @@ -51,5 +51,3 @@ impl Worker { } } // ANCHOR_END: here - -fn main() {} diff --git a/listings/ch20-web-server/listing-20-15/src/main.rs b/listings/ch20-web-server/listing-20-15/src/main.rs new file mode 100644 index 000000000..79efb28a2 --- /dev/null +++ b/listings/ch20-web-server/listing-20-15/src/main.rs @@ -0,0 +1,43 @@ +use hello::ThreadPool; +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, + thread, + time::Duration, +}; + +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); + }); + } +} + +fn handle_connection(mut stream: TcpStream) { + let buf_reader = BufReader::new(&mut stream); + let request_line = buf_reader.lines().next().unwrap().unwrap(); + + let (status_line, filename) = match &request_line[..] { + "GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), + "GET /sleep HTTP/1.1" => { + thread::sleep(Duration::from_secs(5)); + ("HTTP/1.1 200 OK", "hello.html") + } + _ => ("HTTP/1.1 404 NOT FOUND", "404.html"), + }; + + let contents = fs::read_to_string(filename).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(); +} diff --git a/listings/ch20-web-server/listing-20-16/Cargo.toml b/listings/ch20-web-server/listing-20-16/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/listing-20-16/Cargo.toml +++ b/listings/ch20-web-server/listing-20-16/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/listing-20-16/src/bin/main.rs b/listings/ch20-web-server/listing-20-16/src/bin/main.rs deleted file mode 100644 index 9d6a9c154..000000000 --- a/listings/ch20-web-server/listing-20-16/src/bin/main.rs +++ /dev/null @@ -1,44 +0,0 @@ -use hello::ThreadPool; -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; -use std::thread; -use std::time::Duration; - -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); - }); - } -} - -fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - - let get = b"GET / HTTP/1.1\r\n"; - let sleep = b"GET /sleep HTTP/1.1\r\n"; - - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else if buffer.starts_with(sleep) { - thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "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(); -} diff --git a/listings/ch20-web-server/listing-20-16/src/lib.rs b/listings/ch20-web-server/listing-20-16/src/lib.rs index 28b374eed..411c1d003 100644 --- a/listings/ch20-web-server/listing-20-16/src/lib.rs +++ b/listings/ch20-web-server/listing-20-16/src/lib.rs @@ -1,7 +1,5 @@ -use std::thread; // ANCHOR: here -// --snip-- -use std::sync::mpsc; +use std::{sync::mpsc, thread}; pub struct ThreadPool { workers: Vec, @@ -58,5 +56,3 @@ impl Worker { Worker { id, thread } } } - -fn main() {} diff --git a/listings/ch20-web-server/listing-20-16/src/main.rs b/listings/ch20-web-server/listing-20-16/src/main.rs new file mode 100644 index 000000000..79efb28a2 --- /dev/null +++ b/listings/ch20-web-server/listing-20-16/src/main.rs @@ -0,0 +1,43 @@ +use hello::ThreadPool; +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, + thread, + time::Duration, +}; + +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); + }); + } +} + +fn handle_connection(mut stream: TcpStream) { + let buf_reader = BufReader::new(&mut stream); + let request_line = buf_reader.lines().next().unwrap().unwrap(); + + let (status_line, filename) = match &request_line[..] { + "GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), + "GET /sleep HTTP/1.1" => { + thread::sleep(Duration::from_secs(5)); + ("HTTP/1.1 200 OK", "hello.html") + } + _ => ("HTTP/1.1 404 NOT FOUND", "404.html"), + }; + + let contents = fs::read_to_string(filename).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(); +} diff --git a/listings/ch20-web-server/listing-20-17/Cargo.toml b/listings/ch20-web-server/listing-20-17/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/listing-20-17/Cargo.toml +++ b/listings/ch20-web-server/listing-20-17/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/listing-20-17/output.txt b/listings/ch20-web-server/listing-20-17/output.txt index f9749e121..cd06ca1b6 100644 --- a/listings/ch20-web-server/listing-20-17/output.txt +++ b/listings/ch20-web-server/listing-20-17/output.txt @@ -1,17 +1,21 @@ $ cargo check Checking hello v0.1.0 (file:///projects/hello) error[E0382]: use of moved value: `receiver` - --> src/lib.rs:27:42 + --> src/lib.rs:26:42 | -22 | let (sender, receiver) = mpsc::channel(); +21 | let (sender, receiver) = mpsc::channel(); | -------- move occurs because `receiver` has type `std::sync::mpsc::Receiver`, which does not implement the `Copy` trait ... -27 | workers.push(Worker::new(id, receiver)); +25 | for id in 0..size { + | ----------------- inside of this loop +26 | workers.push(Worker::new(id, receiver)); | ^^^^^^^^ value moved here, in previous iteration of loop - -error: aborting due to previous error + | +note: consider changing this parameter type in method `new` to borrow instead if owning the value isn't necessary + --> src/lib.rs:47:33 + | +47 | fn new(id: usize, receiver: mpsc::Receiver) -> Worker { + | --- in this method ^^^^^^^^^^^^^^^^^^^ this parameter takes ownership of the value For more information about this error, try `rustc --explain E0382`. -error: could not compile `hello`. - -To learn more, run the command again with --verbose. +error: could not compile `hello` (lib) due to 1 previous error diff --git a/listings/ch20-web-server/listing-20-17/src/bin/main.rs b/listings/ch20-web-server/listing-20-17/src/bin/main.rs deleted file mode 100644 index 9d6a9c154..000000000 --- a/listings/ch20-web-server/listing-20-17/src/bin/main.rs +++ /dev/null @@ -1,44 +0,0 @@ -use hello::ThreadPool; -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; -use std::thread; -use std::time::Duration; - -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); - }); - } -} - -fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - - let get = b"GET / HTTP/1.1\r\n"; - let sleep = b"GET /sleep HTTP/1.1\r\n"; - - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else if buffer.starts_with(sleep) { - thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "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(); -} diff --git a/listings/ch20-web-server/listing-20-17/src/lib.rs b/listings/ch20-web-server/listing-20-17/src/lib.rs index f3ce7d06e..d764879e5 100644 --- a/listings/ch20-web-server/listing-20-17/src/lib.rs +++ b/listings/ch20-web-server/listing-20-17/src/lib.rs @@ -1,5 +1,4 @@ -use std::sync::mpsc; -use std::thread; +use std::{sync::mpsc, thread}; pub struct ThreadPool { workers: Vec, diff --git a/listings/ch20-web-server/listing-20-17/src/main.rs b/listings/ch20-web-server/listing-20-17/src/main.rs new file mode 100644 index 000000000..79efb28a2 --- /dev/null +++ b/listings/ch20-web-server/listing-20-17/src/main.rs @@ -0,0 +1,43 @@ +use hello::ThreadPool; +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, + thread, + time::Duration, +}; + +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); + }); + } +} + +fn handle_connection(mut stream: TcpStream) { + let buf_reader = BufReader::new(&mut stream); + let request_line = buf_reader.lines().next().unwrap().unwrap(); + + let (status_line, filename) = match &request_line[..] { + "GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), + "GET /sleep HTTP/1.1" => { + thread::sleep(Duration::from_secs(5)); + ("HTTP/1.1 200 OK", "hello.html") + } + _ => ("HTTP/1.1 404 NOT FOUND", "404.html"), + }; + + let contents = fs::read_to_string(filename).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(); +} diff --git a/listings/ch20-web-server/listing-20-18/Cargo.toml b/listings/ch20-web-server/listing-20-18/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/listing-20-18/Cargo.toml +++ b/listings/ch20-web-server/listing-20-18/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/listing-20-18/src/bin/main.rs b/listings/ch20-web-server/listing-20-18/src/bin/main.rs deleted file mode 100644 index 9d6a9c154..000000000 --- a/listings/ch20-web-server/listing-20-18/src/bin/main.rs +++ /dev/null @@ -1,44 +0,0 @@ -use hello::ThreadPool; -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; -use std::thread; -use std::time::Duration; - -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); - }); - } -} - -fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - - let get = b"GET / HTTP/1.1\r\n"; - let sleep = b"GET /sleep HTTP/1.1\r\n"; - - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else if buffer.starts_with(sleep) { - thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "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(); -} diff --git a/listings/ch20-web-server/listing-20-18/src/lib.rs b/listings/ch20-web-server/listing-20-18/src/lib.rs index d47eef869..4bff8acad 100644 --- a/listings/ch20-web-server/listing-20-18/src/lib.rs +++ b/listings/ch20-web-server/listing-20-18/src/lib.rs @@ -1,8 +1,8 @@ -use std::sync::mpsc; -use std::thread; // ANCHOR: here -use std::sync::Arc; -use std::sync::Mutex; +use std::{ + sync::{mpsc, Arc, Mutex}, + thread, +}; // --snip-- // ANCHOR_END: here @@ -74,5 +74,3 @@ impl Worker { } } // ANCHOR_END: here - -fn main() {} diff --git a/listings/ch20-web-server/listing-20-18/src/main.rs b/listings/ch20-web-server/listing-20-18/src/main.rs new file mode 100644 index 000000000..79efb28a2 --- /dev/null +++ b/listings/ch20-web-server/listing-20-18/src/main.rs @@ -0,0 +1,43 @@ +use hello::ThreadPool; +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, + thread, + time::Duration, +}; + +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); + }); + } +} + +fn handle_connection(mut stream: TcpStream) { + let buf_reader = BufReader::new(&mut stream); + let request_line = buf_reader.lines().next().unwrap().unwrap(); + + let (status_line, filename) = match &request_line[..] { + "GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), + "GET /sleep HTTP/1.1" => { + thread::sleep(Duration::from_secs(5)); + ("HTTP/1.1 200 OK", "hello.html") + } + _ => ("HTTP/1.1 404 NOT FOUND", "404.html"), + }; + + let contents = fs::read_to_string(filename).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(); +} diff --git a/listings/ch20-web-server/listing-20-19/Cargo.toml b/listings/ch20-web-server/listing-20-19/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/listing-20-19/Cargo.toml +++ b/listings/ch20-web-server/listing-20-19/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/listing-20-19/src/bin/main.rs b/listings/ch20-web-server/listing-20-19/src/bin/main.rs deleted file mode 100644 index 9d6a9c154..000000000 --- a/listings/ch20-web-server/listing-20-19/src/bin/main.rs +++ /dev/null @@ -1,44 +0,0 @@ -use hello::ThreadPool; -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; -use std::thread; -use std::time::Duration; - -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); - }); - } -} - -fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - - let get = b"GET / HTTP/1.1\r\n"; - let sleep = b"GET /sleep HTTP/1.1\r\n"; - - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else if buffer.starts_with(sleep) { - thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "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(); -} diff --git a/listings/ch20-web-server/listing-20-19/src/lib.rs b/listings/ch20-web-server/listing-20-19/src/lib.rs index bf8127b8e..aeb1facd6 100644 --- a/listings/ch20-web-server/listing-20-19/src/lib.rs +++ b/listings/ch20-web-server/listing-20-19/src/lib.rs @@ -1,7 +1,7 @@ -use std::sync::mpsc; -use std::sync::Arc; -use std::sync::Mutex; -use std::thread; +use std::{ + sync::{mpsc, Arc, Mutex}, + thread, +}; pub struct ThreadPool { workers: Vec, @@ -67,5 +67,3 @@ impl Worker { Worker { id, thread } } } - -fn main() {} diff --git a/listings/ch20-web-server/listing-20-19/src/main.rs b/listings/ch20-web-server/listing-20-19/src/main.rs new file mode 100644 index 000000000..79efb28a2 --- /dev/null +++ b/listings/ch20-web-server/listing-20-19/src/main.rs @@ -0,0 +1,43 @@ +use hello::ThreadPool; +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, + thread, + time::Duration, +}; + +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); + }); + } +} + +fn handle_connection(mut stream: TcpStream) { + let buf_reader = BufReader::new(&mut stream); + let request_line = buf_reader.lines().next().unwrap().unwrap(); + + let (status_line, filename) = match &request_line[..] { + "GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), + "GET /sleep HTTP/1.1" => { + thread::sleep(Duration::from_secs(5)); + ("HTTP/1.1 200 OK", "hello.html") + } + _ => ("HTTP/1.1 404 NOT FOUND", "404.html"), + }; + + let contents = fs::read_to_string(filename).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(); +} diff --git a/listings/ch20-web-server/listing-20-20/Cargo.toml b/listings/ch20-web-server/listing-20-20/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/listing-20-20/Cargo.toml +++ b/listings/ch20-web-server/listing-20-20/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/listing-20-20/src/bin/main.rs b/listings/ch20-web-server/listing-20-20/src/bin/main.rs deleted file mode 100644 index 9d6a9c154..000000000 --- a/listings/ch20-web-server/listing-20-20/src/bin/main.rs +++ /dev/null @@ -1,44 +0,0 @@ -use hello::ThreadPool; -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; -use std::thread; -use std::time::Duration; - -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); - }); - } -} - -fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - - let get = b"GET / HTTP/1.1\r\n"; - let sleep = b"GET /sleep HTTP/1.1\r\n"; - - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else if buffer.starts_with(sleep) { - thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "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(); -} diff --git a/listings/ch20-web-server/listing-20-20/src/lib.rs b/listings/ch20-web-server/listing-20-20/src/lib.rs index 5fdc32e8b..a8ffecabe 100644 --- a/listings/ch20-web-server/listing-20-20/src/lib.rs +++ b/listings/ch20-web-server/listing-20-20/src/lib.rs @@ -1,7 +1,7 @@ -use std::sync::mpsc; -use std::sync::Arc; -use std::sync::Mutex; -use std::thread; +use std::{ + sync::{mpsc, Arc, Mutex}, + thread, +}; pub struct ThreadPool { workers: Vec, @@ -57,7 +57,8 @@ impl Worker { let thread = thread::spawn(move || loop { let job = receiver.lock().unwrap().recv().unwrap(); - println!("Worker {} got a job; executing.", id); + // ワーカー{id}は仕事を得ました; 実行します。 + println!("Worker {id} got a job; executing."); job(); }); diff --git a/listings/ch20-web-server/listing-20-20/src/main.rs b/listings/ch20-web-server/listing-20-20/src/main.rs new file mode 100644 index 000000000..79efb28a2 --- /dev/null +++ b/listings/ch20-web-server/listing-20-20/src/main.rs @@ -0,0 +1,43 @@ +use hello::ThreadPool; +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, + thread, + time::Duration, +}; + +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); + }); + } +} + +fn handle_connection(mut stream: TcpStream) { + let buf_reader = BufReader::new(&mut stream); + let request_line = buf_reader.lines().next().unwrap().unwrap(); + + let (status_line, filename) = match &request_line[..] { + "GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), + "GET /sleep HTTP/1.1" => { + thread::sleep(Duration::from_secs(5)); + ("HTTP/1.1 200 OK", "hello.html") + } + _ => ("HTTP/1.1 404 NOT FOUND", "404.html"), + }; + + let contents = fs::read_to_string(filename).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(); +} diff --git a/listings/ch20-web-server/listing-20-21/Cargo.toml b/listings/ch20-web-server/listing-20-21/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/listing-20-21/Cargo.toml +++ b/listings/ch20-web-server/listing-20-21/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/listing-20-21/src/bin/main.rs b/listings/ch20-web-server/listing-20-21/src/bin/main.rs deleted file mode 100644 index 9d6a9c154..000000000 --- a/listings/ch20-web-server/listing-20-21/src/bin/main.rs +++ /dev/null @@ -1,44 +0,0 @@ -use hello::ThreadPool; -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; -use std::thread; -use std::time::Duration; - -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); - }); - } -} - -fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - - let get = b"GET / HTTP/1.1\r\n"; - let sleep = b"GET /sleep HTTP/1.1\r\n"; - - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else if buffer.starts_with(sleep) { - thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "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(); -} diff --git a/listings/ch20-web-server/listing-20-21/src/lib.rs b/listings/ch20-web-server/listing-20-21/src/lib.rs index 6bde52488..17b37e77b 100644 --- a/listings/ch20-web-server/listing-20-21/src/lib.rs +++ b/listings/ch20-web-server/listing-20-21/src/lib.rs @@ -1,7 +1,7 @@ -use std::sync::mpsc; -use std::sync::Arc; -use std::sync::Mutex; -use std::thread; +use std::{ + sync::{mpsc, Arc, Mutex}, + thread, +}; pub struct ThreadPool { workers: Vec, @@ -55,7 +55,7 @@ impl Worker { fn new(id: usize, receiver: Arc>>) -> Worker { let thread = thread::spawn(move || { while let Ok(job) = receiver.lock().unwrap().recv() { - println!("Worker {} got a job; executing.", id); + println!("Worker {id} got a job; executing."); job(); } diff --git a/listings/ch20-web-server/listing-20-21/src/main.rs b/listings/ch20-web-server/listing-20-21/src/main.rs new file mode 100644 index 000000000..79efb28a2 --- /dev/null +++ b/listings/ch20-web-server/listing-20-21/src/main.rs @@ -0,0 +1,43 @@ +use hello::ThreadPool; +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, + thread, + time::Duration, +}; + +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); + }); + } +} + +fn handle_connection(mut stream: TcpStream) { + let buf_reader = BufReader::new(&mut stream); + let request_line = buf_reader.lines().next().unwrap().unwrap(); + + let (status_line, filename) = match &request_line[..] { + "GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), + "GET /sleep HTTP/1.1" => { + thread::sleep(Duration::from_secs(5)); + ("HTTP/1.1 200 OK", "hello.html") + } + _ => ("HTTP/1.1 404 NOT FOUND", "404.html"), + }; + + let contents = fs::read_to_string(filename).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(); +} diff --git a/listings/ch20-web-server/listing-20-22/Cargo.toml b/listings/ch20-web-server/listing-20-22/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/listing-20-22/Cargo.toml +++ b/listings/ch20-web-server/listing-20-22/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/listing-20-22/output.txt b/listings/ch20-web-server/listing-20-22/output.txt index f5dd1ba13..35588617d 100644 --- a/listings/ch20-web-server/listing-20-22/output.txt +++ b/listings/ch20-web-server/listing-20-22/output.txt @@ -4,11 +4,12 @@ error[E0507]: cannot move out of `worker.thread` which is behind a mutable refer --> src/lib.rs:52:13 | 52 | worker.thread.join().unwrap(); - | ^^^^^^^^^^^^^ move occurs because `worker.thread` has type `std::thread::JoinHandle<()>`, which does not implement the `Copy` trait - -error: aborting due to previous error + | ^^^^^^^^^^^^^ ------ `worker.thread` moved due to this method call + | | + | move occurs because `worker.thread` has type `JoinHandle<()>`, which does not implement the `Copy` trait + | +note: `JoinHandle::::join` takes ownership of the receiver `self`, which moves `worker.thread` + --> /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/thread/mod.rs:1649:17 For more information about this error, try `rustc --explain E0507`. -error: could not compile `hello`. - -To learn more, run the command again with --verbose. +error: could not compile `hello` (lib) due to 1 previous error diff --git a/listings/ch20-web-server/listing-20-22/src/bin/main.rs b/listings/ch20-web-server/listing-20-22/src/bin/main.rs deleted file mode 100644 index 9d6a9c154..000000000 --- a/listings/ch20-web-server/listing-20-22/src/bin/main.rs +++ /dev/null @@ -1,44 +0,0 @@ -use hello::ThreadPool; -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; -use std::thread; -use std::time::Duration; - -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); - }); - } -} - -fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - - let get = b"GET / HTTP/1.1\r\n"; - let sleep = b"GET /sleep HTTP/1.1\r\n"; - - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else if buffer.starts_with(sleep) { - thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "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(); -} diff --git a/listings/ch20-web-server/listing-20-22/src/lib.rs b/listings/ch20-web-server/listing-20-22/src/lib.rs index 824257898..cb3141810 100644 --- a/listings/ch20-web-server/listing-20-22/src/lib.rs +++ b/listings/ch20-web-server/listing-20-22/src/lib.rs @@ -1,7 +1,7 @@ -use std::sync::mpsc; -use std::sync::Arc; -use std::sync::Mutex; -use std::thread; +use std::{ + sync::{mpsc, Arc, Mutex}, + thread, +}; pub struct ThreadPool { workers: Vec, @@ -48,6 +48,7 @@ impl ThreadPool { impl Drop for ThreadPool { fn drop(&mut self) { for worker in &mut self.workers { + // ワーカー{}を終了します println!("Shutting down worker {}", worker.id); worker.thread.join().unwrap(); @@ -66,7 +67,7 @@ impl Worker { let thread = thread::spawn(move || loop { let job = receiver.lock().unwrap().recv().unwrap(); - println!("Worker {} got a job; executing.", id); + println!("Worker {id} got a job; executing."); job(); }); diff --git a/listings/ch20-web-server/listing-20-22/src/main.rs b/listings/ch20-web-server/listing-20-22/src/main.rs new file mode 100644 index 000000000..79efb28a2 --- /dev/null +++ b/listings/ch20-web-server/listing-20-22/src/main.rs @@ -0,0 +1,43 @@ +use hello::ThreadPool; +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, + thread, + time::Duration, +}; + +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); + }); + } +} + +fn handle_connection(mut stream: TcpStream) { + let buf_reader = BufReader::new(&mut stream); + let request_line = buf_reader.lines().next().unwrap().unwrap(); + + let (status_line, filename) = match &request_line[..] { + "GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), + "GET /sleep HTTP/1.1" => { + thread::sleep(Duration::from_secs(5)); + ("HTTP/1.1 200 OK", "hello.html") + } + _ => ("HTTP/1.1 404 NOT FOUND", "404.html"), + }; + + let contents = fs::read_to_string(filename).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(); +} diff --git a/listings/ch20-web-server/listing-20-23/Cargo.toml b/listings/ch20-web-server/listing-20-23/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/listing-20-23/Cargo.toml +++ b/listings/ch20-web-server/listing-20-23/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/listing-20-23/src/bin/main.rs b/listings/ch20-web-server/listing-20-23/src/bin/main.rs deleted file mode 100644 index 9d6a9c154..000000000 --- a/listings/ch20-web-server/listing-20-23/src/bin/main.rs +++ /dev/null @@ -1,44 +0,0 @@ -use hello::ThreadPool; -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; -use std::thread; -use std::time::Duration; - -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); - }); - } -} - -fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - - let get = b"GET / HTTP/1.1\r\n"; - let sleep = b"GET /sleep HTTP/1.1\r\n"; - - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else if buffer.starts_with(sleep) { - thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "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(); -} diff --git a/listings/ch20-web-server/listing-20-23/src/lib.rs b/listings/ch20-web-server/listing-20-23/src/lib.rs index 6ccaee73f..eea339b02 100644 --- a/listings/ch20-web-server/listing-20-23/src/lib.rs +++ b/listings/ch20-web-server/listing-20-23/src/lib.rs @@ -1,28 +1,20 @@ -use std::sync::mpsc; -use std::sync::Arc; -use std::sync::Mutex; -use std::thread; +use std::{ + sync::{mpsc, Arc, Mutex}, + thread, +}; // ANCHOR: here pub struct ThreadPool { workers: Vec, - sender: mpsc::Sender, + sender: Option>, } - // --snip-- - // ANCHOR_END: here -type Job = Box; -enum Message { - NewJob(Job), - Terminate, -} +type Job = Box; // ANCHOR: here impl ThreadPool { - // --snip-- - // ANCHOR_END: here /// Create a new ThreadPool. /// @@ -31,7 +23,11 @@ impl ThreadPool { /// # Panics /// /// The `new` function will panic if the size is zero. + // ANCHOR: here pub fn new(size: usize) -> ThreadPool { + // --snip-- + + // ANCHOR_END: here assert!(size > 0); let (sender, receiver) = mpsc::channel(); @@ -44,25 +40,27 @@ impl ThreadPool { workers.push(Worker::new(id, Arc::clone(&receiver))); } - ThreadPool { workers, sender } + // ANCHOR: here + ThreadPool { + workers, + sender: Some(sender), + } } - // ANCHOR: here pub fn execute(&self, f: F) where F: FnOnce() + Send + 'static, { let job = Box::new(f); - self.sender.send(Message::NewJob(job)).unwrap(); + self.sender.as_ref().unwrap().send(job).unwrap(); } } -// --snip-- - -// ANCHOR_END: here impl Drop for ThreadPool { fn drop(&mut self) { + drop(self.sender.take()); + for worker in &mut self.workers { println!("Shutting down worker {}", worker.id); @@ -72,30 +70,21 @@ impl Drop for ThreadPool { } } } +// ANCHOR_END: here struct Worker { id: usize, thread: Option>, } -// ANCHOR: here impl Worker { - fn new(id: usize, receiver: Arc>>) -> Worker { + fn new(id: usize, receiver: Arc>>) -> Worker { let thread = thread::spawn(move || loop { - let message = receiver.lock().unwrap().recv().unwrap(); - - match message { - Message::NewJob(job) => { - println!("Worker {} got a job; executing.", id); + let job = receiver.lock().unwrap().recv().unwrap(); - job(); - } - Message::Terminate => { - println!("Worker {} was told to terminate.", id); + println!("Worker {id} got a job; executing."); - break; - } - } + job(); }); Worker { @@ -104,4 +93,3 @@ impl Worker { } } } -// ANCHOR_END: here diff --git a/listings/ch20-web-server/listing-20-23/src/main.rs b/listings/ch20-web-server/listing-20-23/src/main.rs new file mode 100644 index 000000000..b6aa046d1 --- /dev/null +++ b/listings/ch20-web-server/listing-20-23/src/main.rs @@ -0,0 +1,45 @@ +use hello::ThreadPool; +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, + thread, + time::Duration, +}; + +fn main() { + 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."); +} + +fn handle_connection(mut stream: TcpStream) { + let buf_reader = BufReader::new(&mut stream); + let request_line = buf_reader.lines().next().unwrap().unwrap(); + + let (status_line, filename) = match &request_line[..] { + "GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), + "GET /sleep HTTP/1.1" => { + thread::sleep(Duration::from_secs(5)); + ("HTTP/1.1 200 OK", "hello.html") + } + _ => ("HTTP/1.1 404 NOT FOUND", "404.html"), + }; + + let contents = fs::read_to_string(filename).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(); +} diff --git a/listings/ch20-web-server/listing-20-24/Cargo.toml b/listings/ch20-web-server/listing-20-24/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/listing-20-24/Cargo.toml +++ b/listings/ch20-web-server/listing-20-24/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/listing-20-24/src/bin/main.rs b/listings/ch20-web-server/listing-20-24/src/bin/main.rs deleted file mode 100644 index 9d6a9c154..000000000 --- a/listings/ch20-web-server/listing-20-24/src/bin/main.rs +++ /dev/null @@ -1,44 +0,0 @@ -use hello::ThreadPool; -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; -use std::thread; -use std::time::Duration; - -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); - }); - } -} - -fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - - let get = b"GET / HTTP/1.1\r\n"; - let sleep = b"GET /sleep HTTP/1.1\r\n"; - - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else if buffer.starts_with(sleep) { - thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "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(); -} diff --git a/listings/ch20-web-server/listing-20-24/src/lib.rs b/listings/ch20-web-server/listing-20-24/src/lib.rs index 747bff2b1..162ba1b13 100644 --- a/listings/ch20-web-server/listing-20-24/src/lib.rs +++ b/listings/ch20-web-server/listing-20-24/src/lib.rs @@ -1,20 +1,15 @@ -use std::sync::mpsc; -use std::sync::Arc; -use std::sync::Mutex; -use std::thread; +use std::{ + sync::{mpsc, Arc, Mutex}, + thread, +}; pub struct ThreadPool { workers: Vec, - sender: mpsc::Sender, + sender: Option>, } type Job = Box; -enum Message { - NewJob(Job), - Terminate, -} - impl ThreadPool { /// Create a new ThreadPool. /// @@ -36,7 +31,10 @@ impl ThreadPool { workers.push(Worker::new(id, Arc::clone(&receiver))); } - ThreadPool { workers, sender } + ThreadPool { + workers, + sender: Some(sender), + } } pub fn execute(&self, f: F) @@ -45,20 +43,13 @@ impl ThreadPool { { let job = Box::new(f); - self.sender.send(Message::NewJob(job)).unwrap(); + self.sender.as_ref().unwrap().send(job).unwrap(); } } -// ANCHOR: here impl Drop for ThreadPool { fn drop(&mut self) { - println!("Sending terminate message to all workers."); - - for _ in &self.workers { - self.sender.send(Message::Terminate).unwrap(); - } - - println!("Shutting down all workers."); + drop(self.sender.take()); for worker in &mut self.workers { println!("Shutting down worker {}", worker.id); @@ -69,27 +60,27 @@ impl Drop for ThreadPool { } } } -// ANCHOR_END: here struct Worker { id: usize, thread: Option>, } +// ANCHOR: here impl Worker { - fn new(id: usize, receiver: Arc>>) -> Worker { + fn new(id: usize, receiver: Arc>>) -> Worker { let thread = thread::spawn(move || loop { - let message = receiver.lock().unwrap().recv().unwrap(); + let message = receiver.lock().unwrap().recv(); match message { - Message::NewJob(job) => { - println!("Worker {} got a job; executing.", id); + Ok(job) => { + println!("Worker {id} got a job; executing."); job(); } - Message::Terminate => { - println!("Worker {} was told to terminate.", id); - + Err(_) => { + // ワーカー{id}は切断されました; 停止します。 + println!("Worker {id} disconnected; shutting down."); break; } } @@ -101,3 +92,4 @@ impl Worker { } } } +// ANCHOR_END: here diff --git a/listings/ch20-web-server/listing-20-24/src/main.rs b/listings/ch20-web-server/listing-20-24/src/main.rs new file mode 100644 index 000000000..b6aa046d1 --- /dev/null +++ b/listings/ch20-web-server/listing-20-24/src/main.rs @@ -0,0 +1,45 @@ +use hello::ThreadPool; +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, + thread, + time::Duration, +}; + +fn main() { + 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."); +} + +fn handle_connection(mut stream: TcpStream) { + let buf_reader = BufReader::new(&mut stream); + let request_line = buf_reader.lines().next().unwrap().unwrap(); + + let (status_line, filename) = match &request_line[..] { + "GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), + "GET /sleep HTTP/1.1" => { + thread::sleep(Duration::from_secs(5)); + ("HTTP/1.1 200 OK", "hello.html") + } + _ => ("HTTP/1.1 404 NOT FOUND", "404.html"), + }; + + let contents = fs::read_to_string(filename).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(); +} diff --git a/listings/ch20-web-server/listing-20-25/Cargo.toml b/listings/ch20-web-server/listing-20-25/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/listing-20-25/Cargo.toml +++ b/listings/ch20-web-server/listing-20-25/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/listing-20-25/src/lib.rs b/listings/ch20-web-server/listing-20-25/src/lib.rs index 424eac8e4..54c0489ab 100644 --- a/listings/ch20-web-server/listing-20-25/src/lib.rs +++ b/listings/ch20-web-server/listing-20-25/src/lib.rs @@ -1,21 +1,15 @@ -// ANCHOR: here -use std::sync::mpsc; -use std::sync::Arc; -use std::sync::Mutex; -use std::thread; +use std::{ + sync::{mpsc, Arc, Mutex}, + thread, +}; pub struct ThreadPool { workers: Vec, - sender: mpsc::Sender, + sender: Option>, } type Job = Box; -enum Message { - NewJob(Job), - Terminate, -} - impl ThreadPool { /// Create a new ThreadPool. /// @@ -37,7 +31,10 @@ impl ThreadPool { workers.push(Worker::new(id, Arc::clone(&receiver))); } - ThreadPool { workers, sender } + ThreadPool { + workers, + sender: Some(sender), + } } pub fn execute(&self, f: F) @@ -46,19 +43,13 @@ impl ThreadPool { { let job = Box::new(f); - self.sender.send(Message::NewJob(job)).unwrap(); + self.sender.as_ref().unwrap().send(job).unwrap(); } } impl Drop for ThreadPool { fn drop(&mut self) { - println!("Sending terminate message to all workers."); - - for _ in &self.workers { - self.sender.send(Message::Terminate).unwrap(); - } - - println!("Shutting down all workers."); + drop(self.sender.take()); for worker in &mut self.workers { println!("Shutting down worker {}", worker.id); @@ -76,19 +67,18 @@ struct Worker { } impl Worker { - fn new(id: usize, receiver: Arc>>) -> Worker { + fn new(id: usize, receiver: Arc>>) -> Worker { let thread = thread::spawn(move || loop { - let message = receiver.lock().unwrap().recv().unwrap(); + let message = receiver.lock().unwrap().recv(); match message { - Message::NewJob(job) => { - println!("Worker {} got a job; executing.", id); + Ok(job) => { + println!("Worker {id} got a job; executing."); job(); } - Message::Terminate => { - println!("Worker {} was told to terminate.", id); - + Err(_) => { + println!("Worker {id} disconnected; shutting down."); break; } } @@ -100,6 +90,3 @@ impl Worker { } } } -// ANCHOR_END: here - -fn main() {} diff --git a/listings/ch20-web-server/listing-20-25/src/bin/main.rs b/listings/ch20-web-server/listing-20-25/src/main.rs similarity index 74% rename from listings/ch20-web-server/listing-20-25/src/bin/main.rs rename to listings/ch20-web-server/listing-20-25/src/main.rs index 22534d2f4..a649ff103 100644 --- a/listings/ch20-web-server/listing-20-25/src/bin/main.rs +++ b/listings/ch20-web-server/listing-20-25/src/main.rs @@ -1,4 +1,3 @@ -// ANCHOR: all use hello::ThreadPool; use std::fs; use std::io::prelude::*; @@ -32,19 +31,23 @@ fn handle_connection(mut stream: TcpStream) { let sleep = b"GET /sleep HTTP/1.1\r\n"; let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") + ("HTTP/1.1 200 OK", "hello.html") } else if buffer.starts_with(sleep) { thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") + ("HTTP/1.1 200 OK", "hello.html") } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html") + ("HTTP/1.1 404 NOT FOUND", "404.html") }; let contents = fs::read_to_string(filename).unwrap(); - let response = format!("{}{}", status_line, contents); + let response = format!( + "{}\r\nContent-Length: {}\r\n\r\n{}", + status_line, + contents.len(), + contents + ); - stream.write(response.as_bytes()).unwrap(); + stream.write_all(response.as_bytes()).unwrap(); stream.flush().unwrap(); } -// ANCHOR_END: all diff --git a/listings/ch20-web-server/no-listing-01-define-threadpool-struct/Cargo.toml b/listings/ch20-web-server/no-listing-01-define-threadpool-struct/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/no-listing-01-define-threadpool-struct/Cargo.toml +++ b/listings/ch20-web-server/no-listing-01-define-threadpool-struct/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/no-listing-01-define-threadpool-struct/output.txt b/listings/ch20-web-server/no-listing-01-define-threadpool-struct/output.txt index 0161c7b2c..953b01d15 100644 --- a/listings/ch20-web-server/no-listing-01-define-threadpool-struct/output.txt +++ b/listings/ch20-web-server/no-listing-01-define-threadpool-struct/output.txt @@ -1,14 +1,11 @@ $ cargo check Checking hello v0.1.0 (file:///projects/hello) -error[E0599]: no function or associated item named `new` found for type `hello::ThreadPool` in the current scope - --> src/bin/main.rs:11:28 +error[E0599]: no function or associated item named `new` found for struct `ThreadPool` in the current scope +(エラー: 現在のスコープで構造体`ThreadPool`の関数または関連アイテムに`new`というものが見つかりません) + --> src/main.rs:12:28 | -11 | let pool = ThreadPool::new(4); - | ^^^ function or associated item not found in `hello::ThreadPool` - -error: aborting due to previous error +12 | let pool = ThreadPool::new(4); + | ^^^ function or associated item not found in `ThreadPool` For more information about this error, try `rustc --explain E0599`. -error: could not compile `hello`. - -To learn more, run the command again with --verbose. +error: could not compile `hello` (bin "hello") due to 1 previous error diff --git a/listings/ch20-web-server/no-listing-01-define-threadpool-struct/src/bin/main.rs b/listings/ch20-web-server/no-listing-01-define-threadpool-struct/src/bin/main.rs deleted file mode 100644 index 8df1b17ee..000000000 --- a/listings/ch20-web-server/no-listing-01-define-threadpool-struct/src/bin/main.rs +++ /dev/null @@ -1,46 +0,0 @@ -// ANCHOR: here -use hello::ThreadPool; -// ANCHOR_END: here -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; -use std::thread; -use std::time::Duration; - -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); - }); - } -} - -fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - - let get = b"GET / HTTP/1.1\r\n"; - let sleep = b"GET /sleep HTTP/1.1\r\n"; - - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else if buffer.starts_with(sleep) { - thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "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(); -} diff --git a/listings/ch20-web-server/no-listing-01-define-threadpool-struct/src/main.rs b/listings/ch20-web-server/no-listing-01-define-threadpool-struct/src/main.rs new file mode 100644 index 000000000..f7b42167f --- /dev/null +++ b/listings/ch20-web-server/no-listing-01-define-threadpool-struct/src/main.rs @@ -0,0 +1,45 @@ +// ANCHOR: here +use hello::ThreadPool; +// ANCHOR_END: here +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, + thread, + time::Duration, +}; + +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); + }); + } +} + +fn handle_connection(mut stream: TcpStream) { + let buf_reader = BufReader::new(&mut stream); + let request_line = buf_reader.lines().next().unwrap().unwrap(); + + let (status_line, filename) = match &request_line[..] { + "GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), + "GET /sleep HTTP/1.1" => { + thread::sleep(Duration::from_secs(5)); + ("HTTP/1.1 200 OK", "hello.html") + } + _ => ("HTTP/1.1 404 NOT FOUND", "404.html"), + }; + + let contents = fs::read_to_string(filename).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(); +} diff --git a/listings/ch20-web-server/no-listing-02-impl-threadpool-new/Cargo.toml b/listings/ch20-web-server/no-listing-02-impl-threadpool-new/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/no-listing-02-impl-threadpool-new/Cargo.toml +++ b/listings/ch20-web-server/no-listing-02-impl-threadpool-new/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/no-listing-02-impl-threadpool-new/output.txt b/listings/ch20-web-server/no-listing-02-impl-threadpool-new/output.txt index c808b8986..667041862 100644 --- a/listings/ch20-web-server/no-listing-02-impl-threadpool-new/output.txt +++ b/listings/ch20-web-server/no-listing-02-impl-threadpool-new/output.txt @@ -1,14 +1,10 @@ $ cargo check Checking hello v0.1.0 (file:///projects/hello) -error[E0599]: no method named `execute` found for type `hello::ThreadPool` in the current scope - --> src/bin/main.rs:16:14 +error[E0599]: no method named `execute` found for struct `ThreadPool` in the current scope + --> src/main.rs:17:14 | -16 | pool.execute(|| { - | ^^^^^^^ method not found in `hello::ThreadPool` - -error: aborting due to previous error +17 | pool.execute(|| { + | -----^^^^^^^ method not found in `ThreadPool` For more information about this error, try `rustc --explain E0599`. -error: could not compile `hello`. - -To learn more, run the command again with --verbose. +error: could not compile `hello` (bin "hello") due to 1 previous error diff --git a/listings/ch20-web-server/no-listing-02-impl-threadpool-new/src/bin/main.rs b/listings/ch20-web-server/no-listing-02-impl-threadpool-new/src/bin/main.rs deleted file mode 100644 index 9d6a9c154..000000000 --- a/listings/ch20-web-server/no-listing-02-impl-threadpool-new/src/bin/main.rs +++ /dev/null @@ -1,44 +0,0 @@ -use hello::ThreadPool; -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; -use std::thread; -use std::time::Duration; - -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); - }); - } -} - -fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - - let get = b"GET / HTTP/1.1\r\n"; - let sleep = b"GET /sleep HTTP/1.1\r\n"; - - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else if buffer.starts_with(sleep) { - thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "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(); -} diff --git a/listings/ch20-web-server/no-listing-02-impl-threadpool-new/src/lib.rs b/listings/ch20-web-server/no-listing-02-impl-threadpool-new/src/lib.rs index 3ff5ad497..f0e1890bb 100644 --- a/listings/ch20-web-server/no-listing-02-impl-threadpool-new/src/lib.rs +++ b/listings/ch20-web-server/no-listing-02-impl-threadpool-new/src/lib.rs @@ -1,4 +1,3 @@ -// ANCHOR: here pub struct ThreadPool; impl ThreadPool { @@ -6,6 +5,3 @@ impl ThreadPool { ThreadPool } } -// ANCHOR_END: here - -fn main() {} diff --git a/listings/ch20-web-server/no-listing-02-impl-threadpool-new/src/main.rs b/listings/ch20-web-server/no-listing-02-impl-threadpool-new/src/main.rs new file mode 100644 index 000000000..79efb28a2 --- /dev/null +++ b/listings/ch20-web-server/no-listing-02-impl-threadpool-new/src/main.rs @@ -0,0 +1,43 @@ +use hello::ThreadPool; +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, + thread, + time::Duration, +}; + +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); + }); + } +} + +fn handle_connection(mut stream: TcpStream) { + let buf_reader = BufReader::new(&mut stream); + let request_line = buf_reader.lines().next().unwrap().unwrap(); + + let (status_line, filename) = match &request_line[..] { + "GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), + "GET /sleep HTTP/1.1" => { + thread::sleep(Duration::from_secs(5)); + ("HTTP/1.1 200 OK", "hello.html") + } + _ => ("HTTP/1.1 404 NOT FOUND", "404.html"), + }; + + let contents = fs::read_to_string(filename).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(); +} diff --git a/listings/ch20-web-server/no-listing-03-define-execute/Cargo.toml b/listings/ch20-web-server/no-listing-03-define-execute/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/no-listing-03-define-execute/Cargo.toml +++ b/listings/ch20-web-server/no-listing-03-define-execute/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/no-listing-03-define-execute/src/bin/main.rs b/listings/ch20-web-server/no-listing-03-define-execute/src/bin/main.rs deleted file mode 100644 index 9d6a9c154..000000000 --- a/listings/ch20-web-server/no-listing-03-define-execute/src/bin/main.rs +++ /dev/null @@ -1,44 +0,0 @@ -use hello::ThreadPool; -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; -use std::thread; -use std::time::Duration; - -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); - }); - } -} - -fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - - let get = b"GET / HTTP/1.1\r\n"; - let sleep = b"GET /sleep HTTP/1.1\r\n"; - - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else if buffer.starts_with(sleep) { - thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "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(); -} diff --git a/listings/ch20-web-server/no-listing-03-define-execute/src/lib.rs b/listings/ch20-web-server/no-listing-03-define-execute/src/lib.rs index 44e0ebd72..1321ab873 100644 --- a/listings/ch20-web-server/no-listing-03-define-execute/src/lib.rs +++ b/listings/ch20-web-server/no-listing-03-define-execute/src/lib.rs @@ -16,5 +16,3 @@ impl ThreadPool { } } // ANCHOR_END: here - -fn main() {} diff --git a/listings/ch20-web-server/no-listing-03-define-execute/src/main.rs b/listings/ch20-web-server/no-listing-03-define-execute/src/main.rs new file mode 100644 index 000000000..79efb28a2 --- /dev/null +++ b/listings/ch20-web-server/no-listing-03-define-execute/src/main.rs @@ -0,0 +1,43 @@ +use hello::ThreadPool; +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, + thread, + time::Duration, +}; + +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); + }); + } +} + +fn handle_connection(mut stream: TcpStream) { + let buf_reader = BufReader::new(&mut stream); + let request_line = buf_reader.lines().next().unwrap().unwrap(); + + let (status_line, filename) = match &request_line[..] { + "GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), + "GET /sleep HTTP/1.1" => { + thread::sleep(Duration::from_secs(5)); + ("HTTP/1.1 200 OK", "hello.html") + } + _ => ("HTTP/1.1 404 NOT FOUND", "404.html"), + }; + + let contents = fs::read_to_string(filename).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(); +} diff --git a/listings/ch20-web-server/no-listing-04-update-worker-definition/Cargo.toml b/listings/ch20-web-server/no-listing-04-update-worker-definition/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/no-listing-04-update-worker-definition/Cargo.toml +++ b/listings/ch20-web-server/no-listing-04-update-worker-definition/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/no-listing-04-update-worker-definition/output.txt b/listings/ch20-web-server/no-listing-04-update-worker-definition/output.txt index 91907cf79..4a622de7f 100644 --- a/listings/ch20-web-server/no-listing-04-update-worker-definition/output.txt +++ b/listings/ch20-web-server/no-listing-04-update-worker-definition/output.txt @@ -1,27 +1,31 @@ $ cargo check Checking hello v0.1.0 (file:///projects/hello) -error[E0599]: no method named `join` found for type `std::option::Option>` in the current scope +error[E0599]: no method named `join` found for enum `Option` in the current scope --> src/lib.rs:52:27 | 52 | worker.thread.join().unwrap(); - | ^^^^ method not found in `std::option::Option>` + | ^^^^ method not found in `Option>` + | +note: the method `join` exists on the type `JoinHandle<()>` + --> /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/thread/mod.rs:1649:5 +help: consider using `Option::expect` to unwrap the `JoinHandle<()>` value, panicking if the value is an `Option::None` + | +52 | worker.thread.expect("REASON").join().unwrap(); + | +++++++++++++++++ error[E0308]: mismatched types --> src/lib.rs:72:22 | 72 | Worker { id, thread } - | ^^^^^^ - | | - | expected enum `std::option::Option`, found struct `std::thread::JoinHandle` - | help: try using a variant of the expected enum: `Some(thread)` + | ^^^^^^ expected `Option>`, found `JoinHandle<_>` | - = note: expected enum `std::option::Option>` - found struct `std::thread::JoinHandle<_>` - -error: aborting due to 2 previous errors + = note: expected enum `Option>` + found struct `JoinHandle<_>` +help: try wrapping the expression in `Some` + | +72 | Worker { id, thread: Some(thread) } + | +++++++++++++ + Some errors have detailed explanations: E0308, E0599. For more information about an error, try `rustc --explain E0308`. -error: could not compile `hello`. - -To learn more, run the command again with --verbose. +error: could not compile `hello` (lib) due to 2 previous errors diff --git a/listings/ch20-web-server/no-listing-04-update-worker-definition/src/bin/main.rs b/listings/ch20-web-server/no-listing-04-update-worker-definition/src/bin/main.rs deleted file mode 100644 index 9d6a9c154..000000000 --- a/listings/ch20-web-server/no-listing-04-update-worker-definition/src/bin/main.rs +++ /dev/null @@ -1,44 +0,0 @@ -use hello::ThreadPool; -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; -use std::thread; -use std::time::Duration; - -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); - }); - } -} - -fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - - let get = b"GET / HTTP/1.1\r\n"; - let sleep = b"GET /sleep HTTP/1.1\r\n"; - - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else if buffer.starts_with(sleep) { - thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "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(); -} diff --git a/listings/ch20-web-server/no-listing-04-update-worker-definition/src/lib.rs b/listings/ch20-web-server/no-listing-04-update-worker-definition/src/lib.rs index 1098330b3..6ef710a26 100644 --- a/listings/ch20-web-server/no-listing-04-update-worker-definition/src/lib.rs +++ b/listings/ch20-web-server/no-listing-04-update-worker-definition/src/lib.rs @@ -1,7 +1,7 @@ -use std::sync::mpsc; -use std::sync::Arc; -use std::sync::Mutex; -use std::thread; +use std::{ + sync::{mpsc, Arc, Mutex}, + thread, +}; pub struct ThreadPool { workers: Vec, @@ -66,7 +66,7 @@ impl Worker { let thread = thread::spawn(move || loop { let job = receiver.lock().unwrap().recv().unwrap(); - println!("Worker {} got a job; executing.", id); + println!("Worker {id} got a job; executing."); job(); }); diff --git a/listings/ch20-web-server/no-listing-04-update-worker-definition/src/main.rs b/listings/ch20-web-server/no-listing-04-update-worker-definition/src/main.rs new file mode 100644 index 000000000..79efb28a2 --- /dev/null +++ b/listings/ch20-web-server/no-listing-04-update-worker-definition/src/main.rs @@ -0,0 +1,43 @@ +use hello::ThreadPool; +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, + thread, + time::Duration, +}; + +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); + }); + } +} + +fn handle_connection(mut stream: TcpStream) { + let buf_reader = BufReader::new(&mut stream); + let request_line = buf_reader.lines().next().unwrap().unwrap(); + + let (status_line, filename) = match &request_line[..] { + "GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), + "GET /sleep HTTP/1.1" => { + thread::sleep(Duration::from_secs(5)); + ("HTTP/1.1 200 OK", "hello.html") + } + _ => ("HTTP/1.1 404 NOT FOUND", "404.html"), + }; + + let contents = fs::read_to_string(filename).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(); +} diff --git a/listings/ch20-web-server/no-listing-05-fix-worker-new/Cargo.toml b/listings/ch20-web-server/no-listing-05-fix-worker-new/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/no-listing-05-fix-worker-new/Cargo.toml +++ b/listings/ch20-web-server/no-listing-05-fix-worker-new/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/no-listing-05-fix-worker-new/src/bin/main.rs b/listings/ch20-web-server/no-listing-05-fix-worker-new/src/bin/main.rs deleted file mode 100644 index 9d6a9c154..000000000 --- a/listings/ch20-web-server/no-listing-05-fix-worker-new/src/bin/main.rs +++ /dev/null @@ -1,44 +0,0 @@ -use hello::ThreadPool; -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; -use std::thread; -use std::time::Duration; - -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); - }); - } -} - -fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - - let get = b"GET / HTTP/1.1\r\n"; - let sleep = b"GET /sleep HTTP/1.1\r\n"; - - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else if buffer.starts_with(sleep) { - thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "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(); -} diff --git a/listings/ch20-web-server/no-listing-05-fix-worker-new/src/lib.rs b/listings/ch20-web-server/no-listing-05-fix-worker-new/src/lib.rs index 6bef23a16..ede3750a1 100644 --- a/listings/ch20-web-server/no-listing-05-fix-worker-new/src/lib.rs +++ b/listings/ch20-web-server/no-listing-05-fix-worker-new/src/lib.rs @@ -1,7 +1,7 @@ -use std::sync::mpsc; -use std::sync::Arc; -use std::sync::Mutex; -use std::thread; +use std::{ + sync::{mpsc, Arc, Mutex}, + thread, +}; pub struct ThreadPool { workers: Vec, @@ -68,7 +68,7 @@ impl Worker { let thread = thread::spawn(move || loop { let job = receiver.lock().unwrap().recv().unwrap(); - println!("Worker {} got a job; executing.", id); + println!("Worker {id} got a job; executing."); job(); }); diff --git a/listings/ch20-web-server/no-listing-05-fix-worker-new/src/main.rs b/listings/ch20-web-server/no-listing-05-fix-worker-new/src/main.rs new file mode 100644 index 000000000..79efb28a2 --- /dev/null +++ b/listings/ch20-web-server/no-listing-05-fix-worker-new/src/main.rs @@ -0,0 +1,43 @@ +use hello::ThreadPool; +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, + thread, + time::Duration, +}; + +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); + }); + } +} + +fn handle_connection(mut stream: TcpStream) { + let buf_reader = BufReader::new(&mut stream); + let request_line = buf_reader.lines().next().unwrap().unwrap(); + + let (status_line, filename) = match &request_line[..] { + "GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), + "GET /sleep HTTP/1.1" => { + thread::sleep(Duration::from_secs(5)); + ("HTTP/1.1 200 OK", "hello.html") + } + _ => ("HTTP/1.1 404 NOT FOUND", "404.html"), + }; + + let contents = fs::read_to_string(filename).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(); +} diff --git a/listings/ch20-web-server/no-listing-06-fix-threadpool-drop/Cargo.toml b/listings/ch20-web-server/no-listing-06-fix-threadpool-drop/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/no-listing-06-fix-threadpool-drop/Cargo.toml +++ b/listings/ch20-web-server/no-listing-06-fix-threadpool-drop/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/no-listing-06-fix-threadpool-drop/src/bin/main.rs b/listings/ch20-web-server/no-listing-06-fix-threadpool-drop/src/bin/main.rs deleted file mode 100644 index 9d6a9c154..000000000 --- a/listings/ch20-web-server/no-listing-06-fix-threadpool-drop/src/bin/main.rs +++ /dev/null @@ -1,44 +0,0 @@ -use hello::ThreadPool; -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; -use std::thread; -use std::time::Duration; - -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); - }); - } -} - -fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - - let get = b"GET / HTTP/1.1\r\n"; - let sleep = b"GET /sleep HTTP/1.1\r\n"; - - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else if buffer.starts_with(sleep) { - thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "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(); -} diff --git a/listings/ch20-web-server/no-listing-06-fix-threadpool-drop/src/lib.rs b/listings/ch20-web-server/no-listing-06-fix-threadpool-drop/src/lib.rs index d5b38a635..b795ea53b 100644 --- a/listings/ch20-web-server/no-listing-06-fix-threadpool-drop/src/lib.rs +++ b/listings/ch20-web-server/no-listing-06-fix-threadpool-drop/src/lib.rs @@ -1,7 +1,7 @@ -use std::sync::mpsc; -use std::sync::Arc; -use std::sync::Mutex; -use std::thread; +use std::{ + sync::{mpsc, Arc, Mutex}, + thread, +}; pub struct ThreadPool { workers: Vec, @@ -68,7 +68,7 @@ impl Worker { let thread = thread::spawn(move || loop { let job = receiver.lock().unwrap().recv().unwrap(); - println!("Worker {} got a job; executing.", id); + println!("Worker {id} got a job; executing."); job(); }); diff --git a/listings/ch20-web-server/no-listing-06-fix-threadpool-drop/src/main.rs b/listings/ch20-web-server/no-listing-06-fix-threadpool-drop/src/main.rs new file mode 100644 index 000000000..b6aa046d1 --- /dev/null +++ b/listings/ch20-web-server/no-listing-06-fix-threadpool-drop/src/main.rs @@ -0,0 +1,45 @@ +use hello::ThreadPool; +use std::{ + fs, + io::{prelude::*, BufReader}, + net::{TcpListener, TcpStream}, + thread, + time::Duration, +}; + +fn main() { + 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."); +} + +fn handle_connection(mut stream: TcpStream) { + let buf_reader = BufReader::new(&mut stream); + let request_line = buf_reader.lines().next().unwrap().unwrap(); + + let (status_line, filename) = match &request_line[..] { + "GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), + "GET /sleep HTTP/1.1" => { + thread::sleep(Duration::from_secs(5)); + ("HTTP/1.1 200 OK", "hello.html") + } + _ => ("HTTP/1.1 404 NOT FOUND", "404.html"), + }; + + let contents = fs::read_to_string(filename).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(); +} diff --git a/listings/ch20-web-server/no-listing-07-define-message-enum/Cargo.lock b/listings/ch20-web-server/no-listing-07-define-message-enum/Cargo.lock deleted file mode 100644 index f2d069f46..000000000 --- a/listings/ch20-web-server/no-listing-07-define-message-enum/Cargo.lock +++ /dev/null @@ -1,6 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "hello" -version = "0.1.0" - diff --git a/listings/ch20-web-server/no-listing-07-define-message-enum/Cargo.toml b/listings/ch20-web-server/no-listing-07-define-message-enum/Cargo.toml deleted file mode 100644 index 78dfe6ebc..000000000 --- a/listings/ch20-web-server/no-listing-07-define-message-enum/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "hello" -version = "0.1.0" -authors = ["Your Name "] -edition = "2018" - -[dependencies] diff --git a/listings/ch20-web-server/no-listing-07-define-message-enum/hello.html b/listings/ch20-web-server/no-listing-07-define-message-enum/hello.html deleted file mode 100644 index fe442d6b9..000000000 --- a/listings/ch20-web-server/no-listing-07-define-message-enum/hello.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Hello! - - -

Hello!

-

Hi from Rust

- - diff --git a/listings/ch20-web-server/no-listing-07-define-message-enum/src/bin/main.rs b/listings/ch20-web-server/no-listing-07-define-message-enum/src/bin/main.rs deleted file mode 100644 index 9d6a9c154..000000000 --- a/listings/ch20-web-server/no-listing-07-define-message-enum/src/bin/main.rs +++ /dev/null @@ -1,44 +0,0 @@ -use hello::ThreadPool; -use std::fs; -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; -use std::thread; -use std::time::Duration; - -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); - }); - } -} - -fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - - let get = b"GET / HTTP/1.1\r\n"; - let sleep = b"GET /sleep HTTP/1.1\r\n"; - - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else if buffer.starts_with(sleep) { - thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "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(); -} diff --git a/listings/ch20-web-server/listing-20-08/404.html b/listings/ch20-web-server/no-listing-07-final-code/404.html similarity index 100% rename from listings/ch20-web-server/listing-20-08/404.html rename to listings/ch20-web-server/no-listing-07-final-code/404.html diff --git a/listings/ch20-web-server/listing-20-04/Cargo.lock b/listings/ch20-web-server/no-listing-07-final-code/Cargo.lock similarity index 100% rename from listings/ch20-web-server/listing-20-04/Cargo.lock rename to listings/ch20-web-server/no-listing-07-final-code/Cargo.lock diff --git a/listings/ch20-web-server/listing-20-04/Cargo.toml b/listings/ch20-web-server/no-listing-07-final-code/Cargo.toml similarity index 50% rename from listings/ch20-web-server/listing-20-04/Cargo.toml rename to listings/ch20-web-server/no-listing-07-final-code/Cargo.toml index 78dfe6ebc..fe619478a 100644 --- a/listings/ch20-web-server/listing-20-04/Cargo.toml +++ b/listings/ch20-web-server/no-listing-07-final-code/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hello" version = "0.1.0" -authors = ["Your Name "] -edition = "2018" +edition = "2021" [dependencies] diff --git a/listings/ch20-web-server/listing-20-04/hello.html b/listings/ch20-web-server/no-listing-07-final-code/hello.html similarity index 100% rename from listings/ch20-web-server/listing-20-04/hello.html rename to listings/ch20-web-server/no-listing-07-final-code/hello.html diff --git a/listings/ch20-web-server/no-listing-07-define-message-enum/src/lib.rs b/listings/ch20-web-server/no-listing-07-final-code/src/lib.rs similarity index 67% rename from listings/ch20-web-server/no-listing-07-define-message-enum/src/lib.rs rename to listings/ch20-web-server/no-listing-07-final-code/src/lib.rs index ed1913b29..54c0489ab 100644 --- a/listings/ch20-web-server/no-listing-07-define-message-enum/src/lib.rs +++ b/listings/ch20-web-server/no-listing-07-final-code/src/lib.rs @@ -1,22 +1,15 @@ -use std::sync::mpsc; -use std::sync::Arc; -use std::sync::Mutex; -use std::thread; +use std::{ + sync::{mpsc, Arc, Mutex}, + thread, +}; pub struct ThreadPool { workers: Vec, - sender: mpsc::Sender, + sender: Option>, } type Job = Box; -// ANCHOR: here -enum Message { - NewJob(Job), - Terminate, -} -// ANCHOR_END: here - impl ThreadPool { /// Create a new ThreadPool. /// @@ -38,7 +31,10 @@ impl ThreadPool { workers.push(Worker::new(id, Arc::clone(&receiver))); } - ThreadPool { workers, sender } + ThreadPool { + workers, + sender: Some(sender), + } } pub fn execute(&self, f: F) @@ -47,12 +43,14 @@ impl ThreadPool { { let job = Box::new(f); - self.sender.send(job).unwrap(); + self.sender.as_ref().unwrap().send(job).unwrap(); } } impl Drop for ThreadPool { fn drop(&mut self) { + drop(self.sender.take()); + for worker in &mut self.workers { println!("Shutting down worker {}", worker.id); @@ -71,11 +69,19 @@ struct Worker { 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(); + let message = receiver.lock().unwrap().recv(); + + match message { + Ok(job) => { + println!("Worker {id} got a job; executing."); + + job(); + } + Err(_) => { + println!("Worker {id} disconnected; shutting down."); + break; + } + } }); Worker { @@ -84,5 +90,3 @@ impl Worker { } } } - -fn main() {} diff --git a/listings/ch20-web-server/listing-20-15/src/bin/main.rs b/listings/ch20-web-server/no-listing-07-final-code/src/main.rs similarity index 66% rename from listings/ch20-web-server/listing-20-15/src/bin/main.rs rename to listings/ch20-web-server/no-listing-07-final-code/src/main.rs index 9d6a9c154..cbd235512 100644 --- a/listings/ch20-web-server/listing-20-15/src/bin/main.rs +++ b/listings/ch20-web-server/no-listing-07-final-code/src/main.rs @@ -10,13 +10,16 @@ fn main() { let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); let pool = ThreadPool::new(4); - for stream in listener.incoming() { + for stream in listener.incoming().take(2) { let stream = stream.unwrap(); pool.execute(|| { handle_connection(stream); }); } + + // 終了します。 + println!("Shutting down."); } fn handle_connection(mut stream: TcpStream) { @@ -27,18 +30,23 @@ fn handle_connection(mut stream: TcpStream) { let sleep = b"GET /sleep HTTP/1.1\r\n"; let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") + ("HTTP/1.1 200 OK", "hello.html") } else if buffer.starts_with(sleep) { thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") + ("HTTP/1.1 200 OK", "hello.html") } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html") + ("HTTP/1.1 404 NOT FOUND", "404.html") }; let contents = fs::read_to_string(filename).unwrap(); - let response = format!("{}{}", status_line, contents); + let response = format!( + "{}\r\nContent-Length: {}\r\n\r\n{}", + status_line, + contents.len(), + contents + ); - stream.write(response.as_bytes()).unwrap(); + stream.write_all(response.as_bytes()).unwrap(); stream.flush().unwrap(); } diff --git a/src/ch20-00-final-project-a-web-server.md b/src/ch20-00-final-project-a-web-server.md index 424f99a73..f9c8bc979 100644 --- a/src/ch20-00-final-project-a-web-server.md +++ b/src/ch20-00-final-project-a-web-server.md @@ -30,7 +30,7 @@ Figure 20-1 in a web browser. 図20-1: 最後の共有されたプロジェクト こちらがWebサーバを構築するプランです: @@ -50,27 +50,25 @@ Here is the plan to build the web server: 5. スレッドプールでサーバのスループットを強化する。 -ですが、取り掛かる前に、ある小さな事実に触れなければなりません: +取り掛かる前に、ある小さな事実に触れなければなりません: わたしたちがこれから行うやり方は、RustでWebサーバを構築する最善の方法ではないだろうということです。 これから構築するよりもより完全なWebサーバとスレッドプールの実装を提供する製品利用可能な多くのクレートが、 -*https://crates.io/* で利用可能なのです。 - - - +[crates.io](https://crates.io/)から利用できるように、コミュニティメンバたちによって公開されています。 しかしながら、この章での意図は、学習を手助けすることであり、簡単なやり方を選ぶことではありません。 Rustはシステムプログラミング言語なので、取りかかる抽象度を選ぶことができ、 -他の言語で可能だったり実践的だったりするよりも低レベルまで行くことができます。一般的な考えと将来使う可能性のあるクレートの背後にある技術を学べるように、 +他の言語で可能だったり実践的だったりするよりも低レベルまで行くことができます。 +そのため、一般的な考えと将来使う可能性のあるクレートの背後にある技術を学べるように、 手動で基本的なHTTPサーバとスレッドプールを書きます。 diff --git a/src/ch20-01-single-threaded.md b/src/ch20-01-single-threaded.md index b01fd42da..dfbdae5fb 100644 --- a/src/ch20-01-single-threaded.md +++ b/src/ch20-01-single-threaded.md @@ -15,12 +15,11 @@ a brief overview will give you the information you need. これらのプロトコルの詳細は、この本の範疇を超えていますが、さっと眺めることで必要な情報が得られるでしょう。 主に2つのプロトコルがWebサーバに関係し、*Hypertext Transfer Protocol* *(HTTP)*(`注釈`: ハイパーテキスト転送プロトコル)と、 @@ -58,20 +57,20 @@ this. Let’s make a new project in the usual fashion: WebサーバはTCP接続をリッスンするので、そこが最初に取り掛かる部分になります。標準ライブラリは、 `std::net`というこれを行うモジュールを用意しています。通常通り、新しいプロジェクトを作りましょう: -```text -$ cargo new hello --bin +```console +$ cargo new hello Created binary (application) `hello` project $ cd hello ``` さて、リスト20-1のコードを*src/main.rs*に入力して始めてください。このコードは、 -TCPストリームを受信するため`127.0.0.1:7878`というアドレスをリッスンします。 +TCPストリームを受信するため`127.0.0.1:7878`というローカルアドレスをリッスンします。 入力ストリームを得ると、`Connection established!`と出力します。 `TcpListener`により、アドレス`127.0.0.1:7878`でTCP接続をリッスンできます。アドレス内で、 コロンの前の区域は、自分のコンピュータを表すIPアドレスで(これはどんなコンピュータでも同じで、 特に著者のコンピュータを表すわけではありません)、`7878`はポートです。このポートを選択した理由は2つあります: -HTTPは通常このポートで受け付けられることと、7878は電話で“rust”と入力されるからです。 +HTTPは通常このポートで受け付けられることがないので、私たちのサーバはおそらく、 +あなたのマシンで実行中かもしれない他のwebサーバと混線しないだろうということと、 +7878は電話で*rust*と入力されるからです。 この筋書きでの`bind`関数は、新しい`TcpListener`インスタンスを返すという点で`new`関数のような働きをします。 -この関数が`bind`と呼ばれている理由は、ネットワークにおいて、リッスンすべきポートに接続することは、 +この関数は`bind`と呼ばれます。ネットワークにおいて、リッスンすべきポートに接続することは、 「ポートに束縛する」(binding to a port)こととして知られているからです。 `bind`関数は`Result`を返し、束縛が失敗することもあることを示しています。例えば、 -ポート80に接続するには管理者権限が必要なので(管理者以外はポート1024以上しかリッスンできません)管理者にならずにポート80に接続を試みたら、 -束縛はうまくいかないでしょう。また、別の例として自分のプログラムを2つ同時に立ち上げて2つのプログラムが同じポートをリッスンしたら、 +ポート80に接続するには管理者権限が必要なので(管理者以外は1023より大きい番号のポートしかリッスンできません)管理者にならずにポート80に接続を試みたら、 +束縛はうまくいかないでしょう。また例えば、自分のプログラムを2つ同時に立ち上げて2つのプログラムが同じポートをリッスンする場合も、 束縛は機能しないでしょう。学習目的のためだけに基本的なサーバを記述しているので、この種のエラーを扱う心配はしません; その代わり、`unwrap`を使用してエラーが発生したら、プログラムを停止します。 @@ -151,16 +142,17 @@ sequence of streams (more specifically, streams of type `TcpStream`). A single *stream* represents an open connection between the client and the server. A *connection* is the name for the full request and response process in which a client connects to the server, the server generates a response, and the server -closes the connection. As such, `TcpStream` will read from itself to see what -the client sent and then allow us to write our response to the stream. Overall, -this `for` loop will process each connection in turn and produce a series of -streams for us to handle. +closes the connection. As such, we will read from the `TcpStream` to see what +the client sent and then write our response to the stream to send data back to +the client. Overall, this `for` loop will process each connection in turn and +produce a series of streams for us to handle. --> `TcpListener`の`incoming`メソッドは、一連のストリームを与えるイテレータを返します(具体的には、型`TcpStream`のストリーム)。 単独の*ストリーム*がクライアント・サーバ間の開かれた接続を表します。*接続*(connection)は、 クライアントがサーバに接続し、サーバがレスポンスを生成し、サーバが接続を閉じるというリクエストとレスポンス全体の過程の名前です。 -そのため、`TcpStream`は自身を読み取って、クライアントが送信したことを確認し、それからレスポンスをストリームに記述させてくれます。 +そのため、`TcpStream`から読み取ってクライアントが送信した内容を確認し、 +それからレスポンスをストリームに書き込んでクライアントにデータを返信します。 総括すると、この`for`ループは各接続を順番に処理し、我々が扱えるように一連のストリームを生成します。 特定のバージョンのコードを走らせ終わった時にctrl-cを押して、 -プログラムを止めることを忘れないでください。そして、一連のコード変更を行った後に`cargo run`を再起動し、 +プログラムを止めることを忘れないでください。そして、一連のコード変更を行った後に、 +`cargo run`コマンドを実行することでプログラムを再起動し、 最新のコードを実行していることを確かめてください。 -`std::io::prelude`をスコープに導入して、ストリームから読み書きさせてくれる特定のトレイトにアクセスできるようにしています。 +`std::io::prelude`と`std::io::BufReader`をスコープに導入して、 +ストリームから読み書きさせてくれるトレイトと型にアクセスできるようにしています。 `main`関数内の`for`ループで、接続を確立したというメッセージを出力する代わりに、今では、 新しい`handle_connection`関数を呼び出し、`stream`を渡しています。 -`handle_connection`関数において、`stream`引数を可変にしました。理由は、 -`TcpStream`インスタンスが内部で返すデータを追いかけているからです。要求した以上のデータを読み取り、 -次回データを要求した時のためにそのデータを保存する可能性があります。故に、内部の状態が変化する可能性があるので、 -`mut`にする必要があるのです; 普通、「読み取り」に可変化は必要ないと考えてしまいますが、この場合、`mut`キーワードが必要です。 +`handle_connection`関数では、`stream`への可変参照をラップする、新しい`BufReader`インスタンスを作成しています。 +`BufReader`は、`std::io::Read`トレイトメソッドへの呼び出しを管理することで、バッファリング機能を追加します。 -次に、実際にストリームから読み取る必要があります。これを2つの手順で行います: まず、 -スタックに読み取ったデータを保持する`buffer`を宣言します。バッファーのサイズは1024バイトにしました。 -これは、基本的なリクエストには十分な大きさでこの章の目的には必要十分です。任意のサイズのリクエストを扱いたければ、 -バッファーの管理はもっと複雑にする必要があります; 今は、単純に保っておきます。このバッファーを`stream.read`に渡し、 -これが`TcpStream`からバイトを読み取ってバッファーに置きます。 +ブラウザがサーバに送信したリクエストの各行を集めるために、`http_request`という名前の変数を作成しています。 +`Vec<_>`型注釈を追加することで、これらの行をベクタに集めたいということを示します。 -2番目にバッファーのバイトを文字列に変換し、その文字列を出力します。`String::from_utf8_lossy`関数は、 -`&[u8]`を取り、`String`を生成します。名前の“lossy”の箇所は、無効なUTF-8シーケンスを目の当たりにした際のこの関数の振る舞いを示唆しています: -無効なシーケンスを`�`、`U+FFFD REPLACEMENT CHARACTER`で置き換えます。 -リクエストデータによって埋められなかったバッファーの部分(`訳注` バッファーとして1024バイトの領域を用意しているが、リクエストデータは1024バイト存在しないことがほとんどなので変数 `buffer` の後ろ部分が埋められないまま放置されることを意図していると思われる) は、置換文字が表示される場合があります。 +`BufReader`は`std::io::BufRead`トレイトを実装しており、これは`lines`メソッドを提供しています。 +`lines`メソッドは、改行バイトを見つけたらそこでデータのストリームを分割することで、 +`Result`からなるイテレータを返します。 +各`String`を得るために、`map`して各`Result`を`unwrap`します。 +データが妥当なUTF-8でない場合や、ストリームからの読み込みに問題があった場合などは、 +`Result`はエラーになる場合があります。繰り返しになりますが、 +実運用のプログラムではこれらのエラーをより丁寧に処理するべきですが、 +ここでは簡潔性のために、エラーの場合にはプログラムを停止することを選択しています。 + + + +ブラウザは2個の改行文字列を連続で送信することでHTTPリクエストの終わりを伝えるてくるので、 +ストリームから1リクエストを取得するためには、空文字列である行が得られるまで行を取得します。 +行をベクタに集めたら、それらをprettyデバッグフォーマットを使用して出力していますので、 +webブラウザが私たちのサーバに送信している指示を確認することができます。 ブラウザによって、少し異なる出力になる可能性があります。今やリクエストデータを出力しているので、 -`Request: GET`の後のパスを見ることで1回のブラウザリクエストから複数の接続が得られる理由が確認できます。 +リクエストの最初の行の`GET`の後のパスを見ることで1回のブラウザリクエストから複数の接続が得られる理由が確認できます。 繰り返される接続が全て */* を要求しているなら、ブラウザは、我々のプログラムからレスポンスが得られないので、 繰り返し */* をフェッチしようとしていることがわかります。 @@ -418,12 +407,13 @@ message-body The first line is the *request line* that holds information about what the client is requesting. The first part of the request line indicates the *method* being used, such as `GET` or `POST`, which describes how the client is making -this request. Our client used a `GET` request. +this request. Our client used a `GET` request, which means it is asking for +information. --> 1行目は、クライアントが要求しているものがなんなのかについての情報を保持するリクエスト行です。 リクエスト行の最初の部分は使用されている`GET`や`POST`などの*メソッド*を示し、これは、どのようにクライアントがこの要求を行なっているかを記述します。 -クライアントは`GET`リクエストを使用しました。 +クライアントは`GET`リクエストを使用しており、これは情報を求めていることを意味します。 -さて、クライアントのリクエストに対する返答としてデータの送信を実装します。レスポンスは、以下のようなフォーマットです: +クライアントのリクエストに対する返答としてデータの送信を実装します。 +レスポンスは、以下のようなフォーマットです: ```text HTTP-Version Status-Code Reason-Phrase CRLF @@ -541,19 +532,8 @@ Listing 20-3. ファイル名: src/main.rs -```rust -# use std::io::prelude::*; -# use std::net::TcpStream; -fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - - stream.read(&mut buffer).unwrap(); - - let response = "HTTP/1.1 200 OK\r\n\r\n"; - - stream.write(response.as_bytes()).unwrap(); - stream.flush().unwrap(); -} +```rust,no_run +{{#rustdoc_include ../listings/ch20-web-server/listing-20-03/src/main.rs:here}} ``` 新しい最初の行に成功したメッセージのデータを保持する`response`変数を定義しています。そして、 -`response`に対して`as_bytes`を呼び出し、文字列データをバイトに変換します。`stream`の`write`メソッドは、 +`response`に対して`as_bytes`を呼び出し、文字列データをバイトに変換します。`stream`の`write_all`メソッドは、 `&[u8]`を取り、接続に直接そのバイトを送信します。 - - - -`write`処理は失敗することもあるので、以前のようにエラーの結果には`unwrap`を使用します。 -今回も、実際のアプリでは、エラー処理をここに追加するでしょう。最後に`flush`は待機し、 -バイトが全て接続に書き込まれるまでプログラムが継続するのを防ぎます; `TcpStream`は内部にバッファーを保持して、 -元となるOSへの呼び出しを最小化します。 +`write_all`処理は失敗することもあるので、以前のようにエラーの結果には`unwrap`を使用します。 +今回も、実際のアプリでは、エラー処理をここに追加するでしょう。 これらの変更とともに、コードを実行し、リクエストをしましょう。最早、端末にどんなデータも出力していないので、 Cargoからの出力以外には何も出力はありません。Webブラウザで*127.0.0.1:7878*をロードすると、 -エラーではなく空のページが得られるはずです。HTTPリクエストとレスポンスを手で実装したばかりなのです! +エラーではなく空のページが得られるはずです。 +HTTPリクエストの受信とレスポンスの送信を手で実装することができました! @@ -623,23 +595,7 @@ possibility. ファイル名: hello.html ```html - - - - - Hello! - - - -

Hello!

- -

Hi from Rust

- - +{{#include ../listings/ch20-web-server/listing-20-05/hello.html}} ``` -先頭に行を追加して標準ライブラリの`File`をスコープに導入しました。ファイルを開き、中身を読み込むコードは、 -馴染みがあるはずです; リスト12-4でI/Oプロジェクト用にファイルの中身を読み込んだ時に第12章で使用しましたね。 +`use`文に`fs`を追加して、標準ライブラリのファイルシステムモジュールをスコープに導入しました。 +ファイルの中身を文字列として読み込むコードは、馴染みがあるはずです; +リスト12-4でI/Oプロジェクト用にファイルの中身を読み込んだ時に第12章で使用しましたね。 次に`format!`でファイルの中身を成功したレスポンスの本体として追記しています。 +妥当なHTTPレスポンスであることを保証するために、`Content-Length`ヘッダを追加し、 +私たちのレスポンスボディのサイズに設定します。この場合は`hello.html`のサイズです。 -現時点では、`buffer`内のリクエストデータは無視し、無条件でHTMLファイルの中身を送り返しているだけです。 +現時点では、`http_request`内のリクエストデータは無視し、無条件でHTMLファイルの中身を送り返しているだけです。 これはつまり、ブラウザで*127.0.0.1:7878/something-else*をリクエストしても、 -この同じHTMLレスポンスが得られるということです。我々のサーバはかなり限定的で、多くのWebサーバとは異なっています。 +この同じHTMLレスポンスが得られるということです。現時点では、我々のサーバはかなり限定的で、 +多くのWebサーバが行うことを行っていません。 リクエストに基づいてレスポンスをカスタマイズし、*/* への合法なリクエストに対してのみHTMLファイルを送り返したいです。 - stream.write(response.as_bytes()).unwrap(); - stream.flush().unwrap(); - } else { - // 何か他の要求 - // some other request - } -} -``` +リスト20-6: */* へのリクエストを他のリクエストとは異なる形で扱う -リスト20-6: リクエストをマッチさせ、*/* へのリクエストを他のリクエストとは異なる形で扱う +HTTPリクエストの最初の行しか見ないので、リクエスト全体をベクタに読むのではなく、 +`next`を呼んでイテレータから最初の要素を取得しています。 +最初の`unwrap`は`Option`を処理するもので、イテレータに要素が無い場合はプログラムを停止します。 +2番目の`unwrap`は`Result`を処理するもので、リスト20-2で追加された`map`の中にあった`unwrap`と同じ効果を持ちます。 -まず、*/* リクエストに対応するデータを`get`変数にハードコードしています。生のバイトをバッファーに読み込んでいるので、 -`b""`バイト文字列記法を中身のデータの先頭に追記することで、`get`をバイト文字列に変換しています。 -そして、`buffer`が`get`のバイトから始まっているか確認します。もしそうなら、*/* への合法なリクエストを受け取ったことを意味し、 -これが、HTMLファイルの中身を返す`if`ブロックで扱う成功した場合になります。 +次に、`request_line`を確認して、*/* パスへのGETリクエストのリクエスト行と等しいか確認します。 +もしそうなら、`if`ブロックはHTMLファイルの中身を返します。 -`buffer`が`get`のバイトで始まら*ない*のなら、何か他のリクエストを受け取ったことになります。 +`request_line`が */* パスへのGETリクエストと等しく*ない*のなら、何か他のリクエストを受け取ったことになります。 この後すぐ、`else`ブロックに他のリクエストに対応するコードを追加します。 さあ、このコードを走らせて*127.0.0.1:7878*を要求してください; *hello.html*のHTMLが得られるはずです。 -*127.0.0.1:7878/something-else*などの他のリクエストを行うと、リスト20-1や20-2のコードを走らせた時に見かけた接続エラーになるでしょう。 +*127.0.0.1:7878/something-else*などの他のリクエストを行うと、リスト20-1や20-2のコードを走らせた時に見かけたような接続エラーになるでしょう。 ここでは、レスポンスにはステータスコード404と理由フレーズ`NOT FOUND`のステータス行があります。 -それでもヘッダは返さず、レスポンスの本体は、ファイル*404.html*のHTMLになります。エラーページのために、 +レスポンスの本体は、ファイル*404.html*のHTMLになります。エラーページのために、 *hello.html*の隣に*404.html*ファイルを作成する必要があります; 今回も、ご自由にお好きなHTMLにしたり、 リスト20-8の例のHTMLを使用したりしてください。 @@ -898,23 +807,7 @@ any HTML you want or use the example HTML in Listing 20-8. ファイル名: 404.html ```html - - - - - Hello! - - - -

Oops!

- -

Sorry, I don't know what you're asking for.

- - +{{#include ../listings/ch20-web-server/listing-20-07/404.html}} ``` @@ -961,35 +854,8 @@ the large `if` and `else` blocks. ファイル名: src/main.rs -```rust -# use std::io::prelude::*; -# use std::net::TcpStream; -# use std::fs::File; -// --snip-- - -fn handle_connection(mut stream: TcpStream) { -# let mut buffer = [0; 1024]; -# stream.read(&mut buffer).unwrap(); -# -# let get = b"GET / HTTP/1.1\r\n"; - // --snip-- - - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html") - }; - - let mut file = File::open(filename).unwrap(); - let mut contents = String::new(); - - file.read_to_string(&mut contents).unwrap(); - - let response = format!("{}{}", status_line, contents); - - stream.write(response.as_bytes()).unwrap(); - stream.flush().unwrap(); -} +```rust,no_run +{{#rustdoc_include ../listings/ch20-web-server/listing-20-09/src/main.rs:here}} ``` diff --git a/src/ch20-02-multithreaded.md b/src/ch20-02-multithreaded.md index 50ec24ef5..948f9d511 100644 --- a/src/ch20-02-multithreaded.md +++ b/src/ch20-02-multithreaded.md @@ -42,53 +42,40 @@ for 5 seconds before responding. ファイル名: src/main.rs -```rust -use std::thread; -use std::time::Duration; -# use std::io::prelude::*; -# use std::net::TcpStream; -# use std::fs::File; -// --snip-- - -fn handle_connection(mut stream: TcpStream) { -# let mut buffer = [0; 1024]; -# stream.read(&mut buffer).unwrap(); - // --snip-- - - let get = b"GET / HTTP/1.1\r\n"; - let sleep = b"GET /sleep HTTP/1.1\r\n"; - - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else if buffer.starts_with(sleep) { - thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html") - }; - - // --snip-- -} +```rust,no_run +{{#rustdoc_include ../listings/ch20-web-server/listing-20-10/src/main.rs:here}} ``` + +リスト20-10: 5秒間スリープすることで遅いリクエストをシミュレーションする + + -リスト20-10: */sleep*を認識して5秒間スリープすることで遅いリクエストをシミュレーションする +3通りの場合分けを持つようになったので、`if`から`match`に切り替えました。 +文字列リテラル値を使ってパターンマッチするには、`request_line`のスライスにマッチすることを明示する必要があります; +`match`は、等値性メソッドが行うような自動的な参照および参照外しを行いません。 -このコードはちょっと汚いですが、シミュレーション目的には十分です。2番目のリクエスト`sleep`を作成し、 -そのデータをサーバは認識します。`if`ブロックの後に`else if`を追加し、*/sleep*へのリクエストを確認しています。 +最初のアームは、リスト20-9の`if`ブロックと同じです。 +2番目のアームは、*/sleep* へのリクエストにマッチします。 そのリクエストが受け付けられると、サーバは成功のHTMLページを描画する前に5秒間スリープします。 +3番目のアームは、リスト20-9の`else`ブロックと同じです。 `cargo run`でサーバを開始してください。それから2つブラウザのウインドウを開いてください: 1つは、 -*http://localhost:7878/* 用、そしてもう1つは*http://localhost:7878/sleep* 用です。 +*http://127.0.0.1:7878/* 用、そしてもう1つは*http://127.0.0.1:7878/sleep* 用です。 以前のように */* URIを数回入力したら、素早く応答するでしょう。しかし、*/sleep*を入力し、それから */* をロードしたら、 `sleep`がロードする前にきっかり5秒スリープし終わるまで、*/* は待機するのを目撃するでしょう。 -より多くのリクエストが遅いリクエストの背後に回ってしまうのを回避するようWebサーバが動く方法を変える方法は複数あります; +遅いリクエストの後ろにリクエストが積み重なってしまうのを回避するための技術は、複数あります; これから実装するのは、スレッドプールです。 -無制限にスレッドを大量生産するのではなく、プールに固定された数のスレッドを待機させます。リクエストが来る度に、 -処理するためにプールに送られます。プールは、やって来るリクエストのキューを管理します。 +無制限にスレッドを大量生産するのではなく、プールに固定された数のスレッドを待機させます。 +やってきたリクエストは、処理するためにプールに送られます。プールは、やって来るリクエストのキューを管理します。 プールの各スレッドがこのキューからリクエストを取り出し、リクエストを処理し、そして、別のリクエストをキューに要求します。 -この設計により、`N`リクエストを並行して処理でき、ここで`N`はスレッド数です。各スレッドが実行に時間のかかるリクエストに応答していたら、 +この設計により、`N`個までのリクエストを並行して処理でき、ここで`N`はスレッド数です。各スレッドが実行に時間のかかるリクエストに応答していたら、 続くリクエストはそれでも、キュー内で待機させられてしまうこともありますが、その地点に到達する前に扱える時間のかかるリクエスト数を増加させました。 このテクニックは、Webサーバのスループットを向上させる多くの方法の1つに過ぎません。探究する可能性のある他の選択肢は、 -fork/joinモデルと、シングルスレッドの非同期I/Oモデルです。この話題にご興味があれば、他の解決策についてもっと読み、 -Rustで実装を試みることができます; Rustのような低レベル言語であれば、これらの選択肢全部が可能なのです。 +*fork/joinモデル*、*シングルスレッドの非同期I/Oモデル*、または*マルチスレッドの非同期I/Oモデル*です。 +この話題に興味があれば、他の解決策についてもっと読み、実装してみるのもよいでしょう; +Rustのような低レベル言語であれば、これらの選択肢全部が可能なのです。 第12章のプロジェクトでTDDを使用したように、ここではCompiler Driven Development(コンパイラ駆動開発)を使用します。 欲しい関数を呼び出すコードを書き、それからコンパイラの出すエラーを見てコードが動くように次に何を変更すべきかを決定します。 +ですがその前に開始点として、使用しない技法について探索してみましょう。 + -#### 各リクエストに対してスレッドを立ち上げられる場合のコードの構造 + + +#### 各リクエストに対してスレッドを立ち上げる まず、全接続に対して新しいスレッドを確かに生成した場合にコードがどんな見た目になるかを探究しましょう。 先ほど述べたように、無制限にスレッドを大量生産する可能性があるという問題のため、これは最終的な計画ではありませんが、 -開始点です。リスト20-11は、新しいスレッドを立ち上げて`for`ループ内で各ストリームを扱うために`main`に行う変更を示しています。 +まず機能するマルチスレッドサーバを得るための開始点です。 +その後、改善としてスレッドプールを追加します。そうすることで、2つの解決策の対比がより簡単になるでしょう。 +リスト20-11は、新しいスレッドを立ち上げて`for`ループ内で各ストリームを扱うために`main`に行う変更を示しています。 @@ -273,11 +256,16 @@ new threads without any limit. 確かに */* へのリクエストは、*/sleep*が完了するのを待機しなくても済むことがわかるでしょう。 ですが、前述したように、無制限にスレッドを生成することになるので、これは最終的にシステムを参らせてしまうでしょう。 + + + -#### 有限数のスレッド用に似たインターフェイスを作成する +#### 有限個のスレッドを作成する + + -#### コンパイラ駆動開発で`ThreadPool`構造体を構築する +#### コンパイラ駆動開発で`ThreadPool`を構築する src\main.rs:10:16 - | -10 | let pool = ThreadPool::new(4); - | ^^^^^^^^^^^^^^^ Use of undeclared type or module - `ThreadPool` - -error: aborting due to previous error +```console +{{#include ../listings/ch20-web-server/listing-20-12/output.txt}} ``` -それから新しいディレクトリ、*src/bin*を作成し、*src/main.rs*に根付くバイナリクレートを*src/bin/main.rs*に移動してください。 -そうすると、ライブラリクレートが*hello*ディレクトリ内で主要クレートになります; それでも、 -`cargo run`で*src/bin/main.rs*のバイナリを実行することはできます。*main.rs*ファイルを移動後、 -編集してライブラリクレートを持ち込み、以下のコードを*src/bin/main.rs*の先頭に追記して`ThreadPool`をスコープに導入してください: +それから*main.rs*ファイルを編集し、以下のコードを*src/main.rs*の先頭に追記して、 +ライブラリクレートから`ThreadPool`をスコープに導入してください: -ファイル名: src/bin/main.rs +ファイル名: src/main.rs ```rust,ignore -extern crate hello; -use hello::ThreadPool; +{{#rustdoc_include ../listings/ch20-web-server/no-listing-01-define-threadpool-struct/src/main.rs:here}} ``` src/bin/main.rs:13:16 - | -13 | let pool = ThreadPool::new(4); - | ^^^^^^^^^^^^^^^ function or associated item not found in - `hello::ThreadPool` +```console +{{#include ../listings/ch20-web-server/no-listing-01-define-threadpool-struct/output.txt}} ``` `size`引数の型として、`usize`を選択しました。何故なら、マイナスのスレッド数は、何も筋が通らないことを知っているからです。 -また、この4をスレッドのコレクションの要素数として使用し、第3章の「整数型」節で議論したように、これは`usize`のあるべき姿であることも知っています。 +また、この4をスレッドのコレクションの要素数として使用し、第3章の[「整数型」][integer-types]節で議論したように、これは`usize`のあるべき姿であることも知っています。 src/lib.rs:4:16 - | -4 | pub fn new(size: usize) -> ThreadPool { - | ^^^^ - | - = note: #[warn(unused_variables)] on by default - = note: to avoid this warning, consider using `_size` instead - -error[E0599]: no method named `execute` found for type `hello::ThreadPool` in the current scope - --> src/bin/main.rs:18:14 - | -18 | pool.execute(|| { - | ^^^^^^^ +```console +{{#include ../listings/ch20-web-server/no-listing-02-impl-threadpool-new/output.txt}} ``` -今度は、警告とエラーが出ました。一時的に警告は無視して、`ThreadPool`に`execute`メソッドがないためにエラーが発生しました。 -「有限数のスレッド用に似たインターフェイスを作成する」節で我々のスレッドプールは、 +今度は、`ThreadPool`に`execute`メソッドがないためにエラーが発生しました。 +[「有限個のスレッドを作成する」](#有限個のスレッドを作成する)節で我々のスレッドプールは、 `thread::spawn`と似たインターフェイスにするべきと決定したことを思い出してください。 さらに、`execute`関数を実装するので、与えられたクロージャを取り、実行するようにプールの待機中のスレッドに渡します。 `ThreadPool`に`execute`メソッドをクロージャを引数として受け取るように定義します。 -第13章の「ジェネリック引数と`Fn`トレイトを使用してクロージャを保存する」節から、 +第13章の[「キャプチャされた値のクロージャからのムーブと、`Fn`系トレイト」][fn-traits]節から、 3つの異なるトレイトでクロージャを引数として取ることができることを思い出してください: `Fn`、`FnMut`、`FnOnce`です。 ここでは、どの種類のクロージャを使用するか決定する必要があります。最終的には、 標準ライブラリの`thread::spawn`実装に似たことをすることがわかっているので、 @@ -549,8 +472,9 @@ shows us the following: ```rust,ignore pub fn spawn(f: F) -> JoinHandle where - F: FnOnce() -> T + Send + 'static, - T: Send + 'static + F: FnOnce() -> T, + F: Send + 'static, + T: Send + 'static, ``` -それでも、`FnOnce`の後に`()`を使用しています。この`FnOnce`は引数を取らず、値も返さないクロージャを表すからです。 +それでも、`FnOnce`の後に`()`を使用しています。この`FnOnce`は引数を取らず、ユニット型`()`を返すクロージャを表すからです。 関数定義同様に、戻り値の型はシグニチャから省略できますが、引数がなくても、カッコは必要です。 src/lib.rs:4:16 - | -4 | pub fn new(size: usize) -> ThreadPool { - | ^^^^ - | - = note: #[warn(unused_variables)] on by default - = note: to avoid this warning, consider using `_size` instead - -warning: unused variable: `f` - --> src/lib.rs:8:30 - | -8 | pub fn execute(&self, f: F) - | ^ - | - = note: to avoid this warning, consider using `_f` instead +```console +{{#include ../listings/ch20-web-server/no-listing-03-define-execute/output.txt}} ``` -これで警告を受け取るだけになり、コンパイルできるようになりました!しかし、`cargo run`を試して、 +コンパイルできました!しかし、`cargo run`を試して、 ブラウザでリクエストを行うと、章の冒頭で見かけたエラーがブラウザに現れることに注意してください。 ライブラリは、まだ実際に`execute`に渡されたクロージャを呼び出していないのです! @@ -671,18 +568,17 @@ actually calling the closure passed to `execute` yet! #### `new`でスレッド数を検査する -`new`と`execute`の引数で何もしていないので、警告が出続けます。欲しい振る舞いでこれらの関数の本体を実装しましょう。 +`new`と`execute`への引数は、何にも使用していません。欲しい振る舞いでこれらの関数の本体を実装しましょう。 まずはじめに、`new`を考えましょう。先刻、`size`引数に非負整数型を選択しました。負のスレッド数のプールは、 全く道理が通らないからです。しかしながら、0スレッドのプールも全く意味がわかりませんが、0も完全に合法な`usize`です。 `ThreadPool`インスタンスを返す前に`size`が0よりも大きいことを確認するコードを追加し、リスト20-13に示したように、 @@ -694,32 +590,8 @@ as shown in Listing 20-13. ファイル名: src/lib.rs -```rust -# pub struct ThreadPool; -impl ThreadPool { - /// 新しいThreadPoolを生成する。 - /// - /// sizeがプールのスレッド数です。 - /// - /// # パニック - /// - /// sizeが0なら、`new`関数はパニックします。 - /// - /// Create a new ThreadPool. - /// - /// The size is the number of threads in the pool. - /// - /// # Panics - /// - /// The `new` function will panic if the size is zero. - pub fn new(size: usize) -> ThreadPool { - assert!(size > 0); - - ThreadPool - } - - // --snip-- -} +```rust,noplayground +{{#rustdoc_include ../listings/ch20-web-server/listing-20-13/src/lib.rs:here}} ``` -doc commentで`ThreadPool`にドキュメンテーションを追加しました。第14章で議論したように、 +doc commentで`ThreadPool`にドキュメンテーションも追加しました。第14章で議論したように、 関数がパニックすることもある場面を声高に叫ぶセクションを追加することで、 いいドキュメンテーションの実践に(なら)っていることに注意してください。 試しに`cargo doc --open`を実行し、`ThreadPool`構造体をクリックして、`new`の生成されるドキュメンテーションがどんな見た目か確かめてください! -ここでしたように`assert!`マクロを追加する代わりに、リスト12-9のI/Oプロジェクトの`Config::new`のように、 -`new`に`Result`を返させることもできるでしょう。しかし、今回の場合、スレッドなしでスレッドプールを作成しようとするのは、 -回復不能なエラーであるべきと決定しました。野心を感じるのなら、以下のシグニチャの`new`も書いてみて、両者を比較してみてください: +ここでしたように`assert!`マクロを追加する代わりに、リスト12-9のI/Oプロジェクトの`Config::build`のように、 +`new`を`build`に変更し、`Result`を返させることもできるでしょう。しかし、今回の場合、スレッドなしでスレッドプールを作成しようとするのは、 +回復不能なエラーであるべきと決定しました。野心を感じるのなら、以下のシグニチャを持つ`build`関数を書いてみて、`new`関数と比較してみてください: ```rust,ignore -pub fn new(size: usize) -> Result { +pub fn build(size: usize) -> Result { ``` 今や、プールに格納する合法なスレッド数を知る方法ができたので、`ThreadPool`構造体を返す前にスレッドを作成して格納できます。 @@ -782,8 +654,9 @@ the `thread::spawn` signature: ```rust,ignore pub fn spawn(f: F) -> JoinHandle where - F: FnOnce() -> T + Send + 'static, - T: Send + 'static + F: FnOnce() -> T, + F: Send + 'static, + T: Send + 'static, ``` 一旦、合法なサイズを受け取ったら、`ThreadPool`は`size`個の要素を保持できる新しいベクタを生成します。 -この本ではまだ、`with_capacity`関数を使用したことがありませんが、これは`Vec::new`と同じ作業をしつつ、 -重要な違いがあります: ベクタに予めスペースを確保しておくのです。ベクタに`size`個の要素を格納する必要があることはわかっているので、 +`with_capacity`関数は`Vec::new`と同じ作業をしますが、重要な違いがあります: +ベクタに予めスペースを確保しておくのです。ベクタに`size`個の要素を格納する必要があることはわかっているので、 このメモリ確保を前もってしておくと、`Vec::new`よりも少しだけ効率的になります。`Vec::new`は、 要素が挿入されるにつれて、自身のサイズを変更します。 -再び`cargo check`を実行すると、もういくつか警告が出るものの、成功するはずです。 +再び`cargo check`を実行すると、成功するはずです。 この新しい振る舞いを管理するスレッドと`ThreadPool`間に新しいデータ構造を導入することでこの振る舞いを実装します。 -このデータ構造を`Worker`と呼び、プール実装では一般的な用語です。レストランのキッチンで働く人々を思い浮かべてください: +このデータ構造を*ワーカー*と呼び、プール実装では一般的な用語です。 +ワーカーは実行する必要のあるコードを受け取り、ワーカーのスレッドでそのコードを実行します。 +レストランのキッチンで働く人々を思い浮かべてください: 労働者は、お客さんからオーダーが来るまで待機し、それからそれらのオーダーを取り、満たすことに責任を負います。 -`ThreadPool`を生成する際に発生することに以下の変更を加えましょう。このように`Worker`をセットアップした後に、 -スレッドにクロージャを送信するコードを実装します: +`ThreadPool`を作成する際に発生する新しいプロセスは、以下のようになります。 +このように`Worker`をセットアップした後に、スレッドにクロージャを送信するコードを実装します: 1. `id`と`JoinHandle<()>`を保持する`Worker`構造体を定義する。 @@ -975,46 +825,8 @@ Ready? Here is Listing 20-15 with one way to make the preceding modifications. ファイル名: src/lib.rs -```rust -use std::thread; - -pub struct ThreadPool { - workers: Vec, -} - -impl ThreadPool { - // --snip-- - 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 - } - } - // --snip-- -} - -struct Worker { - id: usize, - thread: thread::JoinHandle<()>, -} - -impl Worker { - fn new(id: usize) -> Worker { - let thread = thread::spawn(|| {}); - - Worker { - id, - thread, - } - } -} +```rust,noplayground +{{#rustdoc_include ../listings/ch20-web-server/listing-20-15/src/lib.rs:here}} ``` -外部のコード(*src/bin/main.rs*のサーバなど)は、`ThreadPool`内で`Worker`構造体を使用していることに関する実装の詳細を知る必要はないので、 +外部のコード(*src/main.rs*のサーバなど)は、`ThreadPool`内で`Worker`構造体を使用していることに関する実装の詳細を知る必要はないので、 `Worker`構造体とその`new`関数は非公開にしています。`Worker::new`関数は与えた`id`を使用し、 空のクロージャを使って新しいスレッドを立ち上げることで生成される`JoinHandle<()>`インスタンスを格納します。 + + +> 注釈: 十分なシステムリソースが無いためにOSがスレッドを作成できない場合、`thread::spawn`はパニックするでしょう。 +> これは、一部のスレッドが作成に成功したとしても、サーバ全体のパニックを引き起こすでしょう。 +> 簡潔性を優先してこの挙動はまあよしとしますが、実運用のスレッドプール実装では、 +> [`std::thread::Builder`][builder]と、パニックするのではなく`Result`を返す[`spawn`][builder-spawn]メソッドを使用するのがよいでしょう。 + -さて、`thread::spawn`に与えられたクロージャが全く何もしないという問題に取り組みましょう。現在、 +次に取り組む問題は、`thread::spawn`に与えられたクロージャが全く何もしない問題です。現在、 `execute`メソッドで実行したいクロージャを得ています。ですが、`ThreadPool`の生成中、`Worker`それぞれを生成する際に、 実行するクロージャを`thread::spawn`に与える必要があります。 作ったばかりの`Worker`構造体に`ThreadPool`が保持するキューから実行するコードをフェッチして、 そのコードをスレッドが実行できるように送信してほしいです。 -第16章でこのユースケースにぴったりであろう*チャンネル*(2スレッド間コミュニケーションをとる単純な方法)について学びました。 +第16章で学んだチャンネル—2スレッド間コミュニケーションをとる単純な方法—は、このユースケースにぴったりでしょう。 チャンネルをキューの仕事として機能させ、`execute`は`ThreadPool`から`Worker`インスタンスに仕事を送り、 これが仕事をスレッドに送信します。こちらが計画です: -1. `ThreadPool`はチャンネルを生成し、チャンネルの送信側に就く。 -2. `Worker`それぞれは、チャンネルの受信側に就く。 +1. `ThreadPool`はチャンネルを生成し、チャンネルの送信機に就く。 +2. `Worker`それぞれは、チャンネルの受信機に就く。 3. チャンネルに送信したいクロージャを保持する新しい`Job`構造体を生成する。 -4. `execute`メソッドは、実行したい仕事をチャンネルの送信側に送信する。 -5. スレッド内で、`Worker`はチャンネルの受信側をループし、受け取ったあらゆる仕事のクロージャを実行する。 +4. `execute`メソッドは、実行したい仕事をチャンネルの送信機を通して送信する。 +5. スレッド内で、`Worker`はチャンネルの受信機をループし、受け取ったあらゆる仕事のクロージャを実行する。 -`ThreadPool::new`内でチャンネルを生成し、`ThreadPool`インスタンスに送信側を保持することから始めましょう。リスト20-16のようにですね。 +`ThreadPool::new`内でチャンネルを生成し、`ThreadPool`インスタンスに送信機を保持することから始めましょう。リスト20-16のようにですね。 今の所、`Job`構造体は何も保持しませんが、チャンネルに送信する種類の要素になります。 -リスト20-18: `ThreadPool`を変更して`Job`インスタンスを送信するチャンネルの送信側を格納する +リスト20-18: `ThreadPool`を変更して`Job`インスタンスを転送するチャンネルの送信機を格納する -`ThreadPool::new`内で新しいチャンネルを生成し、プールに送信側を保持させています。これはコンパイルに成功しますが、 -まだ警告があります。 +`ThreadPool::new`内で新しいチャンネルを生成し、プールに送信機を保持させています。 +これはコンパイルに成功します。 -スレッドプールがワーカーを生成する際に各ワーカーにチャンネルの受信側を試しに渡してみましょう。 -受信側はワーカーが大量生産するスレッド内で使用したいことがわかっているので、クロージャ内で`receiver`引数を参照します。 +スレッドプールがチャンネルを生成する際に、各ワーカーにチャンネルの受信機を渡してみましょう。 +受信機はワーカーが大量生産するスレッド内で使用したいことがわかっているので、クロージャ内で`receiver`引数を参照します。 リスト20-17のコードはまだ完璧にはコンパイルできません。 -リスト20-17: チャンネルの受信側をワーカーに渡す +リスト20-17: チャンネルの受信機をワーカーに渡す -多少些細で単純な変更を行いました: チャンネルの受信側を`Worker::new`に渡し、それからクロージャの内側で使用しています。 +多少些細で単純な変更を行いました: チャンネルの受信機を`Worker::new`に渡し、それからクロージャの内側で使用しています。 src/lib.rs:27:42 - | -27 | workers.push(Worker::new(id, receiver)); - | ^^^^^^^^ value moved here in - previous iteration of loop - | - = note: move occurs because `receiver` has type - `std::sync::mpsc::Receiver`, which does not implement the `Copy` trait +```console +{{#include ../listings/ch20-web-server/listing-20-17/output.txt}} ``` このコードは、`receiver`を複数の`Worker`インスタンスに渡そうとしています。第16章を思い出すように、これは動作しません: Rustが提供するチャンネル実装は、複数の*生成者*、単独の*消費者*です。要するに、 -チャンネルの消費側をクローンするだけでこのコードを修正することはできません。たとえできたとしても、 -使用したいテクニックではありません; 代わりに、全ワーカー間で単独の`receiver`を共有することで、 -スレッド間に仕事を分配したいです。 +チャンネルの消費側をクローンするだけでこのコードを修正することはできません。 +また、メッセージを複数の消費者に複数回送信したくはありません; +欲しいのは、各メッセージが一度だけ処理されるような、複数のワーカーを備えた、メッセージの単一のリストです。 -リスト20-18: `Arc`と`Mutex`を使用してワーカー間でチャンネルの受信側を共有する +リスト20-18: `Arc`と`Mutex`を使用してワーカー間でチャンネルの受信機を共有する -`ThreadPool::new`で、チャンネルの受信側を`Arc`と`Mutex`に置いています。新しいワーカーそれぞれに対して、 -`Arc`をクローンして参照カウントを跳ね上げているので、ワーカーは受信側の所有権を共有することができます。 +`ThreadPool::new`で、チャンネルの受信機を`Arc`と`Mutex`に置いています。新しいワーカーそれぞれに対して、 +`Arc`をクローンして参照カウントを跳ね上げているので、ワーカーは受信機の所有権を共有することができます。 最後に`ThreadPool`に`execute`メソッドを実装しましょう。 `Job`も構造体から`execute`が受け取るクロージャの型を保持するトレイトオブジェクトの型エイリアスに変更します。 -第19章の「型エイリアスで型同義語を生成する」節で議論したように、型エイリアスにより長い型を短くできます。 +第19章の[「型エイリアスで型同義語を生成する」][creating-type-synonyms-with-type-aliases]節で議論したように、 +使いやすさのために、型エイリアスを利用して長い型を短くすることができます。 リスト20-19をご覧ください。 ミューテックスのロックを獲得できたら、`recv`を呼び出してチャンネルから`Job`を受け取ります。 -最後の`unwrap`もここであらゆるエラーを超えていき、これはチャンネルの送信側を保持するスレッドが閉じた場合に発生する可能性があり、 -受信側が閉じた場合に`send`メソッドが`Err`を返すのと似ています。 +最後の`unwrap`もここであらゆるエラーを超えていき、これはチャンネルの送信機を保持するスレッドが閉じた場合に発生する可能性があり、 +受信機が閉じた場合に`send`メソッドが`Err`を返すのと似ています。 - -理論的には、このコードはコンパイルできるはずです。残念ながら、Rustコンパイラはまだ完全ではなく、 -このようなエラーが出ます: - -```text -error[E0161]: cannot move a value of type std::ops::FnOnce() + -std::marker::Send: the size of std::ops::FnOnce() + std::marker::Send cannot be -statically determined -(エラー: std::ops::FnOnce() + std::marker::Sendの値をムーブできません: -std::ops::FnOnce() + std::marker::Sendのサイズを静的に決定できません) - --> src/lib.rs:63:17 - | -63 | (*job)(); - | ^^^^^^ -``` - - - -問題が非常に謎めいているので、エラーも非常に謎めいています。`Box`に格納された`FnOnce`クロージャを呼び出すためには(`Job`型エイリアスがそう)、 -呼び出す際にクロージャが`self`の所有権を奪うので、 -クロージャは自身を`Box`*から*ムーブする必要があります。一般的に、Rustは`Box`から値をムーブすることを許可しません。 -コンパイラには、`Box`の内側の値がどれほどの大きさなのか見当がつかないからです: -第15章で`Box`に格納して既知のサイズの値を得たい未知のサイズの何かがあるために`Box`を正確に使用したことを思い出してください。 - - - -リスト17-15で見かけたように、記法`self: Box`を使用するメソッドを書くことができ、 -これにより、メソッドは`Box`に格納された`Self`値の所有権を奪うことができます。 -それがまさしくここで行いたいことですが、残念ながらコンパイラはさせてくれません: -クロージャが呼び出された際に振る舞いを実装するRustの一部は、`self: Box`を使用して実装されていないのです。 -故に、コンパイラはまだこの場面において`self: Box`を使用してクロージャの所有権を奪い、 -クロージャを`Box`からムーブできることを理解していないのです。 - - - -Rustはまだコンパイラの改善途上にあり、リスト20-20のコードは、 -将来的にうまく動くようになるべきです。まさしくあなたのような方がこれや他の問題を修正しています!この本を完了したら、 -是非ともあなたにも参加していただきたいです。 - - - -ですがとりあえず、手頃なトリックを使ってこの問題を回避しましょう。この場合、`self: Box`で、 -`Box`の内部の値の所有権を奪うことができることをコンパイラに明示的に教えてあげます; -そして、一旦クロージャの所有権を得たら、呼び出せます。これには、 -シグニチャに`self: Box`を使用する`call_box`というメソッドのある新しいトレイト`FnBox`を定義すること、 -`FnOnce()`を実装する任意の型に対して`FnBox`を定義すること、型エイリアスを新しいトレイトを使用するように変更すること、 -`Worker`を`call_box`メソッドを使用するように変更することが関連します。これらの変更は、 -リスト20-21に表示されています。 - - - -ファイル名: src/lib.rs - -```rust,ignore -trait FnBox { - fn call_box(self: Box); -} - -impl FnBox for F { - fn call_box(self: Box) { - (*self)() - } -} - -type Job = Box; - -// --snip-- - -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.call_box(); - } - }); - - Worker { - id, - thread, - } - } -} -``` - - - -リスト20-21: 新しいトレイト`FnBox`を追加して`Box`の現在の制限を回避する - - - -まず、`FnBox`という新しいトレイトを作成します。このトレイトには`call_box`という1つのメソッドがあり、 -これは、`self: Box`を取って`self`の所有権を奪い、`Box`から値をムーブする点を除いて、 -他の`Fn*`トレイトの`call`メソッドと類似しています。 - - - -次に、`FnOnce()`トレイトを実装する任意の型`F`に対して`FnBox`トレイトを実装します。実質的にこれは、 -あらゆる`FnOnce()`クロージャが`call_box`メソッドを使用できることを意味します。`call_box`の実装は、 -`(*self)()`を使用して`Box`からクロージャをムーブし、クロージャを呼び出します。 - - - -これで`Job`型エイリアスには、新しいトレイトの`FnBox`を実装する何かの`Box`である必要が出てきました。 -これにより、クロージャを直接呼び出す代わりに`Job`値を得た時に`Worker`の`call_box`を使えます。 -任意の`FnOnce()`クロージャに対して`FnBox`トレイトを実装することは、チャンネルに送信する実際の値は何も変えなくてもいいことを意味します。 -もうコンパイラは、我々が行おうとしていることが平気なことであると認識できます。 - - -このトリックは非常にこそこそしていて複雑です。完璧に筋が通らなくても心配しないでください; -いつの日か、完全に不要になるでしょう。 +これで私たちのスレッドプールは動作する状態になりました! +`cargo run`して、リクエストを送ってみてください: - -このトリックの実装で、スレッドプールは動く状態になります!`cargo run`を実行し、 -リクエストを行なってください: - -```text +```console $ cargo run Compiling hello v0.1.0 (file:///projects/hello) -warning: field is never used: `workers` +warning: field is never read: `workers` --> src/lib.rs:7:5 | 7 | workers: Vec, | ^^^^^^^^^^^^^^^^^^^^ | - = note: #[warn(dead_code)] on by default + = note: `#[warn(dead_code)]` on by default -warning: field is never used: `id` - --> src/lib.rs:61:5 +warning: field is never read: `id` + --> src/lib.rs:48:5 | -61 | id: usize, +48 | id: usize, | ^^^^^^^^^ - | - = note: #[warn(dead_code)] on by default -warning: field is never used: `thread` - --> src/lib.rs:62:5 +warning: field is never read: `thread` + --> src/lib.rs:49:5 | -62 | thread: thread::JoinHandle<()>, +49 | thread: thread::JoinHandle<()>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: #[warn(dead_code)] on by default - Finished dev [unoptimized + debuginfo] target(s) in 0.99 secs +warning: `hello` (lib) generated 3 warnings + Finished dev [unoptimized + debuginfo] target(s) in 1.40s Running `target/debug/hello` Worker 0 got a job; executing. Worker 2 got a job; executing. @@ -1803,12 +1271,23 @@ thread run them. サーバが多くのリクエストを受け取っても、システムは過負荷にならないでしょう。*/sleep*にリクエストを行なっても、 サーバは他のスレッドに実行させることで他のリクエストを提供できるでしょう。 + + +> 注釈: */sleep* を複数のブラウザウィンドウで同時に開くと、5秒間隔でひとつずつロードするかもしれません。 +> webブラウザによっては、キャッシュのために、複数の同一リクエストを逐次的に実行します。 +> この制限は私たちのwebサーバによって引き起こされているものではありません。 + -第18章で`while let`ループを学んだ後で、なぜリスト20-22に示したようにワーカースレッドのコードを記述しなかったのか、 +第18章で`while let`ループを学んだ後で、なぜリスト20-21に示したようにワーカースレッドのコードを記述しなかったのか、 不思議に思っている可能性があります。 -リスト20-22: `while let`を使用したもう1つの`Worker::new`の実装 +リスト20-21: `while let`を使用したもう1つの`Worker::new`の実装 このコードはコンパイルでき、動きますが、望み通りのスレッドの振る舞いにはなりません: @@ -1865,19 +1325,39 @@ call to `job.call_box()`, meaning other workers cannot receive jobs. `Mutex`構造体には公開の`unlock`メソッドがありません。ロックの所有権が、 `lock`メソッドが返す`LockResult>`内の`MutexGuard`のライフタイムに基づくからです。 コンパイル時には、ロックを保持していない限り、借用チェッカーはそうしたら、`Mutex`に保護されるリソースにはアクセスできないという規則を強制できます。 -しかし、この実装は、`MutexGuard`のライフタイムについて熟考しなければ、 -意図したよりもロックが長い間保持される結果になり得ます。`while`式の値がブロックの間中スコープに残り続けるので、 -ロックは`job.call_box`の呼び出し中保持されたままになり、つまり、他のワーカーが仕事を受け取れなくなるのです。 - - - -代わりに`loop`を使用し、ロックと仕事をブロックの外ではなく、内側で獲得することで、 -`lock`メソッドが返す`MutexGuard`は`let job`文が終わると同時にドロップされます。 -これにより、複数のリクエストを並行で提供し、ロックは`recv`の呼び出しの間は保持されるけれども、 -`job.call_box`の呼び出しの前には解放されることを保証します。 +しかし、この実装は、`MutexGuard`のライフタイムを心に留めなければ、 +意図したよりもロックが長い間保持される結果になり得ます。 + + + +`let job = receiver.lock().unwrap().recv().unwrap();`を使用するリスト20-20のコードは、機能します。 +`let`を使用する場合、等号の右辺の式内で使用される任意の一時値は、`let`文の終わりの直後にドロップされるからです。 +しかしながら、`while let`(と`if let`と`match`)は、関連するブロックの終わりまで一時値をドロップしません。 +リスト20-21では、`job()`への呼び出しの間ロックは保持されたままになり、 +他のワーカーがジョブを受信できないことになります。 + + + +[creating-type-synonyms-with-type-aliases]: +ch19-04-advanced-types.html#型エイリアスで型同義語を生成する +[integer-types]: ch03-02-data-types.html#整数型 +[fn-traits]: +ch13-01-closures.html#キャプチャされた値のクロージャからのムーブとfn系トレイト +[builder]: https://doc.rust-lang.org/std/thread/struct.Builder.html +[builder-spawn]: https://doc.rust-lang.org/std/thread/struct.Builder.html#method.spawn diff --git a/src/ch20-03-graceful-shutdown-and-cleanup.md b/src/ch20-03-graceful-shutdown-and-cleanup.md index cdadd2c99..75912cb35 100644 --- a/src/ch20-03-graceful-shutdown-and-cleanup.md +++ b/src/ch20-03-graceful-shutdown-and-cleanup.md @@ -5,7 +5,7 @@ ## 正常なシャットダウンと片付け -リスト20-21のコードは、意図した通り、スレッドプールの使用を通してリクエストに非同期に応答できます。 +リスト20-20のコードは、意図した通り、スレッドプールの使用を通してリクエストに非同期に応答できます。 直接使用していない`workers`、`id`、`thread`フィールドについて警告が出ます。この警告は、現在のコードは何も片付けていないことを思い出させてくれます。 優美さに欠けるctrl-cを使用してメインスレッドを停止させる方法を使用すると、 リクエストの処理中であっても、他のスレッドも停止します。 -では、閉じる前に取り掛かっているリクエストを完了できるように、プールの各スレッドに対して`join`を呼び出す`Drop`トレイトを実装します。 +では次に、閉じる前に取り掛かっているリクエストを完了できるように、プールの各スレッドに対して`join`を呼び出す`Drop`トレイトを実装します。 そして、スレッドに新しいリクエストの受付を停止し、終了するように教える方法を実装します。 このコードが動いているのを確かめるために、サーバを変更して正常にスレッドプールを終了する前に2つしかリクエストを受け付けないようにします。 @@ -40,12 +41,12 @@ accept only two requests before gracefully shutting down its thread pool. スレッドプールに`Drop`を実装するところから始めましょう。プールがドロップされると、 -スレッドは全てjoinして、作業を完了するのを確かめるべきです。リスト20-23は、`Drop`実装の最初の試みを表示しています; +スレッドは全てjoinして、作業を完了するのを確かめるべきです。リスト20-22は、`Drop`実装の最初の試みを表示しています; このコードはまだ完全には動きません。 -リスト20-23: スレッドプールがスコープを抜けた時にスレッドをjoinさせる +リスト20-22: スレッドプールがスコープを抜けた時にスレッドをjoinさせる src/lib.rs:65:13 - | -65 | worker.thread.join().unwrap(); - | ^^^^^^ cannot move out of borrowed content +```console +{{#include ../listings/ch20-web-server/listing-20-22/output.txt}} ``` @@ -120,7 +108,7 @@ thread to run. これをリスト17-15では行いました: `Worker`が代わりに`Option>`を保持していれば、 `Option`に対して`take`メソッドを呼び出し、`Some`列挙子から値をムーブし、その場所に`None`列挙子を残すことができます。 言い換えれば、実行中の`Worker`には`thread`に`Some`列挙子があり、`Worker`を片付けたい時には、 -ワーカーが実行するスレッドがないように`Some`を`None`で置き換えるのです。 +`Worker`が実行するスレッドがないように`Some`を`None`で置き換えるのです。 src/lib.rs:65:27 - | -65 | worker.thread.join().unwrap(); - | ^^^^ - -error[E0308]: mismatched types - --> src/lib.rs:89:13 - | -89 | thread, - | ^^^^^^ - | | - | expected enum `std::option::Option`, found struct - `std::thread::JoinHandle` - | help: try using a variant of the expected type: `Some(thread)` - | - = note: expected type `std::option::Option>` - found type `std::thread::JoinHandle<_>` +```console +{{#include ../listings/ch20-web-server/no-listing-04-update-worker-definition/output.txt}} ``` これらの変更によって、コードは警告なしでコンパイルできます。ですが悪い知らせは、このコードが期待したようにはまだ機能しないことです。 @@ -264,44 +211,26 @@ for the first thread to finish. 最初のスレッドが完了するのを待機してメインスレッドは永遠にブロックされるでしょう。 - -この問題を修正するには、スレッドが、実行すべき`Job`か、リッスンをやめて無限ループを抜ける通知をリッスンするように、 -変更します。`Job`インスタンスの代わりに、チャンネルはこれら2つのenum列挙子の一方を送信します。 - - - -ファイル名: src/lib.rs - -```rust -# struct Job; -enum Message { - NewJob(Job), - Terminate, -} -``` - - -この`Message` enumはスレッドが実行すべき`Job`を保持する`NewJob`列挙子か、スレッドをループから抜けさせ、 -停止させる`Terminate`列挙子のどちらかになります。 +この問題を修正するためには、`ThreadPool`の`drop`実装に対して変更を加え、 +その後`Worker`ループに対して変更を加える必要があるでしょう。 -チャンネルを調整し、型`Job`ではなく、型`Message`を使用するようにする必要があります。リスト20-24のようにですね。 +まず、スレッドが完了するのを待つ前に明示的に`sender`をドロップするように、 +`ThreadPool`の`drop`実装を変更します。リスト20-23は +明示的に`sender`をドロップするような`ThreadPool`に対する変更を示しています。 +`ThreadPool`から`sender`をムーブできるようにするために、 +スレッドで使ったのと同じ`Option`と`take`のテクニックを使用します: - -リスト20-24: `Message`値を送受信し、`Worker`が`Message::Terminate`を受け取ったら、ループを抜ける - - -`Message` enumを具体化するために、2箇所で`Job`を`Message`に変更する必要があります: -`ThreadPool`の定義と`Worker::new`のシグニチャです。`ThreadPool`の`execute`メソッドは、 -仕事を`Message::NewJob`列挙子に包んで送信する必要があります。それから、 -`Message`がチャンネルから受け取られる`Worker::new`で、`NewJob`列挙子が受け取られたら、 -仕事が処理され、`Terminate`列挙子が受け取られたら、スレッドはループを抜けます。 +リスト20-23: ワーカースレッドをjoinする前に明示的に`sender`をドロップする -これらの変更と共に、コードはコンパイルでき、リスト20-21の後と同じように機能し続けます。ですが、 -`Terminate`のメッセージを何も生成していないので、警告が出るでしょう。 -`Drop`実装をリスト20-25のような見た目に変更してこの警告を修正しましょう。 +`sender`をドロップすると、チャンネルは閉じられ、それ以上メッセージが送られないことを示します。 +これが発生すると、ワーカーが無限ループ内で行うすべての`recv`への呼び出しは失敗し、エラーを返すでしょう。 +リスト20-24では、そのような場合に正常にループを脱出するように`Worker`ループを変更します。 +正常とは、`ThreadPool`の`drop`実装がスレッドに対して`join`を呼び出すときに、スレッドが完了するようにします。 - -リスト20-25: 各ワーカースレッドに対して`join`を呼び出す前にワーカーに`Message::Terminate`を送信する - - - -今では、ワーカーを2回走査しています: 各ワーカーに`Terminate`メッセージを送信するために1回と、 -各ワーカースレッドに`join`を呼び出すために1回です。メッセージ送信と`join`を同じループで即座に行おうとすると、 -現在の繰り返しのワーカーがチャンネルからメッセージを受け取っているものであるか保証できなくなってしまいます。 - - - -2つの個別のループが必要な理由をよりよく理解するために、2つのワーカーがある筋書きを想像してください。 -単独のループで各ワーカーを走査すると、最初の繰り返しでチャンネルに停止メッセージが送信され、 -`join`が最初のワーカースレッドで呼び出されます。その最初のワーカーが現在、リクエストの処理で忙しければ、 -2番目のワーカーがチャンネルから停止メッセージを受け取り、閉じます。最初のワーカーの終了待ちをしたままですが、 -2番目のスレッドが停止メッセージを拾ってしまったので、終了することは絶対にありません。デッドロックです! - - -この筋書きを回避するために、1つのループでまず、チャンネルに対して全ての`Terminate`メッセージを送信します; -そして、別のループで全スレッドのjoinを待ちます。一旦停止メッセージを受け取ったら、各ワーカーはチャンネルからのリクエストの受付をやめます。 -故に、存在するワーカーと同じ数だけ停止メッセージを送れば、`join`がスレッドに対して呼び出される前に、 -停止メッセージを各ワーカーが受け取ると確信できるわけです。 +リスト20-24: `recv`がエラーを返したときに明示的にループを抜ける このコードが動いているところを確認するために、`main`を変更してサーバを正常に閉じる前に2つしかリクエストを受け付けないようにしましょう。 -リスト20-26のようにですね。 +リスト20-25のようにですね。 -ファイル名: src/bin/main.rs +ファイル名: src/main.rs ```rust,ignore -fn main() { - 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."); -} +{{#rustdoc_include ../listings/ch20-web-server/listing-20-25/src/main.rs:here}} ``` -リスト20-26: ループを抜けることで、2つのリクエストを処理した後にサーバを閉じる +リスト20-25: ループを抜けることで、2つのリクエストを処理した後にサーバを閉じる + +```console $ cargo run Compiling hello v0.1.0 (file:///projects/hello) - Finished dev [unoptimized + debuginfo] target(s) in 1.0 secs + Finished dev [unoptimized + debuginfo] target(s) in 1.0s Running `target/debug/hello` Worker 0 got a job; executing. -Worker 3 got a job; executing. Shutting down. -Sending terminate message to all workers. -Shutting down all workers. Shutting down worker 0 -Worker 1 was told to terminate. -Worker 2 was told to terminate. -Worker 0 was told to terminate. -Worker 3 was told to terminate. +Worker 3 got a job; executing. +Worker 1 disconnected; shutting down. +Worker 2 disconnected; shutting down. +Worker 3 disconnected; shutting down. +Worker 0 disconnected; shutting down. Shutting down worker 1 Shutting down worker 2 Shutting down worker 3 @@ -565,34 +362,35 @@ Shutting down worker 3 ワーカーとメッセージの順番は異なる可能性があります。どうやってこのコードが動くのかメッセージからわかります: -ワーカー0と3が最初の2つのリクエストを受け付け、そして3番目のリクエストではサーバは接続の受け入れをやめます。 -`main`の最後で`ThreadPool`がスコープを抜ける際、`Drop`実装が割り込み、プールが全ワーカーに停止するよう指示します。 -ワーカーはそれぞれ、停止メッセージを確認した時にメッセージを出力し、それからスレッドプールは各ワーカースレッドを閉じる`join`を呼び出します。 +ワーカー0と3が最初の2つのリクエストを受け付けます。 +2回目の接続の後は、サーバは接続を受け付けるのをやめ、ワーカー3がジョブを開始する前に`ThreadPool`の`Drop`実装が実行を開始します。 +`sender`をドロップするとすべてのワーカーは切断され、それらに停止するように指示します。 +ワーカーはそれぞれ切断された時にメッセージを出力し、それからスレッドプールは各ワーカースレッドが完了するのを待つために`join`を呼び出します。 -この特定の実行のある面白い側面に注目してください: `ThreadPool`はチャンネルに停止メッセージを送信しますが、 -どのワーカーがそのメッセージを受け取るよりも前に、ワーカー0のjoinを試みています。ワーカー0はまだ停止メッセージを受け取っていなかったので、 -メインスレッドはワーカー0が完了するまで待機してブロックされます。その間に、各ワーカーは停止メッセージを受け取ります。 -ワーカー0が完了したら、メインスレッドは残りのワーカーが完了するのを待機します。その時点で全ワーカーは停止メッセージを受け取った後で、 -閉じることができたのです。 +この特定の実行のある面白い側面に注目してください: `ThreadPool`は`sender`をドロップしますが、 +どのワーカーがそのエラーを受け取るよりも前に、ワーカー0のjoinを試みています。ワーカー0はまだ`recv`からエラーを受け取っていなかったので、 +メインスレッドはワーカー0が完了するまで待機してブロックされます。その間に、ワーカー3はジョブを受け取り、 +その後すべてのスレッドはエラーを受け取ります。 +ワーカー0が完了したら、メインスレッドは残りのワーカーが完了するのを待機します。 +その時点で全ワーカーはループを脱出し、停止したのです。 -ファイル名: src/bin/main.rs +ファイル名: src/main.rs ```rust,ignore -extern crate hello; -use hello::ThreadPool; - -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; -use std::fs::File; -use std::thread; -use std::time::Duration; - -fn main() { - 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."); -} - -fn handle_connection(mut stream: TcpStream) { - let mut buffer = [0; 1024]; - stream.read(&mut buffer).unwrap(); - - let get = b"GET / HTTP/1.1\r\n"; - let sleep = b"GET /sleep HTTP/1.1\r\n"; - - let (status_line, filename) = if buffer.starts_with(get) { - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else if buffer.starts_with(sleep) { - thread::sleep(Duration::from_secs(5)); - ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") - } else { - ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html") - }; - - let mut file = File::open(filename).unwrap(); - let mut contents = String::new(); - - file.read_to_string(&mut contents).unwrap(); - - let response = format!("{}{}", status_line, contents); - - stream.write(response.as_bytes()).unwrap(); - stream.flush().unwrap(); -} +{{#rustdoc_include ../listings/ch20-web-server/no-listing-07-final-code/src/main.rs}} ``` * `ThreadPool`とその公開メソッドにもっとドキュメンテーションを追加する。 * ライブラリの機能のテストを追加する。 * `unwrap`の呼び出しをもっと頑健なエラー処理に変更する。 * `ThreadPool`を使用してWebリクエスト以外のなんらかの作業を行う。 -* *https://crates.io* でスレッドプールのクレートを探して、そのクレートを代わりに使用して似たWebサーバを実装する。 +* [crates.io](https://crates.io/) でスレッドプールのクレートを探して、そのクレートを代わりに使用して似たWebサーバを実装する。 そして、APIと頑健性を我々が実装したものと比較する。