Skip to content

Instantly share code, notes, and snippets.

@lifeofguenter
Created July 22, 2016 14:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lifeofguenter/6916d5eef0e77c2b93754b0240de9634 to your computer and use it in GitHub Desktop.
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/)
<?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