mod_rewriteで何でも出来ると書かれてるけど
mod_rewriteはスイスアーミーナイフ、mod_rewriteは黒魔術、mod_rewriteで何でもできる。と書かれてるけど、実際のところコピペが多くて仕組みがよく分かんないです。
Rewiteリクエスト処理へなんでもアリと呼称されますが、思い通りになりません。悲しい。だから調べてます。
前回までで
RewriteRuleの処理、RewriteCondの書き方、組み合わせて複数書くところまで調べた。今回は複数記述を省略できるRewriteMapを調べた。
RewriteMapを使ってみる。
RewriteMapはRewriteRuleやRewriteCondをたくさん書かずに済む、設定集。RewriteMapに記述すれば、RewriteRuleを連発しなくて済むようになってます。
RewriteMapの下準備
RewriteMapを使う前に、実験環境を整えておきます。カンタンなRewriteRuleを書いておきます。
ディレクトリ構造はこんな感じ
takuya@~/Sites/rewrites$ tree . -a . ├── ex1 │ ├── .htaccess │ ├── data.txt │ └── index.php └── get.php
.htaccess ファイルに次のように記述
RewriteRule (.*) ../get.php?from=ex1&map=$1
RewriteRuleによる書換が行われることを確認。
takuya@~/Sites/rewrites/ex1$ curl http://localhost/~takuya/rewrites/ex1/Foooooo array(2) { ["from"]=> string(3) "ex1" ["map"]=> string(7) "Foooooo" }
リクエストの末尾のディレクトリ(ファイル名)が書き換えられ、URLに埋め込まれるカンタンなものです。
これをベースにRewriteMapを使おうと思います。
最初の書換の目標
最初にやってみるRewriteMapによる書換は、URLのファイル名が来たら、それを*書換えてクエリ文字列に埋め込みたいと思います。
http://localhost/~takuya/rewrites/ex1/Foooooo ↓ http://localhost/~takuya/rewrites/get.php?from=ex1&map=Mapで置換済み文字列
RewriteMap の準備
次のようなハッシュマップを用意します、ファイル名に指定したらRewriteMapがURLに書き換え処理を走らせたい。
とりあえず、サイト名を入れたら、ドメインを返すように設定を書くようにする。
Mapによる書換文字列
yahoo www.yahoo.co.jp mixi mixi.jp google www.google.com apple www.apple.com twitter t.co
このファイルをRewriteMapとして登録する。
RewriteMap は .htaccessでは動作しません。それは、起動時に読み込まれ展開されるからです。
そこで、/etc/apache2/users/takuya.conf 、次の記述を追加しました。
/etc/apache2/users/takuya.conf
RewriteMap titles "txt:/Users/takuya/Sites/rewrites/ex1/data.txt" <Directory "/Users/takuya/Sites/rewrites/ex1" > RewriteEngine On </Directory>
もし動的に指定出来たら、RewriteMap指定したファイルが存在しない時に、エラーになっちゃいますし。 動的に出来るならセキュリティ・ホールに・・・存在しないファイル読みに行くのは怖いですね。また侵入者が.htaccess書き換えてゴニョゴニョできちゃうもんね。。(個人的には動的Mapでもイイと思うけど。
RewriteRuleでRewriteMapを使うように設定する。
RewriteRule (.*) ../get.php?from=ex3&map=${titles:$1|no_match}
書換処理を確認
RewriteRuleを書きました。つぎにリクエストを実行してみます。
takuya@~/Sites/rewrites/ex1$ curl http://localhost/~takuya/rewrites/ex1/auction array(2) { ["from"]=> string(3) "ex3" ["map"]=> string(8) "no_match" } takuya@~/Sites/rewrites/ex1$ curl http://localhost/~takuya/rewrites/ex1/mixi array(2) { ["from"]=> string(3) "ex3" ["map"]=> string(7) "mixi.jp" }
無事にMapの定義通りハッシュマップを通して書き換わっているのがわかります。
RewriteMapの記述方法
わかりやすく日本語で表記すると、次のようになりました。
RewriteMap 名前 "タイプ:ファイル名"
Rewrite の タイプにはいくつかある。
タイプ | 説明 |
---|---|
txt | スペース区切りの一覧表 |
rnd | txt で値が複数指定されたものがランダムで選択 |
prg | 独自のプログラムを使う |
dbm | SDBM/GDBM などが使える |
dbd | SQL でデータベースにアクセス |
RewriteMapは関数
RewriteMapは関数だと解釈すれば理解が容易だと思います。
RewriteMap titles "type:source"
これを関数と考えておく。
var titles = function (type, source){..}
ということで、RewriteMapとRewriteRuleを組み合わせると
関数の定義と、
RewriteMap titles "txt:/Users/takuya/Sites/rewrites/ex1/data.txt" RewriteRule (.*) ../get.php?from=ex3&map=${titles:$1|no_match}
RewriteMap type TXT の動作イメージを関数で表現してみた。
//RwriteMap var titile = (function ( source ) var data = File.load(source); return function( key ){ return data[key] || "" })( "/Users/takuya/Sites/rewrites/ex1/data.txt" ) //RewriteRule if ( REQUEST_URI.match(//)) ){ REQUEST_URI.replace( "./get.php?from=ex3&map=" + title( match[1] || "default_value" ) ) }
プログラムで表現しても絶対同一にはならないので、そのへんはご容赦下さい。理解の助けのためのイメージ図描くよりプログラムで表現しほうが、私は理解しやすかったです。
RewriteMapでRandomもやってみる
rnd を指定したら、ランダムに結果を返してくれるので、ランダムの結果を表示するようにしてみる。
httpd.conf
RewriteMap titles "rnd:/Users/takuya/Sites/rewrites/ex1/data.txt" <Directory "/Users/takuya/Sites/rewrites/ex1" > RewriteEngine On </Directory>
.htaccess
RewriteRule (.*) ../get.php?from=ex3&map=${titles:$1|no_match}
書換データベースを用意する
takuya@~/Sites/rewrites/ex1$ cat data.txt じゃんけん グー|チョキ|パー
リクエストを送ってみる。
takuya@~/Sites/rewrites/ex1$ curl http://localhost/~takuya/rewrites/ex1/グー array(2) { ["from"]=> string(3) "ex3" ["map"]=> string(6) "パー" } takuya@~/Sites/rewrites/ex1$ curl http://localhost/~takuya/rewrites/ex1/パー array(2) { ["from"]=> string(3) "ex3" ["map"]=> string(9) "チョキ" }
PHPを書かなくても設定だけでランダム動かせるとかテンション上がりますね。
RewriteMapランダム何に使うの。。。
何に使うといえば、、、うーん。負荷分散とかに使えそう。
プロキシと組み合わせてランダム動かせば、負荷分散に役立ちそう。まぁ通常はDNSラウンドロビンをするだけろうけど。。。
RewriteMapでプログラムを使う。
RewriteMapがプログラムを定義できるので、プログラムでRewriteCondで使ってみる。
RewriteMapが単なる関数だと考えたら、プログラムを呼び出す関数を作れば自由にできる。それがprg。
httpd.conf
RewriteMap sample1 "prg:/Users/takuya/Sites/rewrites/ex1/sample1Map.rb" <Directory "/Users/takuya/Sites/rewrites/ex1" > RewriteEngine On </Directory>
.htaccess
RewriteRule (.*) ../get.php?from=ex3&map=${sample1:$1|no_match}
書換の関数。
ここがメイン部分。RewriteMapにより呼び出される側のプログラム
#!/usr/bin/env ruby require 'logger' log = Logger.new("/tmp/ruby_rewrite_map.log") $stdin.sync = $stdout.sync = true loop{ line = $stdin.gets #ここでブロックされる。Blocking IOになる。 line.force_encoding("utf-8") # 日本語URLも来るのでエンコード強制 line = line.strip # 改行消す log.info( "READ #{line}" ) if( line.match(/免許/i ) ) $stdout.puts "早く免許取るんだ" else $stdout.puts "あ、なんでもない" end }
RewriteMapから呼び出されるプログラムは、STDOUT/STDINを使ってデータの受け渡しをする。
一行書き込まれたら、一回のリクエスト。ソレに応答して一行書いたら終わり。
IOのREAD待ちでブロッキングされるので、無限ループで処理して問題ない。
プログラムで書換テスト
リクエストを送ったら返してくれるのを確認する。
takuya@~/Sites/rewrites/ex1$ curl http://localhost/~takuya/rewrites/ex1/免許 array(2) { ["from"]=> string(3) "ex3" ["map"]=> string(24) "早く免許取るんだ" } takuya@~/Sites/rewrites/ex1$ curl http://localhost/~takuya/rewrites/ex1/コーヒ array(2) { ["from"]=> string(3) "ex3" ["map"]=> string(24) "あ、なんでもない" }
RewriteCondでRewriteMapを使う。
RewriteMapで定義したマッピングは、RuleでもCondでもどちらでも使える。
たとえば、特定のヘッダをチェックしてようと思います。
Cookieチェックしてアクセス禁止
Cookieの値をチェックしてアクセス禁止して見るサンプルを書いてみようと思う。
httpd.conf
RewriteMap sample2 "prg:/Users/takuya/Sites/rewrites/ex1/cookie_check.rb" <Directory "/Users/takuya/Sites/rewrites/ex1" > RewriteEngine On </Directory>
.htaccess
RewriteCond ${sample2:%{HTTP_COOKIE}|none} !^OK$ RewriteRule ^ - [F]
書換プログラム
#!/usr/bin/env ruby require 'logger' require 'cgi' $stdin.sync = $stdout.sync = true log = Logger.new("/tmp/ruby_rewrite_map.log") loop{ line = $stdin.gets #ここでブロックされる line.force_encoding("utf-8") line = line.strip log.info( "READ #{line}" ) params = CGI::parse line log.info( "params #{params}" ) if( params.has_key? "login" ) log.info("write OK") $stdout.puts "OK" else log.info("write NG") $stdout.puts "NG" end }
リクエストしてみる。
Cookie無しでアクセスしてみた結果。
takuya@~/Desktop$ curl -I http://localhost/~takuya/rewrites/ex1/index.php HTTP/1.1 403 Forbidden
Cookieがないのでアクセスを制限された!
Cookieつけてアクセスをした結果。
takuya@~/Desktop$ curl -I -b "login=1" http://localhost/~takuya/rewrites/ex1/index.php HTTP/1.1 200 OK
アクセスできる
RewriteMap で起動したプログラムについて
RewriteMapで起動したプログラムは、プロセスとしてずっと起動しっぱなしになっています。
IO待ちでBlockされるので当然なのですが。次のようにプロセスとして常駐しています。
takuya@~/Desktop$ pstree 45145 -+= 45145 root /usr/sbin/httpd -D FOREGROUND |--- 45728 root ruby /Users/takuya/Sites/rewrites/ex1/sample1Map.rb |--- 45729 root ruby /Users/takuya/Sites/rewrites/ex1/cookie_check.rb |--- 45730 _www /usr/sbin/httpd -D FOREGROUND \--- 46225 _www /usr/sbin/httpd -D FOREGROUND
さらに、プロセスとして常駐しているので、なにかエラーが有って強制終了すると面倒なことになります。。。応答がなくなるなど。
今回は取扱しなかったMapタイプ
dbm は sdbm がよくわからなかったのでパスした。httxt2dbm で作って簡単にできる。ただしdbm ファイルをPython・Rubyから取り扱う方法がわからなかったのでパス。
dbd はデータベースにアクセスで、mod_authのときに、dbd 扱ったので今回はパス
基本的な使い方はわかった。
ここまでで、基本的な使い方はわかった。
RewriteRuleでURLにマッチ、
RewriteCondで条件を補完、
RewriteCondで条件を組み合わせる
RewriteMapで条件記述をシンプルに柔軟に出来る。
とわかった。
あとは、よく使うようなRewriteのオプションを書きながら、色々考えてみたいと思う。
参考資料
先人に感謝
- http://serverfault.com/questions/125579/how-to-secure-a-directory-in-apache-using-a-php-session
- http://blog.usoinfo.info/article/290447505.html
- http://d.hatena.ne.jp/a666666/20080421/1208743268
- http://blog.koshigoe.jp/archives/2006/11/mod_rewriterewr.html
- http://blog.livedoor.jp/techblog/archives/65219198.html
- http://koseki.hatenablog.com/entry/20090611/ModRewrite ←スゴくよく分かった!
- http://simonecarletti.com/blog/2009/01/apache-query-string-redirects/
- http://pontago.hatenablog.com/entry/20091002/1254485563 ←すごく参考になった
- http://tm.root-n.com/server:apache:module:rewrite
- http://piro791.blog.so-net.ne.jp/2015-03-31
- http://ameblo.jp/gonta3333/entry-10360324799.html