今年の言語はRust その６１

# 今年の言語はRust その６１

Rustを学びます

Rustの日本語ドキュメント 2nd Edition
https://doc.rust-jp.rs/book/second-edition/

オリジナル(英語)
https://doc.rust-lang.org/book/


## 実行環境

```sh
$ 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を使って接続を試します。

```rust
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ブラウザで指定のアドレス:ポートにアクセスすると、接続されます。

```bash
$ cargo run

Connection established
Connection established
Connection established
Connection established
Connection established
Connection established
Connection established
Connection established
Connection established
Connection established
```

#### パケットを読み取る

```rust
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[..]));
}
```

```bash
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

```rust
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を返す

```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>
```

```rust
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が返ってきたよー！

#### リクエストにバリデーションをかけ、選択的にレスポンスを返す


ルーティングさせてみます


```rust
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{

	}
	
}
```


リファクタリングして終了です。

```rust
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](https://amzn.to/2VdAQ6y)

<a href="https://www.amazon.co.jp/%E5%AE%9F%E8%B7%B5Rust%E5%85%A5%E9%96%80-%E8%A8%80%E8%AA%9E%E4%BB%95%E6%A7%98%E3%81%8B%E3%82%89%E9%96%8B%E7%99%BA%E6%89%8B%E6%B3%95%E3%81%BE%E3%81%A7-%CE%BAeen/dp/4297105594/ref=as_li_ss_il?_encoding=UTF8&psc=1&refRID=KADG0R1CJZD9Y07B3P7E&linkCode=li2&tag=peace098beat-22&linkId=52fef8a307968a1010a3375899274077&language=ja_JP" target="_blank"><img border="0" src="//ws-fe.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=4297105594&Format=_SL160_&ID=AsinImage&MarketPlace=JP&ServiceVersion=20070822&WS=1&tag=peace098beat-22&language=ja_JP" ></a><img src="https://ir-jp.amazon-adsystem.com/e/ir?t=peace098beat-22&language=ja_JP&l=li2&o=9&a=4297105594" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />

※アフェっときます