2010年11月30日火曜日

apacheの.confのRewriteRuleを通すとなぜか%3とかが消えてしまう

ちょっとタイトルの意味が分からない
結局はとても初歩なんですが…。
どういうコトかというと。
apacheの設定ファイル(http.confなど)のRewriteRuleディレクティブにて、以下のような記述があったとします(URLは全部アテです)。
RewriteCond %{HTTP_USER_AGENT} IE的な何か
RewriteRule ^/(.*)$ http://example.com/redirect/http%3A//example.co.jp/ [R=302,L]

「IE的な何か」のユーザーエージェントでの全てのリクエストを「http://example.com/redirect/http%3A//example.co.jp/」にhttp status code 302でリダイレクトさせる、という記述です(のはず)。
で、最終的には 内部ロジックで「%3A」が「:」に解釈されて、http://example.co.jp/ にリダイレクトさせたいはずです。URL的には。

キモはhttp%3A//example.co.jp/となっている部分です。
http://example.com/redirect/ のエントリポイントの仕様により、「:」が「%3A」にURLエンコードされているところです。

実際に「IE的な何か」のUAでリクエストを実行すると、
  1. http://example.com/redirect/httpA//example.co.jp/みたいな形でリダイレクトされる
  2. httpA//example.co.jp/にリダイレクト
  3. 「httpA」だと、なんだそれ?とブラウザに怒られる

となってしまいました。
1.の時点で「%3」が消えてる!

なぜか
「%3」や「%1」がRewriteRuleで使われる特殊な記法だからです。
勉強不足なんで曖昧ですが、RewriteCondの後方参照として、「%n」このような記法があります。
mod_rewriteで動的ページを静的ページっぽいURLにする(Freak)にてこの辺りの解説がされています。
RewriteRuleで「%n」という文字列は避けた方がよさそうです。

「%」をエスケープしたらいいんじゃね?
「%n」が使えないなら「%」をエスケープしたら、「%n」の「%」は正しく認識されるのでは?
と思ってapacheの設定ファイルを変えてみます。
RewriteCond %{HTTP_USER_AGENT} IE的な何か
RewriteRule ^/(.*)$ http://example.com/redirect/http\%3A//example.co.jp/ [R=302,L]


で、再度「IE的な何か」のUAでリクエストを実行すると、
  1. http://example.com/redirect/http%253A//example.co.jp/みたいな形でリダイレクトされる
  2. http%253A//example.co.jp/にリダイレクト
  3. 「http%253A」だと、なんだそれ?とブラウザに怒られる

となってしまいました。
「%」がエスケープされて、「\%3」が「%253」になってる!

ではどうすればよいのか
RewriteRuleの[NE]フラグを使用するです。
[NE]フラグとは「No Escape」=「エスケープしない」。
「%」を「\%」とエスケープし、かつ[NE]でエスケープしない、とすればよいのです。

最終的には以下の形になります。
RewriteCond %{HTTP_USER_AGENT} IE的な何か
RewriteRule ^/(.*)$ http://example.com/redirect/http\%3A//example.co.jp/ [R=302,L,NE]


こうすると、「IE的な何か」のUAでリクエストを実行すると、
  1. http://example.com/redirect/http%3A//example.co.jp/みたいな形でリダイレクトされる
  2. http//example.co.jp/にリダイレクト
  3. 完了

JavaScriptが世界制覇しそうな予感

なぜかというと
米Yahoo!で議論された、フロントエンドエンジニアリングの将来(前編)という記事を発見しまして。
サーバサイドのJavaScriptという衝撃的な「node.js」に言及しています。
最近チラホラ聞きます(もっとも、本当に「サーバサイドのJavaScript」程度しか知らないのですが。。)

これはJavaScriptをもっと勉強しておいた方がよさそう。。

2010年11月15日月曜日

PHPで、1つのリクエストに対するcurlとcurl_multiとのパフォーマンスの差

複数リクエストの場合は
勿論、並列にリクエストを実行するcurl_multiの方が早いでしょう。
curl_multiを使っての実装方法はAPIとの通信効率をよくする実装例(1) curl_multi (Yahoo!JapanのTechBlog)に詳しく載っています。

じゃあ、リクエストが1つだけの場合は?
勿論、無駄な処理のないただのcURLの方が早いでしょう。
それは分かるけどどれくらいの差があるのでしょうか?
実験してみます。
PHPは5.2.5です。

ただのcURL場合
以下のプログラム(抜粋ですが)を10回試行して平均を取ってみます。
$url = "http://www.yahoo.co.jp/";

$start = microtime();

$res = my_curl($url);

if($res["info"]["http_code"] == 200){
  print("success");
}else{
  print("error");
}

$end = microtime();

function my_curl($url){
  $ch = curl_init($url);
  curl_setopt($ch, CURLOPT_URL, $url);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  $res = curl_exec($ch);

  $info = curl_getinfo($ch);
 
  return array("res" => $res, "info" => $info);
}


平均すると0.1679922秒かかりました。

curl_multiの場合
同じく、以下のプログラム(抜粋ですが)を10回試行して平均を取ってみます。
$url = "http://www.yahoo.co.jp/";

$start = microtime();

$res = my_curl_multi(array($url));

if($res[0]["info"]["http_code"] == 200){
  print("success");
}else{
  print("error");
}

$end = microtime();

function my_curl_multi($urls){

  $mh = curl_multi_init();
  $chs = array();
  foreach($urls as $url){
    $ch = curl_init($url);
    $chs[] = $ch;
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
    curl_multi_add_handle($mh, $ch);
  }

  do{
    curl_multi_exec($mh, $running);
  }while($running);

  $res = array();
  foreach($chs as $ch){
    $info = curl_getinfo($ch);
    $body = curl_multi_getcontent($ch);
    curl_multi_remove_handle($mh, $ch);
    curl_close($ch);
  
    $res[] = array("res" => $body, "info" => $info);
  }

  curl_multi_close($mh);
 
  return $res;

}


平均すると0.300859秒かかりました。

まとめ
秒数自体は通信環境に因るところが大きいと思うので参考程度に…ですが、それにしてもコンマ数秒のオーダーで違ってきています。
並列処理をする必要がない場合は、極力通常のcURLを使用するのがよさそうです。

2010年11月12日金曜日

apacheの.confでUserAgentを判定する方法

こちらのブログに載っていたのでメモ。
%{HTTP_USER_AGENT}で判定できるらしい。

2010年11月3日水曜日

PHPで一時的なファイルを作成するときに、ファイル名の重複がないようにする

テンポラリファイルを作成して後で利用する処理などの場合、ファイル名が同じだとテンポラリファイルが上書きされてしまうかもしれない。
そのときの時分秒をファイル名に使用しても、重複する可能性はある。

ファイル名の重複を避ける便利な関数がある
tempnam関数を使えばよい。
この関数は、重複しないファイル名でサイズ0バイトのファイルを生成する。
返り値は生成したファイル名。
どういう原理で重複を避けてるのか。。

パフォーマンスへの影響は?
tempnam関数を使ってファイルを生成→削除した場合と、使用しないで生成→削除した場合の比較してみる。
PHP 5.2.5を使っています。

tempnam関数を使った場合
$time_start = microtime();
for($i = 0; $i < 1000; $i++){
 $filename = tempnam($dir, $prefix);
 unlink($filename);
}
$time_end = microtime();

上のようなサンプルを書いて試した(主要な部分のみ抜粋)。
tempnam関数を使ってファイル生成→削除を1000回ループまわしています。
10回試して平均した結果。
4.3750467(秒)

tempnam関数を使わない場合
$time_start = microtime();
for($i = 0; $i < 1000; $i++){
 $fp = fopen($dir.$prefix, "w");
 fclose($fp);
 unlink($dir.$prefix);
}
$time_end = microtime();

単純にファイル生成→削除を1000回ループまわしています。
10回試して平均した結果。
2.738844(秒)

1.5倍くらい違うという結果になった
tempnam関数を使用すると1.5倍くらい遅くなる、という結果になった。
そこまで劇的には変わらないようです。

でもどういう原理なんでしょうねー。

Related Posts Plugin for WordPress, Blogger...