## - http://linuxchannel.net/ ## ## [changes] ## - 2014.12.09 : timeout tunning ## - 2009.08.04 : bug fixed _gethttp() ## - 2008.12.03 : some stderr(), add more info(connect time, response time ...) ## - 2008.12.01 : bug fixed _parse_url() ## - 2008.06.18 : bug fixed none-blocking mode ## - 2005.03.28 : bug fixed and tunning ## - 2005.03.24 : new build ## ## [download & online source view] ## - http://ftp.linuxchannel.net/devel/php_download/ ## ## [references] ## ## [usage] ## class download { /*** var $sock = ''; // socket var $blocking = FALSE; // blocking mode var $bsize = 1048576; // socket send/receive buffer size(Bytes) var $debug = FALSE; // debug mode or gregress view mode var $timeout = array('sec'=>0,'usec'=>100000); ***/ function &gethttp($_url, $blocking=FALSE, $debug=FALSE) { static $bsize = 1048576; // socket send/receive buffer size(Bytes) static $timeout = array('sec'=>0,'usec'=>500000); list($host,$port,$uri) = download::_parse_url($_url); if(!$host) return; if(preg_match('/[a-z]/',$host)) $ip = gethostbyname($host); // to ip else $ip = $host; $sock = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP); @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); if($blocking) $bmode = 'blocking-mode'; else { @socket_set_nonblock($sock); // set to non-blocking mode $bmode = 'non-blocking-mode'; } $req = 'GET '.$uri.' HTTP/1.1'."\r\n". 'Host: '.$host."\r\n". 'User-Agent: PHP/gethttp'."\r\n". 'Connection: close'."\r\n". // good idea for blocking mode "\r\n"; // enf of request header mark $conn_start = microtime(); $code = download::_connect($sock,$ip,$port,5,$blocking); $conn_stop = microtime(); if($code != 1) { @socket_close($sock); if($debug) download::stderr('can not connect to '.$host.':'.$port); return; } if(!@socket_write($sock,$req)) { @socket_close($sock); if($debug) download::stderr('can not send request '.$uri); return; } $req_stop = microtime(); $header = download::_getheader($sock,1024,$blocking); // read header $header_stop = microtime(); $header = download::_parse_header($header); if($header['error']) { @socket_close($sock); if($debug) download::stderr($header['status'].' '.$url); return; } $total = $header['content-length']; // total size $pg = $debug ? $total : 0; // debug mode with progress $transfer_start = microtime(); $size = download::_readsize($sock,$bsize,$blocking,$pg); // check bandwidth mode //download::_readbyall($sock,$buf='',$bsize,$blocking,$pg); //$size = strlen($buf); @socket_close($sock); $transfer_stop = microtime(); //$secs = download::_getmicrotime(defined('_RESPONSE_END_')?_RESPONSE_END_:$transfer_start,$transfer_stop); $secs = download::_getmicrotime($transfer_start,$transfer_stop); $rr = @sprintf("%s %.1f%s\t%.1f%s\t%d Bytes\t%.2f secs", $bmode,$size*100/$total,'%',$size/1024/$secs,'KB/sec',$size,$secs); echo $rr; if($debug) { $conn_time = download::_getmicrotime($conn_start,$conn_stop) * 1000; $request_time = download::_getmicrotime($conn_start,$req_stop) * 1000; $response_time = download::_getmicrotime($conn_start,$header_stop) * 1000; $transfer_time = $secs * 1000; $total_time = ($response_time + $transfer_time) / 1000; printf ( "\nconnect.time %.1f ms, request.time %.1f ms, response.time %.1f ms, ". "transfer %.1f ms, total %.4f secs", $conn_time, $request_time, $response_time, $transfer_time, $total_time ); } } function &_gethttp($_url, $debug=FALSE) { list($host,$port,$uri) = download::_parse_url($_url); if(!$host) return; $bmode = 'blocking-mode'; $req = 'GET '.$uri.' HTTP/1.1'."\r\n". 'Host: '.$host."\r\n". 'User-Agent: PHP/gethttp'."\r\n". 'Connection: close'."\r\n". // good idea for blocking mode "\r\n"; // enf of request header mark if(!$fp = @fsockopen($host,$port,$errno,$errstr,3)) { download::stderr($errstr); return; } @stream_set_timeout($fp,0,500000); // 0.5 seconds, good idea for blocking mode if(!@fwrite($fp,$req)) return; ## read header ## $header = ''; while($buf = fgets($fp,1024)) { if(!trim($buf)) { fgets($fp,1024); // seek break; } $header .= $buf; } $header = download::_parse_header($header); if($header['error']) { @fclose($fp); if($debug) download::stderr($header['status'].' '.$_url); return; } $total = $header['content-length']; // total size $pg = $debug ? $total : 0; // debug mode with progress $size = 0; $start = microtime(); while($buf = fread($fp,4096)) { $size += strlen($buf); if($pg) download::__progress($size,$total,&$op); // debug mode } if($pg) echo (int)($size*100/$total)."%\n"; // debug mode @fclose($fp); $secs = download::_getmicrotime($start,microtime()); $rr = @sprintf("%s %.1f%s\t%.1f%s\t%d Bytes\t%.2f secs", $bmode,$size*100/$total,'%',$size/1024/$secs,'KB/sec',$size,$secs); echo $rr; } ## connect to host:port ## ## [return] ## 1 or TRUE : connected ## 0 or FALSE : can't connect ## -1 : connetion time out ## function &_connect(&$sock, $host, $port, $timeout=3, $blocking=FALSE) { if($blocking) return @socket_connect($sock,$host,$port); $_time = time(); while(1) { @socket_connect($sock,$host,$port); if(@socket_last_error()==56 || @socket_last_error()==106) { return TRUE; break; } else if((time()-$_time) >= $timeout) { return -1; break; } } return FALSE; } ## read header by socket_read() ## PHP_NORMAL_READ : 1 ## PHP_BINARY_READ : 2 ## function &__readheader(&$sock, $bsize) { while($buf = @socket_read($sock,$bsize,PHP_NORMAL_READ)) { //if(!defined('_RESPONSE_END_')) define('_RESPONSE_END_',microtime()); if(!trim($obuf.$buf)) { @socket_read($sock,$bsize,PHP_NORMAL_READ); // seek 1 bytes break; // end of header } $rbuf .= $buf; $obuf = $buf; } return $rbuf; } ## read HTTP all headers ## function &_getheader(&$sock, $bsize=1024, $blocking=FALSE) { if($blocking) return @download::__readheader($sock,$bsize); $stream = array($sock); while(@socket_select($stream,$write=NULL,$except=NULL,10,0) !== FALSE) { //if(!in_array($sock,$stream)) break; // is bug ??? $rbuf = download::__readheader($sock,$bsize); break; // require } return $rbuf; } function &_parse_url($_url) { $url = parse_url(preg_replace(';^(?:http://)*;i','http://',trim($_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 return array($host,$port,$uri); } 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; } function &__progress($size, $total, &$op) { $np = (int)($size*100/$total); if($np != $op) { echo '='; //if(!($np%25)) echo $np; //if($size == $total) break; // good idea in while() } $op = $np; flush(); } ## read by size ## function &_readbysize(&$sock, $bsize=4096, $blocking=FALSE) { if($blocking) return @socket_read($sock,$bsize); $stream = array($sock); while(@socket_select($stream,$write=NULL,$except=NULL,10,0) !== FALSE) { if(!in_array($sock,$stream)) break; return @socket_read($sock,$bsize); break; // require } return; } ## read by all ## function &_readbyall(&$sock, &$rbuf, $bsize=1048576, $blocking=FALSE, $total=0) { if($blocking) { while(($buf = @socket_read($sock,$bsize))) { $rbuf .= $buf; if($total) download::__progress($rsize+=strlen($buf),$total,&$op); } if($total) echo (int)($rsize*100/$total)."%\n"; // debug mode return TRUE; } $stream = array($sock); while(@socket_select($stream,$write=NULL,$except=NULL,10,0) !== FALSE) { if(!in_array($sock,$stream)) break; if(($buf = @socket_read($sock,$bsize))) { $rbuf .= $buf; if($total) download::__progress($rsize+=strlen($buf),$total,&$op); } else break; // good idea, EOF } if($total) echo (int)($rsize*100/$total)."%\n"; // debug mode return TRUE; } ## only read size, this is a bandwidth test version ## function &_readsize(&$sock, $bsize=1048576, $blocking=FALSE, $total=0) { if($blocking) { while(($tsize = strlen(@socket_read($sock,$bsize)))) { $rsize += $tsize; if($total) download::__progress($rsize,$total,&$op); // debug mode } if($total) echo (int)($rsize*100/$total)."%\n"; // debug mode return $rsize; } $stream = array($sock); while(@socket_select($stream,$write=NULL,$except=NULL,10,0) !== FALSE) { if(!in_array($sock,$stream)) break; if($tsize = strlen(@socket_read($sock,$bsize))) { $rsize += $tsize; if($total) download::__progress($rsize,$total,&$op); // debug mode } else break; // good idea, EOF } if($total) echo (int)($rsize*100/$total)."%\n"; // debug mode return $rsize; } function &_getmicrotime($_start, $_end) { $end = explode(' ', $_end); $start = explode(' ', $_start); return sprintf('%.4f', ($end[1]+$end[0]) - ($start[1]+$start[0])); } function &socket_info($socket) { $opt = array ( 'SO_DEBUG'=>SO_DEBUG, 'SO_ACCEPTCONN'=>SO_ACCEPTCONN, 'SO_BROADCAST'=>SO_BROADCAST, 'SO_REUSEADDR'=>SO_REUSEADDR, 'SO_KEEPALIVE'=>SO_KEEPALIVE, 'SO_LINGER'=>SO_LINGER, 'SO_OOBINLINE'=>SO_OOBINLINE, 'SO_SNDBUF'=>SO_SNDBUF, 'SO_RCVBUF'=>SO_RCVBUF, 'SO_ERROR'=>SO_ERROR, 'SO_TYPE'=>SO_TYPE, 'SO_DONTROUTE'=>SO_DONTROUTE, 'SO_RCVLOWAT'=>SO_RCVLOWAT, 'SO_RCVTIMEO'=>SO_RCVTIMEO, 'SO_SNDLOWAT'=>SO_SNDLOWAT, 'SO_SNDTIMEO'=>SO_SNDTIMEO, ); foreach($opt AS $k=>$v) { $r[$k] = @socket_get_option($socket,SOL_SOCKET,$v); } return $r; // array } ## print to stderr such as error_log() ## ## arguments : ## $error string, error message ## ## return : ## $return mixed, the number of bytes written, or FALSE on error ## function _stderr($errstr) { global $_SERVER; if($fp = @fopen('php://stderr','w')) { ## add error_log format style ## if($_SERVER['HTTP_USER_AGENT']) { $head = '['.date('D M d H:i:s Y').'] [PHP-stderr] '. '[client '.$_SERVER['REMOTE_ADDR'].'] '; } $return = @fwrite($fp,$head.$errstr."\n"); fclose($fp); } return $return; } function stderr($errstr) { return error_log($errstr,0); } } // end of class /*** example *** set_time_limit(0); ob_implicit_flush(); //ini_set('memory_limit','150M'); $host = 'www.foobar.com'; download::gethttp("http://$host/test/100M.data",0,1); ***/ ?>