フィヨルドブートキャンプのライトニングトーク会で Rack について話した

otomi 2021-04-27

フィヨルドブートキャンプのライトニングトーク会に先日参加し、Rack について話したのでスライドと学んで得た内容を書く。

最初にライトニングトーク会へ参加するというのだけ決めて、テーマは後から考えた。

Ruby の gem である OmniAuth を使っているときにミドルウェアの概念に出会ったので、よくわかっていないミドルウェアのことを学ぶいい機会だなと思ったので発表テーマに選んで、Rack を触ってみた。

発表したスライド

https://speakerdeck.com/ot0m1/rack-karajian-rumidoruueafalseshi-jie

発表した内容

Rack から見るミドルウェアの世界というタイトルで、ミドルウェアってどんなものか、Rack とはなんぞや、Rack を触ってみてどうかをお話します。

なぜ Rack を触ろうと思ったか

「omniauth を使って GitHub 認証を実装する」のプラクティスをやっているときに読んだパーフェクト Ruby on Rails に、 OmniAuthはRackミドルウェアとして動作する、という記述があったためです。
ミドルウェアとは?アクセスを受け取るとは?どんな動きをしているのかというところを調べたかったのが動機になります。

Rack とは

自分で調べたところ、WEB サーバと WEB アプリケーション/フレームワーク間のインターフェースの役割を果たすライブラリ、とのことでした

なぜ WEB サーバと WEB アプリケーション/フレームワーク間のインターフェースの役割を果たすライブラリがあるとうれしいのか、なぜ Rack ができたのか

参考にした URL に次のような経緯が書かれてありました。

  • WSGI という、Python のための Web サーバと Web アプリケーション/フレームワーク間の標準インターフェースを定める仕様があり、Rack はこの WSGI に影響されて開発された
  • 各フレームワークの実装は特定の Web サーバに依存していることが多く,使用したいフレームワークの為に環境を制限されるということがあったため、WSGI という標準インターフェイスを定める仕様が提唱された
  • 様々な Web サーバやフレームワークが開発されても,双方が Rack を使用してインターフェース部分を実装していさえすれば、サーバとアプリケーションの組み合わせを気にしなくてよくなる

という経緯とうれしい理由があるようです。

フィヨルドブートキャンプのプラクティスでも出てくる Sinatora や Ruby on Rails でもこの Rack は使われているようです。

というわけでさっそく触ってみます。

まずは Rack でアプリケーションを作ってみて、インターフェイスに触れてみます。
Rack のアプリケーションとしてはスライドに書かれている最低限 3つのことを満たせばよいそうです。

はいできました。

https://github.com/ot0m1/sibatora

GET リクエストでフォームを描画して、POST でフォームに書かれた内容を受け取って表示するというものです。

shibatora という名称は Sinatra から来ています。
当初 Sinatra をモチーフにルーティングができるウェブアプリケーションフレームワークを作ろうとしていたんですが、ミドルウェアの動きを見たいのが今回の目的なので、あんまり複雑なことをやっても本題からずれるなと思って上記のような簡単な挙動のアプリケーションにしました。その時の名残で名前だけ引き継いでいます。

Rack が色々やってくれるので Web サーバのことを書かなくても、rackup すれば自動でサーバを起動して、ブラウザでアクセスできるようにしてくれます。
Sinatra や Ruby on Rails を起動させるのと同じ仕様感でした。

ここで確かに Sinatra や Ruby on Rails も Rack が使われており、アプリケーションはサーバのことを意識しなくてよいということなのかなということを感じました。

さらに Rack でミドルウェア的なものも作ってみる

できました。

冒頭でお話した OmniAuth と同じように、サーバとアプリケーションの中間に入って振る舞ってもらうようなものを作ります。

というわけで、ウェブアプリケーションフレームじゃないほうの WAF(ウェブアプリケーションファイアウォール)を作ってみました。 shibatora_waf.rb というファイルを追加しています。さっきのアプリケーションのフォームに URL が書き込まれたら警告文とステータスコード 403 を返すようにようにするというものです。

サーバーが動いているので curl がたたけるため、このようにコマンドラインでも確認できます。

-> %  curl http://127.0.0.1:9292/ -X POST -d 'hello' -i
HTTP/1.1 200 OK
Content-Type: text/html;charset=utf-8
Content-Length: 31

<html><body>hello</body></html>%

-> % curl http://127.0.0.1:9292/ -X POST -d 'https://example.com' -i
HTTP/1.1 403 Forbidden
Content-Type: text/html;charset=utf-8
Content-Length: 68

<html><body>URLを含む文章は投稿できません</body></html>%

このように、ミドルウェアとして動いていることが確認できます。 shibatora_waf.rb がミドルウェアとして振る舞ってくれ、サーバとアプリケーションの間の通信に割り込んでいるということがわかります。

コードを書く側は、アプリケーション層だけを意識しておけばよく、その下の層において Rack がいい感じに HTTP のレスポンスを操作してくれています。

というわけで冒頭の OmniAuth の振る舞いをなんとなくイメージできました。