Created
July 22, 2016 14:54
-
-
Save lifeofguenter/6916d5eef0e77c2b93754b0240de9634 to your computer and use it in GitHub Desktop.
keep-alive benchmark tool in php ($ php katest.php -m 100 http://localhost/)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// debug | |
error_reporting(E_ALL); | |
ini_set('display_errors', true); | |
ini_set('html_errors', false); | |
function clrf($num = 1) | |
{ | |
return str_repeat("\r\n", $num); | |
} | |
function remote_socket_url(array $parsed_url) | |
{ | |
if (!empty($parsed_url['scheme']) && $parsed_url['scheme'] === 'https') { | |
$proto = 'tls'; | |
} else { | |
$proto = 'tcp'; | |
} | |
if ($proto === 'tls') { | |
$port = 443; | |
} else { | |
$port = 80; | |
} | |
return sprintf('%s://%s:%d', $proto, $parsed_url['host'], $port); | |
} | |
function http_get_request(array $parsed_url, $keep_alive = true) | |
{ | |
$headers = []; | |
$headers[] = sprintf('GET %s HTTP/1.1', $parsed_url['path']); | |
$headers[] = sprintf('Host: %s', $parsed_url['host']); | |
if (!$keep_alive) { | |
$headers[] = 'Connection: close'; | |
} | |
return implode(clrf(), $headers) . clrf(2); | |
} | |
function http_header_get(array $header, $key) | |
{ | |
foreach ($header as $row) { | |
if (preg_match('~^' . preg_quote($key, '~') . ':\s*(.*)$~i', $row, $match)) { | |
return $match[1]; | |
} | |
} | |
} | |
function stream_nb_write($resource, $string) | |
{ | |
$len = mb_strlen($string, 'ASCII'); | |
while($len > 0) { | |
$ret = fwrite($resource, substr($string, $len * -1)); | |
if ($ret === false) { | |
echo 'ERROR: fwrite' . PHP_EOL; | |
exit(1); | |
} | |
$len -= $ret; | |
} | |
} | |
function stream_wait($resource) | |
{ | |
$read = [$resource]; | |
$write = $expect = null; | |
do { | |
$changed_num = @stream_select($read, $write, $expect, 0, 10000); | |
if ($changed_num === false) { | |
//trigger_error('ERROR: stream_select', E_USER_WARNING); | |
return false; | |
} | |
} while($changed_num === 0); | |
return current($read); | |
} | |
function parseopt(array $options, $short, $long, $default = null) | |
{ | |
if (isset($options[$short])) { | |
return (int) $options[$short]; | |
} elseif (isset($options[$long])) { | |
return (int) $options[$long]; | |
} else { | |
return $default; | |
} | |
} | |
$shortopts = ''; | |
$shortopts .= 'd:'; // --delay | |
$shortopts .= 'm:'; // --delay | |
$shortopts .= 'h'; // --help | |
$longopts = [ | |
'delay:', | |
'max-requests:', | |
'help', | |
]; | |
$options = getopt($shortopts, $longopts); | |
$delay = parseopt($options, 'd', 'delay', 50000); | |
$max_requests = parseopt($options, 'm', 'max-requests', 10); | |
//var_dump($_SERVER['argv']); | |
//var_dump($_SERVER['argc']); | |
//exit; | |
if (empty($_SERVER['argv']) || isset($options['h']) || isset($options['help'])) { | |
printf('Usage: %s [OPTION] URL' . PHP_EOL, basename($_SERVER['argv'][0])); | |
printf('Benchmark keep-alive http(s) servers.' . PHP_EOL . PHP_EOL); | |
printf('Options:' . PHP_EOL); | |
printf(' %-29s%s' . PHP_EOL, '-h, --help', 'show this help'); | |
printf(' %-29s%s' . PHP_EOL, '-d, --delay', 'set delay (microseconds) between requests'); | |
printf(' %-29s%s' . PHP_EOL, '-m, --max-requests', 'set max number of requests'); | |
exit(1); | |
} | |
if (empty($_SERVER['argv'][1]) || !($parsed_url = parse_url(array_slice($_SERVER['argv'], -1)[0])) || !isset($parsed_url['host'])) { | |
echo 'ERROR: invalid url' . PHP_EOL; | |
exit(1); | |
} | |
if (!isset($parsed_url['path'])) { | |
$parsed_url['path'] = '/'; | |
} | |
$stopwatch = $codes = []; | |
$response = ''; | |
$reqs = 0; | |
//var_dump(remote_socket_url($parsed_url)); | |
//var_dump(http_get_request($parsed_url, true)); | |
//exit; | |
$stopwatch['total']['start'] = $stopwatch['connect']['start'] = microtime(true); | |
$fp = stream_socket_client(remote_socket_url($parsed_url), $errno, $errstr, 30); | |
if (!$fp) { | |
printf('ERROR: %s (%d)' . PHP_EOL, $errstr, $errno); | |
exit(1); | |
} else { | |
$stopwatch['connect']['end'] = microtime(true); | |
stream_set_blocking($fp, false); | |
$stopwatch['request']['start'] = microtime(true); | |
$do_request = true; | |
while (!feof($fp)) { | |
if ($do_request) { | |
stream_nb_write($fp, http_get_request($parsed_url)); | |
++$reqs; | |
} | |
$read = stream_wait($fp); | |
if ($read === false) { | |
$do_request = false; | |
continue; | |
} | |
$response = stream_get_contents($read); | |
if (!($pos = strpos($response, clrf(2)))) { | |
trigger_error('ERROR: unable to parse response', E_USER_WARNING); | |
var_dump($response); | |
break; | |
} | |
$header = explode(clrf(), substr($response, 0, $pos)); | |
$code = $header[0]; | |
//if (strpos($header[0], 'HTTP/1.1') !== 0) { | |
// var_dump($response); | |
//} | |
if (!isset($codes[$code])) { | |
$codes[$code] = 1; | |
} else { | |
++$codes[$code]; | |
} | |
if ($codes[$code] >= $max_requests) { | |
break; | |
} | |
usleep($delay); | |
$do_request = true; | |
} | |
$stopwatch['total']['end'] = $stopwatch['request']['end'] = microtime(true); | |
$meta = stream_get_meta_data($fp); | |
fclose($fp); | |
} | |
// printout some stats | |
foreach ($stopwatch as $name => $timers) { | |
printf('%s: %01.2f' . PHP_EOL, $name, $timers['end'] - $timers['start']); | |
} | |
echo '---' . PHP_EOL; | |
printf('timed_out: %d' . PHP_EOL, (int) $meta['timed_out']); | |
printf('eof: %d' . PHP_EOL, (int) $meta['eof']); | |
printf('reqs: %d' . PHP_EOL, $reqs); | |
echo '---' . PHP_EOL; | |
foreach ($codes as $name => $num) { | |
printf('%s: %d' . PHP_EOL, $name, $num); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment