[mod_mruby] リバースプロキシしたコンテンツを更にフィルタで操作する


これは [mod_mruby ngx_mruby Advent Calendar 2014](http://qiita.com/advent-calendar/2014/mod-ngx-mruby) の１８日目(12/18)の投稿です。

この投稿の位置づけとしては、アドベントカレンダーの１６日目に書いた記事（[静的リソースをリバースプロキシで配信する](http://qiita.com/hkusu/items/1b8e1f95faa8e62a98d9)）の応用編です。

Apache では、リバースプロキシしたコンテンツに対して、更に出力フィルタをかけて処理することが出来ます。（有名な出力フィルタだと mod_deflate など。）

イメージは次のとおり。

![スクリーンショット 2014-12-16 22.10.17.png](https://qiita-image-store.s3.amazonaws.com/0/35733/5c0795d5-7cd6-1ea8-838a-77f2efcd2980.png "スクリーンショット 2014-12-16 22.10.17.png")

> Apache のフィルタについての説明はこちら。ちなみにコンテンツハンドラ(例：PHP、Perl)の前に起動されるのが入力フィルタで、後に起動されるのが出力フィルタです。
> ⇒ http://httpd.apache.org/docs/current/ja/filter.html

もちろん mod_mruby でもフィルタを作成できますので、同様にリバースプロキシしたコンテンツに対して任意のフィルタ処理をすることが可能です。

mod_mruby でのプロキシの設定については１６日目に書いたので、今回はWEBサービスにおける通信の削減を目的としたフィルタの設定を２例、紹介します。

> 前提となる環境はアドベントカレンダーの３日目、Apache のディレクティブ設定や mod_mruby スクリプトの例は１０日目に書いたので、宜しければそちらも参照ください。
> ⇒ [mod_mruby を Amazon EC2、Apache2.4 へ導入する](http://qiita.com/hkusu/items/ca8622c83b0bfc2cac72)（Apache のバージョンは `2.4.10`、mod_mruby のバージョンは `1.9.7` です）
> ⇒ [mod_mruby の可能性とディレクティブ設定/スクリプトの例](http://qiita.com/hkusu/items/dce6ab77a418bb26765c)

## ① リバースプロキシしたコンテンツのHTTPレスポンスヘッダを操作

リバースプロキシ先から返ってきた画像ファイルに対し、mod_mruby で `Cache-Control` ヘッダを操作して、ブラウザにキャッシュさせる時間を任意に指定する例です。

```ruby:/usr/local/apache2/mruby/respose_header.rb
r = Apache::Request.new

if /\.(gif|jpe?g|png)$/ =~ r.filename
  r.headers_out['Cache-Control'] = 'max-age=20'
end
```

この例だと 20秒だけブラウザにキャッシュさせます。上記のスクリプトを出力フィルタとしてフックします。

```apacheconf:***.conf
SetOutputFilter mruby
mrubyOutputFilter /usr/local/apache2/mruby/respose_header.rb
```

するとブラウザへのHTTPレスポンスヘッダは次のようになります。
（リバースプロキシ先は GitHub Pages の例です。）

![スクリーンショット_2014-12-16_15_05_22.png](https://qiita-image-store.s3.amazonaws.com/0/35733/a0927d39-7543-d641-af1f-f25e61e29993.png "スクリーンショット_2014-12-16_15_05_22.png")

> フィルタのディレクティブ設定については公式ページをご覧ください。
> ⇒ http://httpd.apache.org/docs/current/ja/filter.html
>
> `<Files>` 等のディレクティブと組み合わせたり、`.htaccess` に書いたりもできます。
>
> また上記の例ではスクリプト内で拡張子を判定していますが、拡張子をキーに出力フィルタを定義することも出来ると思います。

ただ出力フィルタのフェーズで Apache の HTTPリクエストやヘッダにアクセスするのは行儀が悪いかも^^; まあこういう設定もできるよということで..

## ② リバースプロキシしたコンテンツを gzip 圧縮して配信

静的コンテンツを gzip 圧縮して、ネットワーク帯域を節約します。gzip 圧縮は [mod_deflate](http://httpd.apache.org/docs/2.4/filter.html) を利用します。ちなみに mod_deflate が何をしてくれるかというと、ブラウザが gzip 圧縮に対応している場合は次のようなヘッダを送ってくるので、

![スクリーンショット_2014-12-16_14_58_25.png](https://qiita-image-store.s3.amazonaws.com/0/35733/1a50b3c8-69da-6b74-9a62-3e0cda9e6bb1.png "スクリーンショット_2014-12-16_14_58_25.png")

mod_deflate はコンテンツを圧縮して送出し、ヘッダは次のように返します。ブラウザはこれで圧縮されたコンテンツだと判断し解凍します。

![スクリーンショット_2014-12-16_14_58_42.png](https://qiita-image-store.s3.amazonaws.com/0/35733/c4a8e246-6e97-b205-d1fd-bd353f1ddfe2.png "スクリーンショット_2014-12-16_14_58_42.png")

> もし mod_deflate がサーバに入ってない（mod_deflate.so が存在しない）場合は、次のように configure し直してから Apache を再コンパイルすると導入できます。
> `$ ./configure --enable-deflate`

設定方法については、まず `.conf` ファイルにて mod_deflate を有効にします。

```apacheconf:***.conf
LoadModule deflate_module modules/mod_deflate.so
```

例として PNG 形式の画像ファイルを gzip 圧縮する場合は次のようにします。

```apacheconf:***.conf
AddOutputFilterByType DEFLATE image/png
```

するとブラウザへのHTTPレスポンスヘッダは次のようになります。
（これもリバースプロキシ先は GitHub Pages の例です。）

![スクリーンショット_2014-12-16_15_03_19.png](https://qiita-image-store.s3.amazonaws.com/0/35733/47954144-814f-31bb-f652-cbe6fbd066f8.png "スクリーンショット_2014-12-16_15_03_19.png")

ちなみにフィルタは複数を併用できるので、例えば上記の①②を同時に設定することもできます。

![スクリーンショット_2014-12-16_15_06_46.png](https://qiita-image-store.s3.amazonaws.com/0/35733/4925c259-d55a-ccfb-620b-0b79c8c4319c.png "スクリーンショット_2014-12-16_15_06_46.png")

## おわりに

今回は mod_mruby でリーバスプロキシしたコンテンツに対して、更に通信の削減を目的としたフィルタを適用する例を紹介しました。

これ以外にも、フィルタで色々できそうです。mod_mruby では最も簡単なフィルタは次のように書けます。（何もしないコード。）下記の例だと、変数 `content` には HTTPレスポンスのコンテンツ内容(HTML、JavaScript、CSS、画像バイナリ、JSON、etc..)が格納されているので、ここで加工が出来ちゃいます。

> ただ大きいコンテンツはメモリに注意と思われ。

```ruby:フィルタのコード例
f = Apache::Filter.new

content = f.flatten
f.cleanup

f.insert_tail content
f.insert_eos
```

またの機会に、スマホ用の画像変換をしてみようかなと^^

明日(12/19)は [inokappa](http://qiita.com/inokappa) さんによる記事です！よろしくお願いします。