0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

今年の言語はRust その61

Posted at

今年の言語は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

※アフェっときます

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?