h2oでmrubyを使ってリダイレクト
概要
h2oを使っているときにリダイレクト処理を入れたい場合、PATHベースであれば下記のようにシンプルに記載が出来ます。
https://h2o.examp1e.net/configure/redirect_directives.html
redirect:
url: https://inamuu.com/
status: 301
しかし、クエリストリングを使っている場合に下記のように記述してもリダイレクトされません。
redirect:
url: https://inamuu.com/?p=100
status: 301
そこでmrubyを使うことでクエリストリングが含まれている場合の書き方になります。
https://h2o.examp1e.net/configure/mruby.html
前提
h2oをmrubyで使うのにあたり、幾つか仕様として最低限覚えておかないといけないことがあります。
1つは、戻り値は配列にする必要があること。
下記のようなコードを書いたとします。
Proc.new do |env|
if /\.(html|css)\z/.match(env["PATH_INFO"])
p 'test'
end
end
そうすると、エラーログにarrayじゃなかったよって出力されます。
配列は下記のように ステータスコード
, ヘッダー
, body
にしてあげる必要があります。
if /\.(html|css)\z/.match(env["PATH_INFO"])
[200, { 'content-type' => 'text/plain' }, ['hoge']]
end
end
次に、条件にマッチしなかったら、yamlの処理、つまり次に処理を進めたい場合はステータスコードを399で返す必要がある点です。
上記のコードは、/hoge/hoge.html
は問題ありませんが、 /hoge
は Internal Server Error
となります。
そこでマッチしなかったら、ステータス399を返すようにしてあげればその後の /var/www/html
が参照されます。
if /\.(html|css)\z/.match(env["PATH_INFO"])
[200, { 'content-type' => 'text/plain'}, ['hoge']]
else
[399, {}, []]
end
end
file.dir: /var/www/html
各クライアントからのリクエスト時に得られた値は、envハッシュに格納されています。
これらを参照することで、リクエスト時に色々な処理をしてあげることが出来るようになります。
{"REQUEST_METHOD"=>"GET", "SCRIPT_NAME"=>"/hoge", "PATH_INFO"=>"/", "QUERY_STRING"=>"p=100", "SERVER_NAME"=>"wiki.kazuma.tokyo", "SERVER_PROTOCOL"=>"HTTP/2", "SERVER_ADDR"=>"XXX.XXX.XXX.XXX", "SERVER_PORT"=>"443", "HTTP_HOST"=>"wiki.kazuma.tokyo", "REMOTE_ADDR"=>"XXX.XXX.XXX.XXX", "REMOTE_PORT"=>"56716", "HTTP_ACCEPT"=>"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "HTTP_COOKIE"=>"_ga=GAXXXXX; _gid=GAXXXX; _ga=GAXXXX; _gid=GAXXXX; XSRF-TOKEN=XXXX; laravel_session=XXXX", "HTTP_PRAGMA"=>"no-cache", "HTTP_USER_AGENT"=>"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36", "HTTP_AUTHORIZATION"=>"Basic XXXXX", "HTTP_CACHE_CONTROL"=>"no-cache", "HTTP_SEC_FETCH_DEST"=>"document", "HTTP_SEC_FETCH_MODE"=>"navigate", "HTTP_SEC_FETCH_SITE"=>"none", "HTTP_SEC_FETCH_USER"=>"?1", "HTTP_ACCEPT_ENCODING"=>"gzip, deflate, br", "HTTP_ACCEPT_LANGUAGE"=>"ja,en-US;q=0.9,en;q=0.8", "HTTP_UPGRADE_INSECURE_REQUESTS"=>"1", "rack.url_scheme"=>"https", "rack.multithread"=>false, "rack.multiprocess"=>true, "rack.run_once"=>false, "rack.hijack?"=>false, "rack.errors"=>#<IO:0x1c51f08>, "SERVER_SOFTWARE"=>"h2o/2.2.6"}
クエリストリングを評価してリダイレクト
env["QUERY_STRING"]
を正規表現で評価して、マッチしたらLocationヘッダーを返すことで、リダイレクトされるようになります。
if /(p\=14|p\=32|p\=37|p\=39)/.match(env["QUERY_STRING"])
[301, { 'Location' => "https://#{env['HTTP_HOST']}/test/?#{env['QUERY_STRING']}"}, []]
else
[399, {}, []]
end
end
自作のファイルを指定する
上記のように少なければ直接confに書いても良いとは思いますが、mrubyのファイルだけ別にしたい場合は下記のようにします。
mruby.handler-file: /etc/h2o/mruby/redirect.rb
file.dir: /var/www/html
ちなみに、過去には handler_path:
というディレクティブが使われていたようで、それにしたところ下記のようにエラーが出てきました。
そして、PRのリンクを参照すると、mruby.handler-file
に変わったことがわかります。
まとめ
前提条件をスッとばして書き始めると動きませんが、理解できると大変便利です。
個人的にはmruby.handler_pathのエラーにGitHubのリンクがあったことが大変感動的で(たまに特定のソフトウェアのエラーログにリンクがあったりすることがありますが)、今まで特にh2oは使ってこなかったのですがmrubyを使うことで色々処理していけることがわかったので、これからは積極的に採用してみようと思います。