今年の言語はRust その61
Rustを学びます
Rustの日本語ドキュメント 2nd Edition
https://doc.rust-jp.rs/book/second-edition/
オリジナル(英語)
https://doc.rust-lang.org/book/
実行環境
$ cargo -V
cargo 1.33.0 (f099fe94b 2019-02-12)
$ rustup -V
rustup 1.17.0 (069c88ed6 2019-03-05)
$ rustc --version
rustc 1.33.0 (2aa4c46cf 2019-02-28)
$ cat /proc/version
Linux version 4.14.97-74.72.amzn1.x86_64 (mockbuild@gobi-build-64002)
(gcc version 7.2.1 20170915 (Red Hat 7.2.1-2) (GCC))
# 1 SMP Tue Feb 5 20:59:30 UTC 2019
$ uname -a
Linux ip-10-100-0-8 4.14.97-74.72.amzn1.x86_64
# 1 SMP Tue Feb 5 20:59:30 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
$ cat /etc/\*release
NAME="Amazon Linux AMI"
VERSION="2018.03"
ID="amzn"
ID_LIKE="rhel fedora"
VERSION_ID="2018.03"
PRETTY_NAME="Amazon Linux AMI 2018.03"
ANSI_COLOR="0;33"
CPE_NAME="cpe:/o:amazon:linux:2018.03:ga"
HOME_URL="http://aws.amazon.com/amazon-linux-ami/"
Amazon Linux AMI release 2018.03
20. マルチスレッドのWebサーバーを構築する
やっとここまで来ました。最後の章です。
実はWebサーバーの仕組みが知りたくて知りたくて60回もやってきました。
以下がレッスン内容になります
- TCPとHTTPについて少し学ぶ。
- ソケットでTCP接続をリッスンする。
- 少量のHTTPリクエストを構文解析する。
- 適切なHTTPレスポンスを生成する。
- スレッドプールでサーバのスループットを強化する。
20.1 シングルスレッドのWebサーバーを構築する
プロトコル
- HTTP (Hyper Text Transfer Protocol)
- TCP (Transmission Control Protocol)
TCP接続をリッスンする
TcpListenerを使って接続を試します。
use std::net::TcpListener;
fn main() {
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
for stream in listener.incoming() {
let stream = stream.unwrap();
println!("Connection established");
}
}
プログラムを実行し、Webブラウザで指定のアドレス:ポートにアクセスすると、接続されます。
$ cargo run
Connection established
Connection established
Connection established
Connection established
Connection established
Connection established
Connection established
Connection established
Connection established
Connection established
パケットを読み取る
extern crate hello;
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; 512];
stream.read(&mut buffer).unwrap();
println!("Request: {:?}", String::from_utf8_lossy(&buffer[..]));
}
Request: "GET / HTTP/1.1\r
Host: 127.0.0.1:7878\r
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0\r
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r
Accept-Language: ja,en-US;q=0.7,en;q=0.3\r
Accept-Encoding: gzip, deflate\r
Connection: keep-alive\r
Cookie: isNotIncognito=true; ki_t=1556436188620%3B1556436188620%3B1556436188620%3B1%3B1; ki_r=; _ga=GA1.1.1341425303.1556435843\r
Upgrade-Insecure-Requests: 1\r\n\r\n\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}"
HTTPリクエストを詳しく見る
HTTPリクエストのフォーマットは以下のようになっている
Method Request-URI HTTP-Version CRLF
headers CRLF
message-body
- Method: GET, POST, ..
- Request-URI: /
- HTTP-Version: HTTP/1.1,..
- Host: ヘッダ
レスポンスを記述する
HTTP-Version Status-Code Reason-Phrase CRLF
headers CRLF
message-body
- HTTP-Version: HTTP/1.1
- Status-Code: 200, 500
- Reason-Phrase: OK
extern crate hello;
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; 512];
stream.read(&mut buffer).unwrap();
let response = "HTTP/1.1 200 OK\r\n\r\n Hello My Server";
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
なんと!サーバーができてしまった!
本物のHTMLを返す
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Hello!</title>
</head>
<style>
h1{
font-size:100px;
}
</style>
<body>
<h1>Hello!</h1>
<p>Hi from Rust</p>
</body>
</html>
extern crate hello;
use std::io::prelude::*;
use std::net::TcpListener;
use std::net::TcpStream;
use std::fs::File;
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; 512];
stream.read(&mut buffer).unwrap();
println!("{:?}", String::from_utf8_lossy(&buffer[..]));
let mut file = File::open("hello.html").unwrap();
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
let response = format!("HTTP/1.1 200 ok\r\n\r\n{}", contents);
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
HTMLが返ってきたよー!
リクエストにバリデーションをかけ、選択的にレスポンスを返す
ルーティングさせてみます
fn handle_connection(mut stream: TcpStream) {
let mut buffer = [0; 512];
// Request
stream.read(&mut buffer).unwrap();
println!("{:?}", String::from_utf8_lossy(&buffer[..]));
// Routing
let get = b"GET / HTTP/1.1\r\n"; // byte[]
// Response
if buffer.starts_with(get) {
let mut file = File::open("hello.html").unwrap();
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
let response = format!("HTTP/1.1 200 ok\r\n\r\n{}", contents);
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}else{
}
}
リファクタリングして終了です。
fn handle_connection(mut stream: TcpStream) {
let mut buffer = [0; 512];
// Request
stream.read(&mut buffer).unwrap();
println!("{:?}", String::from_utf8_lossy(&buffer[..]));
// Response
let get = b"GET / HTTP/1.1\r\n"; // byte[]
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();
}
シングルスレッドのWebサーバーができたぞー!
イエス!
あと、5/8にRustの最新本がでるみたいです! 気が付いてラッキー! (現在5/8)
実践Rust入門[言語仕様から開発手法まで] 単行本(ソフトカバー) – 2019/5/8
※アフェっときます