HTTP、TCP。
成果物
参考
- https://doc.rust-jp.rs/book/second-edition/ch20-00-final-project-a-web-server.html
- https://doc.rust-jp.rs/book/second-edition/ch20-01-single-threaded.html
シングルスレッドWebサーバ構築
cargo new hello --bin
src/main.rs
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!("接続が確立しました!"); } }
cargo run
ブラウザのロケール欄に127.0.0.1:7878
を入力してアクセスする。
ターミナルに接続が確立しました!
と出力される。
サーバはCtrl
+C
キーなどで強制終了する。(強制終了の方法はターミナル次第)
リクエストを読み取る
use std::io::prelude::*; use std::net::TcpStream; 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(); 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[..])); }
$ cargo run Compiling hello v0.1.0 (/tmp/work/Rust.HttpServer.20190709080759/src/1/hello) Finished dev [unoptimized + debuginfo] target(s) in 8.38s Running `/tmp/work/Rust.HttpServer.20190709080759/src/1/hello/target/debug/hello` Request: GET / HTTP/1.1 Host: 127.0.0.1:7878 Connection: keep-alive Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Raspbian Chromium/72.0.3626.121 Chrome/72.0.3626.121 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate, br Accept-Language: ja,en-US;q=0.9,en;q=0.8
HTTPリクエスト
HTTPプロトコルは以下のようなテキストフォーマットである。
Method Request-URI HTTP-Version CRLF headers CRLF message-body
たとえば以下。
HTTP/1.1 200 OK\r\n\r\n
これをサーバが返すコードを以下に示す。
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"; stream.write(response.as_bytes()).unwrap(); stream.flush().unwrap(); }
ターミナルには何もでない。ブラウザもエラーがでなくなり、真っ白になる。
HTMLを返す
サーバが返却するhello.html
ファイルをルート直下に配置する。
hello.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Hello!</title> </head> <body> <h1>Hello!</h1> <p>Hi from Rust</p> </body> </html>
use std::io::prelude::*; use std::net::TcpStream; use std::net::TcpListener; 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(); 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(); }
cargo run
ブラウザで127.0.0.1:7878
にアクセスすると、hello.htmlの内容が表示される。
リクエストにバリデーションをかける
fn handle_connection(mut stream: TcpStream) { let mut buffer = [0; 512]; stream.read(&mut buffer).unwrap(); let get = b"GET / HTTP/1.1\r\n"; 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 { // 何か他の要求 // some other request } }
404ページを実装する。
else { let status_line = "HTTP/1.1 404 NOT FOUND\r\n\r\n"; let mut file = File::open("404.html").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(); }
404.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Hello!</title> </head> <body> <!-- ああ! --> <h1>Oops!</h1> <!-- すいません。要求しているものが理解できません --> <p>Sorry, I don't know what you're asking for.</p> </body> </html>
cargo run
ブラウザで127.0.0.1:7878/foo
など存在しないURLを指定すると、404.htmlを返す。
リファクタリング
fn handle_connection(mut stream: TcpStream) { let mut buffer = [0; 512]; stream.read(&mut buffer).unwrap(); let get = b"GET / HTTP/1.1\r\n"; let (status_line, filename) = if buffer.starts_with(get) { ("HTTP/1.1 200 OK\r\n\r\n", "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(); }
対象環境
- Raspbierry pi 3 Model B+
- Raspbian stretch 9.0 2018-11-13
bash 4.4.12(1)-release
rustc 1.34.2 (6c2484dc3 2019-05-13)
- cargo 1.34.0 (6789d8a0a 2019-04-01)
$ uname -a Linux raspberrypi 4.19.42-v7+ #1219 SMP Tue May 14 21:20:58 BST 2019 armv7l GNU/Linux
前回まで
- Rustを学んでみたい(プログラミング言語)
- Rustの環境構築
- RustでHelloWorld
- Rustの和訳ドキュメント
- Cargoでプロジェクト作成・ビルド・実行
- クレートとは?
- Rustで関数を使ってみる
- Rustでモジュールを使ってみる
- Rustで乱数を生成する(rand)
- Rustで標準入力する(std::io::stdin().read_line())
- RustでMatch判定する(match)
- Rustでprintとread_lineを1行にする方法
- Rustで数当てゲーム
- クレート名にドット.が使えない
- Rustの変数と可変性(let, mut) error[E0384]: cannot assign twice to immutable variable
x
- Rustのimmutable束縛とconst定数の違い
- RustのREPL、evcxrのインストールに失敗した
- Rustでコンパイルするときの変数未使用warningを消す
- Rustの変数(再代入、再宣言(シャドーイング))
- Rustのシャドーイングについて
- イミュータブルについて(副作用)
- Rustの定数(const)
- Rustのデータ型(数値)
- Rustのデータ型(論理)
- Rustのデータ型(文字)
- Rustのデータ型(タプル)
- Rustのデータ型(配列)
- Rustの関数
- Rustのif式
- Rustのくりかえし文(loop)
- Rustのくりかえし文(while)
- Rustのくりかえし文(for)
- Rustの所有権(ムーブ)
- Rustの所有権(関数)
- Rustの所有権(スライス)
- Rustの構造体(定義とインスタンス化)
- Rustの構造体(プログラム例)
- Rustの構造体(メソッド)
- Rustの列挙型(enum)
- Rustの列挙型(enum)
- Rustの列挙型(enum)
- Rustのmatch(制御フロー演算子)
- RustでNULLを扱う(Option, Some, None)
- NULL参照は10億ドルの失敗だった
- Rustの列挙型に独自表示を実装する(E0277 対策 std::fmt::Display 実装)
- RustのIfLet(matchの糖衣構文)
- Rustのプロジェクト構造
- Rustのcargoでライブラリ&テスト(単体、結合)
- Rustのモジュール(mod)
- Rustのモジュール(pub)
- Rustのmod参照方法(
mod 子モジュール名;
,use 要素名;
,extern crate クレート名;
,super
) - Rustのインポートまとめ(Rust2018)
- RustのコレクションVec型
- RustのコレクションString型
- RustのコレクションHashMap型
- Rustのコレクション(練習問題)
- Rustのエラー処理
- Rustのジェネリクス
- Rustのトレイト
- Rustのライフタイム1
- Rustのライフタイム2(構造体の定義)
- Rustのライフタイム3(ライフタイム省略)
- Rustのライフタイム4(impl定義)
- Rustの静的ライフタイム5('static)
- Rustのライフタイム6(ジェネリクス、トレイト境界とともに)
- Rustのテストコードを書く
- Rustのテスト実行
- Rustのテスト体系化
- Rustでコマンドライン引数を受け取る
- Rustのファイル読込
- Rustでリファクタリング(モジュール性とエラー処理の向上)
- Rustでテスト駆動開発
- Rustで環境変数を取得する
- RustでStdErr出力
- Rustのクロージャ
- Rustのイテレータ
- Rustのイテレータ(Minigrep改善)
- Rustのイテレータ(パフォーマンス)
- Rustのイテレータ(Minigrep改善)
- Rustのcargo(ビルドのカスタマイズ)
- Rustのcargo(cargo docでドキュメント生成)
- Rustのエクスポート(pub use)
- Rustのクレートを公開する方法(crates.io)
- Rustのcargoでワークスペースをつくる
- Rustのcargo installでバイナリをインストールする
- Rustのcargoを拡張する方法
- Rustのスマートポインタ
- スマートポインタBox
- Rustのスマートポインタ(Derefトレイト)
- Rustのスマートポインタ(Dropトレイト)
- Rustのスマートポインタ(Rc
) - Rustのスマートポインタ(RefCell
) - Rustのスマートポインタ(Weak
) - Rustのスレッド
- Rustのスレッド(メッセージ送受信)
- Rustのスレッド(Mutex、Arc)
- Rustのスレッド(Send、Syncトレイト)
- Rustのオブジェクト指向
- RustのOOP(トレイトオブジェクト)
- Rustのオブジェクト指向(デザインパターン)
- Rustのパターン
- Rustのパターン(論駁可能性)
- Rustパターン(記法)
- Rustの高度な機能(Unsafe Rust)
- Rustの高度な機能(ライフタイム)
- Rustの高度な機能(トレイト)
- Rustの高度な機能(型)
- Rustの高度な機能(関数、クロージャ)