ようへいの日々精進XP

よかろうもん

mod_mruby でユーザーエージェントを判断してアクセス先を振り分けてみる

はじめに

  • mod_mruby を動かせる環境が作れたのでユーザーエージェントを判断してアクセス先を振り分けてみる

参考


SmartPhone とそれ以外のアクセスを分けたい

mod_rewrite を使う場合

以前に取り上げたが mod_rewrite を使って SmartPhone からのアクセスとそれ以外のアクセスを分ける場合には以下のように書ける。

SetEnvIf User-Agent "iPhone" UA=smartphone-apple
SetEnvIf User-Agent "iPod" UA=smartphone-apple
SetEnvIf User-Agent "iPad" UA=smartphone-apple
SetEnvIf User-Agent "Android" UA=smartphone-google
RewriteCond %{ENV:UA} ^smartphone-apple$
RewriteRule ^/(path/to)(.*$) http://iPhoneのあれや/これや/$2 [R,L]
RewriteCond %{ENV:UA} ^smartphone-google$
RewriteRule ^/(path/to)(.*$) http://Androidのあれや/これや/$2 [R,L]
RewriteCond %{ENV:UA} !^smartphone*$
RewriteRule ^/(path/to)(.*$) http://それ以外のあれや/これや/$2 [R,L]

その他にも書き方があるが SetEnvIf を使うのが最近の個人的な好み(他の書き方と差異があるのかは未検証)。ユーザーエージェントを判断してグルーピングして RewriteCond とマッチさせて RewriteRule で振り分けるって感じかな。

mod_mruby での実装

ここからが本題。mod_mruby でユーザーエージェントを判断してリライトする場合には以下のように書いてみた。上記の内容を完全に実現するには引き続き調べなければいけないけどとりあえず。

#
hi = Apache::Request.new.headers_in
agent = hi["User-Agent"]
#
def go_redirect(redirect_path)
  r = Apache::Request.new()
  s = Apache::Server.new()
  r.filename = s.document_root + redirect_path
  Apache.return(Apache::OK)
end
#
if agent.include?("Mac") then
  go_redirect("/iphone.html")
elsif agent.include?("Android") then
  go_redirect("/android.html")
else
  Apache.return(Apache::DECLINED)
end

ユーザーエージェントは Apache::Request.new.headers_in にハッシュで格納されているのでキー(User-Agent)を指定して取り出すことが出来た。但し、ここからがちょっと悩んだ...

if /iPhone|iPad|iPod/ =~ agent
  redirect_path   = "/iphone.html"
  r.filename = s.document_root + redirect_path

上記のように取得したユーザーエージェントが格納されている変数内に iPhoneiPad さらに iPhone の何れかが含まれている場合にはという書き方が出来れば嬉しかったのだが、今回は下記のようなエラーが出てしまい動かすことが出来なかった。

[Fri Feb 28 20:34:04 2014] [error] mod_mruby ERROR ap_mrb_raise_error: mrb_run failed: [TYPE: FILE] [CACHED: DISABLE] mruby raise: /etc/apache2/rewrite.rb:5: '%s' is an invalid regular expression because %s. (ArgumentError)

mod_mrubyコンパイルする際の build_config.rb 内に以下のように mruby-hs-regexp という正規表現を扱う為の mrbgems も一緒にビルドされるように書かれているようなので動作するはずなのだが...

conf.gem :git => 'git://github.com/masamitsu-murase/mruby-hs-regexp.git'

上記についてはインストール時の自分の手違いである可能性も高い為、もう少し調べて issue で質問出来たらと考えている。


とりあえずデモ

mruby を実行するタイミングを指定

/etc/apache2/mods-enables/ 以下に mruby.conf というファイル名で mruby を実行するタイミング(Hook Function)を定義する。

mrubyTranslateNameMiddle /etc/apache2/hooks/rewrite.rb

振り分けの mruby スクリプトを設置

上記で指定したパス(/etc/apache2/hooks/rewrite.rb)にスクリプトを設置する。設置したら念の為 apache を再起動する。再起動後、スクリプトの修正を行った場合には再起動は不要なようだ。

テスト用の HTML ファイルの設置

ドキュメントルート以下に以下のようなファイルを設置する。

# tree /var/www
/var/www
|-- android.html # ユーザーエージェントが Android の場合に表示される HTML
|-- index.html    # ユーザーエージェントが iPhone 等場合に表示される HTML
`-- iphone.html # 上記以外のユーザーエージェントの場合

0 directories, 3 files

HTML ファイルには以下のようにユーザーエージェントを表示する Javascript も埋め込んでおく。

<SCRIPT Language="JavaScript">
<!--
document.write(navigator.userAgent);
// -->
</SCRIPT>

iPhone のユーザーエージェントでアクセスしてみる

f:id:inokara:20140301092021p:plain

iPhone 以外にも iPodiPad もアクセスされることを想定してユーザーエージェントを判断する文字列として「Mac」を指定。*1

Android のユーザーエージェントでアクセスしてみる

f:id:inokara:20140301092215p:plain

上記以外のユーザーエージェントでアクセスしてみる

f:id:inokara:20140301092224p:plain

現時点で条件の指定に正規表現を利用出来ないのがちょっと辛いが上記のように振り分けを行うことが出来た。


最後に

  • mod_mruby を使ってお手軽もユーザーエージェントを判断して振り分けの設定を行うことが出来た
  • 振り分けの条件が複雑になるにつれて通常の mod_rewrite よりも可読性が良くなるのが個人的にはお気に入り
  • 但し、検証した環境では正規表現での条件判定が出来なかったので引き続き調査したい
  • これらの環境がとりあえず試せるであろう Dockerfile も作ってアップした

*1:MacBook Pro とかはどうすんねん...ということがあるので引き続き検討...