Rustで
簡易HTTPサーバーを
作ってみよう
Rust LT #1
@helloyuk13
目次
• 自己紹介
• HTTPサーバーのしくみ
• 今回の要件
• Rustで実装する
• まとめ
自己紹介
• とよだゆうき
• サイバーエージェント アドテクスタジオ
• 言語: Scala, Rust, C++, Java, etc
• 興味分野: 並列計算,分散計算,言語処理系
• twitter: @helloyuk13
今日話すこと
• さかのぼること半年前…
• Web系の会社に入ったのでHTTPサーバーくらいわからな
いと!と思って,簡易HTTPサーバーを作ってみた話
• Rustでやったときに必要だったライブラリの話
HTTPサーバーのしくみ
リクエストを受け取って,処理をして,レスポンスを返す
HTTPリクエスト
HTTPレスポンス
あなた Webサーバー
なんらかの処理
(リクエストのパース,
ルーティングなど)
今回の要件
• GET のリクエストを受け取り
• 中でパスを読み取ってから URL をハンドリングし
• それに応じたレスポンスを返す
• …以外のことはやりません!笑
がんばりすぎない!
つまり…
• Cache とか難しいことを考えないし 🙅
• GET 以外はすべて無視するし 🙅
• パフォーマンスも考えないし 🤷♀️
• セキュリティも考えない 🙅
実用性皆無ですが,HTTPサーバーを勉強するにはこれで十分!
ブラウザで見るとこんな感じ
Rustで実装する
• リクエストの受け取り: std::net::TcpStream#read
• リクエストのパース: regex (今回は許してください🙏)
• URL のハンドリング
• レスポンス生成: format! マクロ
• レスポンスの書き出し: std::net::TcpStream#write
なにをつかったらいいの??
リクエストの受け取り
std::net::TcpStream#read
use std::net::TcpStream;
let mut buf = [0; 512];
self.stream.read(&mut buf).expect("parsing error in stream");
let result: String = String::from_utf8(buf.to_vec()).unwrap();
リクエストでくる文字列
"GET /ja/test/test.html HTTP/1.1rn"
"Connection: closern"
"Accept: */*rn"
"Host: xxx.comrn"
"rn"
今回は「リクエストライン」と呼ばれる1行目だけをパースする
リクエストのパース
regex crateを使用する
extern crate regex;
use regex::Regex;
static ref REGEX: Regex = Regex::new(r"(.*) /(.*) HTTP/(.*)rn”).unwrap();
let caps = REGEX.captures(&result).unwrap();
URL のハンドリング
HTML ファイルを読み込み,それをbodyに入れてレスポンスを返す
if req.head.path == "hello" {
let content = read_html("./src/static/assets/html/hello.html");
return Response::builder()
.status(StatusCode::Ok)
.content_type(Mime::Html)
.content_length(len2str(&content))
.body(content)
.build();
}
URL のハンドリング
HTML ファイルを読み込み,それをbodyに入れてレスポンスを返す
fn read_html(path: &'static str) -> String {
let f = File::open(path).expect("failed to open file.");
let mut lines = String::new();
BufReader::new(f).read_to_string(&mut lines).expect(
"failed to read file.",
);
lines
}
レスポンスの生成
format! マクロで整形して,レスポンスの文字列に直す
fn into_http_response(res: &Response) -> String {
format!("HTTP/1.1 {}rnServer: SimpleRustHttpServerrn
Content-Type: {}rnContent-Length: {}rnConnection: Closernrn",
&res.head.status, &res.head.content_type, &res.head.content_length)
}
レスポンスの書き出し
std::net::TcpStream#write
use std::net::TcpStream;
fn write(&mut self, res: Response) {
let res_str = into_http_response(&res);
self.stream.write(res_str.as_bytes()).unwrap();
self.stream.write(res.body.as_bytes()).unwrap();
self.stream.flush().unwrap();
}
まとめ
• HTTP サーバーのしくみがわかった!
• 細かいことを考えなければ,結構簡単につくれる!
• Rust のいい練習になる!
• 解説しきれなかった部分は
https://github.com/yuk1ty/simple-http-server をご覧くださ
い.

Rust で簡易 HTTP サーバーを作ってみよう