download this file: class.socket.http.php view text/plain: class.socket.http.php file encoding: UTF-8 [goback]
<?php
##
## this file name is 'class.socket.http.php'
##
## socket http object
##
## [author]
##  - san2(at)linuxchannel.net
##
## [changes]
##  - 2014.12.09 : timeout tunning
##  - 2014.07.14 : some comment
##  - 2014.07.09 : tuning, receive EOF
##  - 2014.04.30 : add TTFB and idle check timeout
##  - 2014.04.27 : add dns lookup time
##  - 2014.04.07 : some tuning
##  - 2014.01.12 : new build from clss.google.php
##
## [references]
##  - http://ftp.linuxchannel.net/devel/php_socket_http/
##
## [usage]
##
## [example]
##

class socket
{
  
## loading server latency
  ## default non-blocking mode
  ##
  
public function &http($_url$iporhost=''$method='GET'$connect_timeout=5$connect_retry=1$read_timeout=10)
  {
    static 
$bsize 1048576// socket send/receive buffer size(Bytes)
    
static $timeout = array('sec'=>0,'usec'=>500000);

    if(!
$sock = @socket_create(AF_INETSOCK_STREAMSOL_TCP))
    {
        return array(-
1000,0,socket_last_error());
    }

    
//@socket_set_option($sock,SOL_TCP,TCP_NODELAY,1); // Nagle algorithm off
    
@socket_set_option($sock,SOL_SOCKET,SO_REUSEADDR,1);
    @
socket_set_option($sock,SOL_SOCKET,SO_KEEPALIVE,1);
    @
socket_set_option($sock,SOL_SOCKET,SO_SNDBUF,$bsize);
    @
socket_set_option($sock,SOL_SOCKET,SO_RCVBUF,$bsize);
    @
socket_set_option($sock,SOL_SOCKET,SO_RCVTIMEO,$timeout); // good idea for blocking mode
    
@socket_set_option($sock,SOL_SOCKET,SO_SNDTIMEO,$timeout);
    @
socket_set_option($sock,SOL_SOCKET,SO_LINGER,array('l_onoff'=>1,'l_linger'=>0));
    @
socket_set_nonblock($sock); // set to non-blocking mode

    
list($host,$ip,$port,$uri,$lookuptime) = socket::gethostip($_url,$iporhost);
    if(!
preg_match('/^(GET|HEAD)$/',$method)) $method 'GET';
    
$connect_timeout = (int)$connect_timeout;
    
$connect_retry = (int)$connect_retry;
    
$connect_retry $connect_retry $connect_retry 1;
    
$method strtoupper($method);

    
$req =  $method.' '.$uri.' HTTP/1.1'."\r\n".
        
'User-Agent: Mozilla/5.0 (Windows NT 5.1) PHP/socket.http'."\r\n".
        
'Connection: close'."\r\n"// good idea for blocking mode
        //'Connection: Keep-Alive'."\r\n". // good idea for blocking mode
    
$req .= $host 'Host: '.$host."\r\n" "\r\n"// host and eof request
    
$req .= "\r\n";
//echo $req;
    
$_cs microtime();
    for(
$i=0$i<$connect_retry$i++)
    {
        list(
$code,$msg) = socket::_connect($sock,$ip,$port,$connect_timeout);
        if(
$code == 1) { $_ce microtime(); break; } // ok
    
}

    if(
$code != 1)
    {
        @
socket_close($sock);
        return array(-
2000,0,"can not connect to $ip:$port msg:$msg");
    }

    
$_ws microtime();
    if(!@
socket_write($sock,$req))
    {
        @
socket_close($sock);
        return array(-
3000,0,"can not send request $uri");
    }
    
$_we microtime();

    list(
$_fb,$header) = socket::_getheader($sock,1024,$read_timeout); // read header
    
$header socket::_parse_header($header);
//echo "_fb=$fb\n";
    /***
    if($header['error'])
    {
        @socket_close($sock);
        return array(-1, socket::getms($stime), $header['status'].' '.$uri);
    }
    ***/

    
$rsize socket::_read_bodysize($sock,$bsize,$read_timeout,$header['content-length']); // check bandwidth mode
    
$_ee   microtime();
    @
socket_close($sock);

    return array
    (
        array
        (
        
$lookuptime,            // DNS lookup time
        
socket::getms($_cs,$_ce),    // connect time
        
socket::getms($_ws,$_we),    // send time
        
socket::getms($_ws,$_fb),    // wait time send ~ TTFB(Time To First Byte), server latency think time
        
socket::getms($_fb,$_ee),    // receive time
        
socket::getms($_cs,$_ee) + $lookuptime    // total time
        
),
        
$header,            // array header
        
$rsize,                // body size
        
"$ip:$port",            // target IP
        
$uri                // rewrite uri
    
);
  }

  public function 
gethostip($_url$iporhost='')
  {
    static 
$_port 80;

    
$lookuptime 0;
    list(
$nhost,$port,$uri) = $u socket::_parse_url($_url);

    if(
preg_match('/[a-z]/',$nhost)) $host $nhost;
    else if(
$nhost$ip $nhost;

    if(
$iporhost)
    {
        list(
$iporhost,$ipport) = explode(':',$iporhost);
        if(
preg_match('/[a-z]/',$iporhost)) $host $iporhost;
        else if(
$iporhost$ip $iporhost;
        
$port $ipport $ipport $port// overwrite
    
}

    if(!
$port$port $_port// last overwrite
    
if(!$ip)
    {
        
$s microtime();
        
$ip = ($ip=gethostbyname($host)) ? $ip gethostbyname($host); // one more check, to ip
        
$lookuptime socket::getms($s,microtime());
    }
    if(!
$host$host $ip// not good

    
return array($host,$ip,$port,$uri,$lookuptime);
  }

  
## connect to host:port
  ##
  ## [return]
  ##  1 or TRUE  : connected
  ##  0 or FALSE : can't connect, client issue
  ##  -1         : connetion time out, remote issue
  ##
  
private function &_connect(&$sock$host$port$timeout=5)
  {
    
$_time time();
    while(
1)
    {
        @
socket_connect($sock,$host,$port);
        
$errno = @socket_last_error();
//echo "$errno\t".socket_strerror($errno)."\n"; // debug
        
if($errno==114 || $errno==115) continue; // in progress
        
else if($errno == 106) { return array(TRUE,'OK'); break; }
        else if(
$errno 0) { return array(FALSE,socket_strerror($errno)); break; } // unknown host
        
else if($errno==100 || $errno==101 || $errno==102) { return array(FALSE,socket_strerror($errno)); break; } // Network fail
        
else if($errno==110 || $errno==111) { return array(-1,socket_strerror($errno)); break; } // Connection timed out, Connection refused
        
else if($errno==112 || $errno==113) { return array(-1,socket_strerror($errno)); break; } // Host is down, No route to host
        
else if((time()-$_time) >= $timeout) { return array(-1,'Connect timeout='.$timeout); break; }
    }

    return array(
FALSE,'');
  }

  
## read HTTP all headers
  ##
  ## read header by socket_read()
  ## PHP_NORMAL_READ : 1
  ## PHP_BINARY_READ : 2
  ##
  
private function &_getheader(&$sock$bsize=1024$timeout=10)
  {
    
$_fb 0// first byte time
    
$_break FALSE;
    
$_time time(); // idle check initial
    
$stream = array($sock);
    while(
1)
    {
        if((
time()-$_time) >= $timeout) break;
        
$select = @socket_select($stream,$write=NULL,$except=NULL,$timeout,0);
        if(
$select === FALSE) continue;
    
        
//if(!in_array($sock,$stream)) break; // is bug ???
        //$rbuf = socket::__readheader($sock,$bsize);

        
while($buf = @socket_read($sock,$bsize,PHP_NORMAL_READ))
        {
            if(!
$_fb$_fb microtime();
//echo "H:'$buf'";
            //if(!defined('_RESPONSE_END_')) define('_RESPONSE_END_',microtime());
            
if(!trim($obuf.$buf))
            {
                @
socket_read($sock,1,PHP_NORMAL_READ); // seek 1 bytes
                
$_break TRUE;
                break; 
// end of header
            
}
            
$rbuf .= $buf;
             
$obuf $buf;
        }
        if(
$_break) break; // require
        
$_time time(); // idle check reset
    
}

    return array(
$_fb,$rbuf);
  }

  private function &
_parse_header($buf)
  {
    
$lines preg_split('/[\r\n]+/',trim($buf));
    
$size sizeof($lines);
    
$r['status'] = trim($lines[0]);

    
/***
    if(!preg_match(';200 OK;',$lines[0]))
    {
        $r['error'] = TRUE;
        return $r;
    }
    ***/

    
for($i=1$i<$size$i++)
    {
        
$line trim($lines[$i]);
        list(
$k) = preg_split('/:\s+/',$line);
        
$r[strtolower($k)] = preg_replace(';^'.$k.':\s+;','',$line);
    }

    return 
$r;
  }

  
## only read size, this is a bandwidth test version
  ##
  
private function &_read_bodysize(&$sock$bsize=1048576$timeout=10$content_length=NULL)
  {
    
$_time time(); // idle check
    
$rsize 0;
    
$stream = array($sock);
    while(
1)
    {
        if((
time()-$_time) >= $timeout) break;
        
$select = @socket_select($stream,$write=NULL,$except=NULL,$timeout,0);
        if(
$select === FALSE) continue;

        
$buf socket_read($sock,$bsize,PHP_BINARY_READ);
        if(
$buf === '') break;
//echo "'$buf'";
        
$rsize += strlen($buf);
        if(
$content_length && $rsize>=$content_length) break;
        else if(
preg_match("/(0\r\n\r\n|f\r\n\r\n)$/",substr($buf,-5))) break;
        
$_time time(); // idle check
    
}

    return (int)
$rsize;
  }

  private function &
getms($_start$_end=NULL)
  {
    
$end explode(' '$_end?$_end:microtime());
    
$start explode(' '$_start);

    return 
sprintf('%d',(($end[1]+$end[0])-($start[1]+$start[0])) * 1000);
  }

  private function &
_parse_url($_url)
  {
    
$_url trim($_url);
    if(
preg_match(';^/;',$_url)) return array('','',$_url);

    
$url parse_url(preg_replace(';^(?:http://)*;i','http://',$_url));
    
$host $url['host'];
    
$port $url['port']; // ? $url['port'] : 80;
    
$uri $url['path'] ? $url['path'] : '/';

    if(
$url['query']) $uri .= '?'.$url['query']; // add san2@2008.12.01
//echo "$host,$port,$uri\n";
    
return array($host,$port,$uri);
  }

  private function &
error($errstr)
  {
    return 
error_log($errstr,0);
  }
// end of class

?>