network.http.Http->request バグ(その2)

昨日書いた network.http.Http->request バグ - gounx2の日記 が間違っていたので書き直します。

どんなバグか?

次のスクリプトを実行すると。。。

<?php
require_once('./__init__.php');
Rhaco::import('network.http.Browser');
$url = 'http://twitter.1x1.jp/search/?source=&keyword=千葉&lang=&text=1';
$html = Http::get($url);
echo $html;

次のように一部化けることがあります。
(これはfirefox3で見たとき。真ん中あたりちょっと変です)

原因がわかった。

調査の為に、Http->request へ echo を追加した。

class Http{
    /**
     * リクエストを発行する
     *
     * @param string $url
     * @param string $method
     * @param array $headers
     * @param int $timeout
     * @param int $blocking
     * @return array
     */
    function request($url,$method="GET",$headers=array(),$timeout=5,$blocking=1){
:
            $fp = @fsockopen((($isssl) ? "ssl://" : "").$host,$port,$errorno,$errormsg,$timeout);

            if($fp == false || false == stream_set_blocking($fp,$blocking) || false == stream_set_timeout($fp,$timeout)){
                return ExceptionTrigger::raise(new NotConnectionException(Message::_("URL [{1}] {2} {3}",$url,$errormsg,$errorno)));
            }
:
            }else if(preg_match("/Transfer\-Encoding:[\s]+chunked/i",$header)){
                while(!feof($fp)){
                    $bytes = fgets($fp,4096);

                    if(preg_match("/^([0-9a-fA-F]+)[\s\r\n]*$/",$bytes,$match)){
                        $size = hexdec($match[1]);
                        if($size <= 0) break;

//                      $body .= fread($fp,$size);
$wk = fread($fp,$size);
$body .= $wk;
echo "size:". $size . " fread:" . strlen($wk) . "<br/>";
                    }else if(!preg_match("/^[\r\n]+$/",$bytes)){
echo "...size:" . strlen($bytes) . "<br/>";
                        $body .= $bytes;
                    }
                }
            }else{
:

これを実行すると、次のログが取れた。

size:2593 fread:2491   --- fread($fp,2593) してるのに 実際に受信したのは 2491byte!!
...size:34
...size:9
...size:10
...size:50
size:1448 fread:1448   --- これはOKですね。
size:2896 fread:2678   --- これはNG
...size:19
...size:38
...size:8
...size:7
...size:21
:

というように、freadで指定したサイズ分を受信できずに、次の処理に行ってしまっている。

まとめると。

  • stream_set_blockingでブロッキングモードにしていても、
  • Transfer-Encoding: chunked のときは、効いていないように見える。

ブロッキングっていうのはTCPレベルの話なので、そのあたりは詳しくは無いわけですが、まぁ、そういう特殊な送信の仕方するのが chunked の仕様ってことなのだろう。

と無理やり納得するとして、これを正常に動くようにしてみる。

修正してみる。

得たいデータを確実に取得するよう fgets、fread をループするように修正してみた。

class Http{
    /**
     * リクエストを発行する
     *
     * @param string $url
     * @param string $method
     * @param array $headers
     * @param int $timeout
     * @param int $blocking
     * @return array
     */
    function request($url,$method="GET",$headers=array(),$timeout=5,$blocking=1){
:
            }else if(preg_match("/Transfer\-Encoding:[\s]+chunked/i",$header)){
                while(!feof($fp)){
//                  $bytes = fgets($fp,4096);
//                  if(preg_match("/^([0-9a-fA-F]+)[\s\r\n]*$/",$bytes,$match)){
$bytes = "";
while(!feof($fp) && substr($bytes, strlen($bytes)-2) !== "\r\n"){
    $bytes = fgets($fp,4096);
    echo "bytes:" . $bytes . "<br/>";
}
                    if(preg_match("/^([0-9a-fA-F]+)[\s]*\r\n$/",$bytes,$match)){
                        $size = hexdec($match[1]);
                        if($size <= 0) break;
//                      $body .= fread($fp,$size);
echo "fread1:" . $size. "<br/>";
while(!feof($fp) && $size !== 0){
    $buf = fread($fp,$size);
    $size -= strlen($buf);
    echo "fread1:" . strlen($buf) . " " . $size. "<br/>";
    $body .= $buf;
}
$size = 2;
echo "fread2:" . $size. "<br/>";
while(!feof($fp) && $size !== 0){
    $buf = fread($fp,$size);
    $size -= strlen($buf);
    echo "fread2:" . strlen($buf) . " " . $size. "<br/>";
}
//                  }else if(!preg_match("/^[\r\n]+$/",$bytes)){
//                      $body .= $bytes;
                    }
:

echoの出力が、

bytes:a21
fread1:2593       --- 2593byte受信したい。
fread1:2491 102   --- 2491byte受信した。残り102byte
fread1:102 0      --- 102byte受信した。残り0byte
fread2:2
fread2:2 0
bytes:5a8
fread1:1448
fread1:1448 0
fread2:2
fread2:2 0
bytes:b50
fread1:2896
fread1:2678 218
fread1:218 0
fread2:2
fread2:2 0
bytes:5a8
fread1:1448
fread1:1448 0
fread2:2
fread2:2 0
:

うまくいってるようだ。

    • -

2008-06-30追記
以下でfix
http://fixdap.com/p/rhaco/14271/